【发布时间】:2021-10-15 07:14:34
【问题描述】:
我正在制作一个纸牌游戏,我正在尝试将纸牌放入ScrollView。我通过遍历一组卡片并随后使用CardView 为每张卡片构建视图来做到这一点。
struct ContentView: View {
@ObservedObject var viewModel = SetGameViewModel()
var body: some View {
VStack {
ScrollView {
LazyVGrid(columns: [GridItem(),GridItem(),GridItem(),GridItem()]) {
ForEach(viewModel.cards) { card in
CardView(card: card)
//Individual card gets built depending on its data.
.aspectRatio(2/3, contentMode: .fill)
}
}
}
DealThreeCardsButton()
.onTapGesture { viewModel.dealThreeCards() }
}
}
}
每张卡片都有特定数量的符号(以及其他几个特征,但现在不是问题。)它应该显示。 ->
struct CardView: View {
let card: SetGame.Card
private var color: Color {
switch card.content.color {
case .green:
return Color(.green)
case .purple:
return Color(.purple)
case .red:
return Color(.red)
}
}
var body: some View {
if card.isFaceUp {
ZStack{
RoundedRectangle(cornerRadius: 20)
.foregroundColor(.clear)
RoundedRectangle(cornerRadius: 20)
.stroke(lineWidth: 2.0)
.foregroundColor(/*@START_MENU_TOKEN@*/.blue/*@END_MENU_TOKEN@*/)
VStack {
CardContentView(content: card.content)
//The contents of the cards are created
.foregroundColor(color)
Text("\(card.id), n: \(card.content.number)")
}
}
} else {
RoundedRectangle(cornerRadius: 20)
.foregroundColor(.blue)
}
}
}
private struct CardContentView: View {
let content: SetGame.CardContent
var body: some View {
ForEach(1..<content.number+1) { _ in
//This ForEach should create the number of symbols held by the card struct
//However this seems to not work.
ZStack {
switch content.symbol {
case .oval:
DrawCircle(fillType: content.fillType.rawValue)
case .diamond:
DrawDiamond(fillType: content.fillType.rawValue)
case .squiggle:
DrawSquiggle(fillType: content.fillType.rawValue)
}
}
}
}
}
当我运行它时,视图会正确构建。该图显示了 id 和 n(卡片上应包含的符号数。)
但是,当我向下滚动和向后滚动时,视图会错误地重建。符号的数量似乎是完全随机的。这仅在ScrollView 具有一定长度时才会发生,因此当卡片数量超过 30 张卡片时(取决于它们的大小)。卡片不会混淆,因为 id 保持不变并且 n 也没有变化.
Refreshed and incorrectly rebuilt View
我错过了什么吗?一定与ScrollView 和ForEach 交互的方式有关。 CardContentView 结构和其中的 ForEach 语句似乎有问题。我只是不知道是什么。
这些是我刷新后遇到的错误;
ForEach
, DrawSquiggle>>> count (3) != 它的初始计数 (2)。 ForEach(_:content:)应该只用于常量数据。而是将数据符合Identifiable或使用ForEach(_:id:content:)并提供明确的id!
下面;整个Model 包含CardContent 和Card 结构以及创建卡片数组的init;
struct SetGame {
private(set) var cards: Array<Card>
private(set) var deck: Array<Card>
init() {
deck = []
cards = []
var id = 1
var color = CardContent.Color.red
var fillType = CardContent.FillType.hollow
var symbol = CardContent.Symbol.oval
while deck.count < 81 {
if deck.count % 27 == 0 {
switch color {
case .red:
color = .green
case .green:
color = .purple
case .purple:
color = .red
}
}
if deck.count % 9 == 0 {
switch symbol {
case .oval:
symbol = .diamond
case .diamond:
symbol = .squiggle
case .squiggle:
symbol = .oval
}
}
if deck.count % 3 == 0 {
switch fillType {
case .hollow:
fillType = .shaded
case .shaded:
fillType = .filled
case .filled:
fillType = .hollow
}
}
deck.append(Card(id: id, content: CardContent(number: 1, color: color, fillType: fillType, symbol: symbol)))
deck.append(Card(id: id+1, content: CardContent(number: 2, color: color, fillType: fillType, symbol: symbol)))
deck.append(Card(id: id+2, content: CardContent(number: 3, color: color, fillType: fillType, symbol: symbol)))
id += 3
}
//deck.shuffle()
while cards.count < 81 {
//When cards.count > 28; the view starts bugging
cards.append(deck.popLast()!)
}
}
mutating func dealThreeCards() {
for _ in 0...2 {
if deck.isEmpty {
break
} else {
cards.append(deck.popLast()!)
}
}
print("I was called :)")
}
struct Card: Identifiable {
var isFaceUp = true
var isMatched = false
let id: Int
let content: CardContent
}
struct CardContent: Equatable {
let number: Int
let color: Color
let fillType: FillType
let symbol: Symbol
enum Color { case red, green, purple }
enum FillType: Double {
case hollow = 0.0
case shaded = 0.2
case filled = 1.0
}
enum Symbol { case oval, diamond, squiggle }
}
}
下面; ContentView 中使用的整个 ViewModel。该应用程序非常简单,但我不明白什么是行不通的。
class SetGameViewModel: ObservableObject {
@Published private(set) var game: SetGame
init() {
game = SetGame()
}
var cards: Array<SetGame.Card> {
game.cards
}
func dealThreeCards() {
game.dealThreeCards()
print("I was called :)))")
}
}
最小可重现示例
主要:
import SwiftUI
@main
struct mreApp: App {
var body: some Scene {
let game = ViewModel()
WindowGroup {
ContentView(viewModel: game)
}
}
}
型号:
import Foundation
struct CardGameModel {
private(set) var cards: Array<Card>
private(set) var deck: Array<Card>
init() {
//the init only takes care of creating the two arrays
//deck and cards. It seems to be working correctly and nothing
//is wrong here, I believe.
deck = []
cards = []
var id = 1
var color = CardContent.Color.red
var fillType = CardContent.FillType.hollow
var symbol = CardContent.Symbol.oval
while deck.count < 81 {
if deck.count % 27 == 0 {
switch color {
case .red:
color = .green
case .green:
color = .purple
case .purple:
color = .red
}
}
if deck.count % 9 == 0 {
switch symbol {
case .oval:
symbol = .diamond
case .diamond:
symbol = .squiggle
case .squiggle:
symbol = .oval
}
}
if deck.count % 3 == 0 {
switch fillType {
case .hollow:
fillType = .shaded
case .shaded:
fillType = .filled
case .filled:
fillType = .hollow
}
}
deck.append(Card(id: id, content: CardContent(numberOfShapes: 1, color: color, fillType: fillType, symbol: symbol)))
deck.append(Card(id: id+1, content: CardContent(numberOfShapes: 2, color: color, fillType: fillType, symbol: symbol)))
deck.append(Card(id: id+2, content: CardContent(numberOfShapes: 3, color: color, fillType: fillType, symbol: symbol)))
id += 3
}
//deck.shuffle()
while cards.count < 81 {
//When cards.count > 28; the view starts bugging.
//However it also depends on the amount of columns in the
//LazyVGrid. If more columns are included, the number of cards
//displayable before bugs is greater.
//Optional
cards.append(deck.popLast()!)
}
}
struct Card: Identifiable {
let id: Int
let content: CardContent
}
struct CardContent: Equatable {
let numberOfShapes: Int
let color: Color
let fillType: FillType
let symbol: Symbol
enum Color { case red, green, purple }
enum FillType: Double {
case hollow = 0.0
case shaded = 0.2
case filled = 1.0
}
enum Symbol { case oval, diamond, squiggle }
}
}
视图模型:
import Foundation
class ViewModel: ObservableObject {
@Published private(set) var game: CardGameModel
init() {
game = CardGameModel()
}
var cards: Array<CardGameModel.Card> {
game.cards
}
}
查看:
import SwiftUI
struct ContentView: View {
@ObservedObject var viewModel: ViewModel
var body: some View {
ScrollView {
LazyVGrid(columns: [GridItem(),GridItem(),GridItem(),GridItem()]) {
ForEach(viewModel.cards) { card in
CardView(card: card)
.aspectRatio(2/3,contentMode: .fill)
}
}
}
}
}
struct CardView: View {
let card: CardGameModel.Card
private var color: Color {
switch card.content.color {
case .green:
return Color(.green)
case .purple:
return Color(.purple)
case .red:
return Color(.red)
}
}
var body: some View {
ZStack{
RoundedRectangle(cornerRadius: 20)
.stroke(lineWidth: 2.0)
.foregroundColor(/*@START_MENU_TOKEN@*/.blue/*@END_MENU_TOKEN@*/)
VStack {
CardContentView(content: card.content)
//The contents of the cards are created
.foregroundColor(color)
Text("\(card.id), n: \(card.content.numberOfShapes)")
}
}
}
}
struct CardContentView: View {
let content: CardGameModel.CardContent
var body: some View {
VStack {
ForEach(0..<content.numberOfShapes) { _ in
switch content.symbol {
case .oval:
Circle()
case .squiggle:
RoundedRectangle(cornerRadius: 35.0)
case .diamond:
Rectangle()
}
}
}
}
}
【问题讨论】:
-
你能包含 Card 和你的 viewModel 的代码吗?我认为问题在于——基于错误,我猜是 Card 是 Identifiable 但没有提供稳定的 ID,或者视图模型的数组正在改变。
-
当然。我会将它们添加到原始问题中。
-
包括minimal reproducible example。您所包含的内容不能由任何人编译或运行。
-
好的,我也会加入。
标签: swift foreach swiftui scrollview