我的代码中有一个奇怪的行为.这是一个简单的应用程序,它显示四个视图(CardView),每次点击其中一个,视图将通过更改其边框来反映它(使用"选定"属性).问题是,当"Value"属性是一个字符串时,它不起作用.但是,当"Value"属性的类型为Int时,它确实可以工作.这种行为是由于什么原因造成的?

import SwiftUI

struct Deck {
    var cards = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map {
//        Card(value: $0)
        Card(value: "\($0)")
    }
}

struct Card: Identifiable {
    let id = UUID()
//    let value: Int // Using int value solves the problem...
    let value: String // Using string value provokes card view not updated
    var selected = false
}

extension Card: Equatable {
    static func ==(lhs: Card, rhs: Card) -> Bool {
        return lhs.id == rhs.id
    }
}

final class TestVM: ObservableObject {
    @Published var deck = Deck()
    
    func toggleSelection(card: Card) {
        let idx = deck.cards.firstIndex(of: card)!
        deck.cards[idx].selected.toggle()
    }
}

struct CardView: View {
    @ObservedObject var testVM: TestVM
    let card: Card
    
    var body: some View {
        Text("\(card.value)")
            .frame(width: 40, height: 80)
            .background {
                Rectangle()
                    .stroke(card.selected ? .red : .black, lineWidth: 2)
            }
            .background(card.selected ? .red.opacity(0.2) : .gray.opacity(0.2))
            .onTapGesture {
                testVM.toggleSelection(card: card)
            }
    }
}

struct TestZStackView: View {
    
    @StateObject var testVM = TestVM()
    
    var body: some View {
        HStack {
            ForEach(testVM.deck.cards.prefix(4)) { card in
                CardView(testVM: testVM, card: card)
            }
        }
    }
}

struct TestZStackView_Previews: PreviewProvider {
    static var previews: some View {
        TestZStackView()
    }
}

推荐答案

CardView内显示声明为let card: Cardcard, 这意味着它不会改变.当调用testVM.toggleSelection(card: card)Deck中的Card的数组并没有真正改变,也就是说,没有向其中添加或从中移除任何卡. 因此,该视图不会更新.

试试这个方法,对我很管用:

struct Deck {
    var cards = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map {Card(value: "\($0)")}
}

struct Card: Identifiable {
    let id = UUID()
    let value: String
    var selected = false
}

extension Card: Equatable {
    static func ==(lhs: Card, rhs: Card) -> Bool {
        return lhs.id == rhs.id
    }
}

final class TestVM: ObservableObject {
    @Published var deck = Deck()
}

struct CardView: View {
    @Binding var card: Card // <-- here

    var body: some View {
        Text("\(card.value)")
            .frame(width: 40, height: 80)
            .background {Rectangle().stroke(card.selected ? .red : .black, lineWidth: 2)}
            .background(card.selected ? .red.opacity(0.2) : .gray.opacity(0.2))
            .onTapGesture {
                card.selected.toggle()  // <-- here
            }
    }
}

struct ContentView: View {
    @StateObject var testVM = TestVM()
    
    var body: some View {
        HStack {
            ForEach($testVM.deck.cards) { $card in  // <-- here
                if testVM.deck.cards.prefix(4).contains(card) { 
                    CardView(card: $card)  // <-- here
                }
            }
        }
    }
}

Swift相关问答推荐

为什么Swift在某些链调用中不能对不可变值使用变异成员,而在其他链调用中则不能使用变异成员?

防止NavigationLink自行返回导航视图

SWIFT Vapor控制台应用程序-操作无法完成.权限被拒绝

当计数大于索引时,索引超出范围崩溃

如何强制创建新的UIViewControllerRenatable|更新Make UIViewController上的视图

表视图插座单元测试失败

协议元类型'SomeProtocol.Type'上无法使用静态成员'currency'

Swift:结果的失败类型不能是协议 - Type 'any ShadowError' cannot conform to Error

本地对象的异步/等待引用捕获总是安全的还是理论上不安全?

防止带有背景的文本扩展到 maxWidth

从 iPhone 中的安全飞地获取真正的随机数?

您是否必须手动指定您的 DispatchQueue 是串行的?

数组使用偏移量对自身执行 XOR

如何 for each 用户制作一个单独的按钮,以便在按下它时切换 isFollowingUser Bool 并更改为关注?

展开 List 中的 HStack 以端到端但不是从上到下,因此看起来项目之间有更宽的空间

swift 如何从 Int 转换?到字符串

使 struct 可散列?

在 Swift 中以编程方式更新约束的常量属性?

如何将应用程序与 iOS 联系人应用程序集成?

UITableView 布局在 push segue 和 return 上搞砸了. (iOS 8、Xcode beta 5、Swift)