繁体   English   中英

Escaping 闭包捕获变异的“自我”参数 Swift 中的错误

[英]Escaping closure captures mutating 'self' parameter Error in Swift

创建一个简单的纸牌游戏(套装),我在 model 中有一个 function,它将 X 牌放到牌组上。 目前,当我单击交易卡按钮时,它们都会同时显示,因此我添加了计时器,以便它们一个接一个地出现。 这给出了错误“转义闭包捕获变异'self'参数”关于我可以修复什么的任何想法?

mutating func deal(_ numberOfCards: Int) {
        for i in 0..<numberOfCards {
            Timer.scheduledTimer(withTimeInterval: 0.3 * Double(i), repeats: false) { _ in
                if deck.count > 0 {
                    dealtCards.append(deck.removeFirst())
                }
            }
        }    
}

甚至不需要计时器。 您可以将转换与 animation 结合使用以获得所需的效果。 这里的转换是根据卡片的索引延迟的:

class CardModel: ObservableObject {
    @Published var cards: [Int] = []
    
    func deal(_ numberOfCards: Int) {
        cards += (cards.count ..< (cards.count + numberOfCards)).map { $0 }
    }
    
    func clear() {
        cards = []
    }
}

struct ContentView: View {

    @ObservedObject var cardModel = CardModel()
    
    var body: some View {
        
        GeometryReader { geometry in
            VStack {
                HStack {
                    ForEach(0 ..< self.cardModel.cards.count, id: \.self) { index in
                        CardView(cardNumber: self.cardModel.cards[index])
                            .transition(.offset(x: geometry.size.width))
                            .animation(Animation.easeOut.delay(Double(index) * 0.1))
                    }
                }
                
                Button(action: { self.cardModel.deal(2) }) {
                    Text("Deal")
                }
                
                Button(action: { self.cardModel.clear() }) {
                    Text("Clear")
                }
            }
        }
    }
}

struct CardView: View {
    let cardNumber: Int
    
    var body: some View {
        Rectangle()
            .foregroundColor(.green)
            .frame(width: 9, height: 16)
    }
}

或者更简单一点(没有 CardModel):

struct ContentView: View {

    @State var cards: [Int] = []
    
    func deal(_ numberOfCards: Int) {
        cards += (cards.count ..< (cards.count + numberOfCards)).map { $0 }
    }
    
    func clear() {
        cards = []
    }
    
    var body: some View {
        
        GeometryReader { geometry in
            VStack {
                HStack {
                    ForEach(0 ..< self.cards.count, id: \.self) { index in
                        CardView(cardNumber: self.cards[index])
                            .transition(.offset(x: geometry.size.width))
                            .animation(Animation.easeOut.delay(Double(index) * 0.1))
                    }
                }
                
                Button(action: { self.deal(2) }) {
                    Text("Deal")
                }
                
                Button(action: { self.clear() }) {
                    Text("Clear")
                }
            }
        }
    }
}

struct CardView: View {
    let cardNumber: Int
    
    var body: some View {
        Rectangle()
            .foregroundColor(.green)
            .frame(width: 9, height: 16)
    }
}

请注意,如果您将卡片居中,这种方法可以正常工作,因为之前的卡片也需要移动。 但是,如果您将卡左对齐(使用间隔),则不需要移动的卡会出现 animation 延迟(animation 以尴尬的延迟开始)。 如果您需要考虑这种情况,您需要使新插入的索引部分成为 model 的一部分。

(这可能是在重复 Jack Goossen 所写的内容;go 先看那里,如果不清楚,这可能会给出更多解释。)

这里的核心问题是您似乎将结构视为引用类型。 结构是一个值。 这意味着它的每个持有者都有自己的副本。 如果您将值传递给 Timer,则 Timer 正在改变它自己的该值的副本,它不能是self

在 SwiftUI 中,模型通常是引用类型(类)。 它们代表了一种可识别的“事物”,可以观察到并随着时间的推移而变化。 将此类型更改为 class 可能会解决您的问题。 (请参阅 Jack Goossen 的回答,它使用 class 来保存卡片。)

这与 Swift 与 UIKit 的方向相反,其中视图是引用类型,并且鼓励 model 由值类型组成。 在 SwiftUI 中,视图是结构,而 model 通常由类组成。

(使用与 SwiftUI 结合使用,可以将视图和 model 都变成值类型,但这可能超出了您在这里尝试做的事情,如果您还没有研究过组合或反应式编程,那就有点复杂了。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM