SwiftUI CS193P - 无法在 属性 初始值设定项中使用实例成员 'card'; 属性 初始值设定项 运行 在 'self' 可用之前

SwiftUI CS193P - Cannot use instance member 'card' within property initializer; property initializers run before 'self' is available

我正在关注 Stanfords' CS193p Developing Apps for iOS online course。 我正在使用 Xcode 11.5。 (我没有更新,因为那是课程讲师 (Paul Heagarty) 使用的版本。)

我正在尝试 Assignment 3 (Set Game)。我知道我的代码可能在很多地方写得不好,但我稍后会对其进行完善,现在我只是想让它没有错误地工作。 我目前无法修复的错误是:

var shading: SetGameModel.Card.Shading = card.shading //Error: Cannot use instance member 'card' within property initializer; property initializers run before 'self' is available

这是完整的查看代码: 如果您还需要 ViewModel、Model 或其他一些文件来帮助我修复它,请告诉我:-)

请尝试以某种方式帮助我修复它,作为初学者我会理解

//
//  SetGameView.swift
//  TheSetGame
//

import SwiftUI

struct SetGameView: View {
    
    @ObservedObject var viewModel: SetGameViewModel
    
    var body: some View {
            Grid(viewModel.cards) { card in
                CardView(card: card, shading: .solid).onTapGesture {
                    self.viewModel.choose(card: card)
                }
            }
        .padding()
        .foregroundColor(Color.orange)
    }
}

struct CardView: View {
    var card: SetGameModel.Card
    // Shape features (numberOfShapes, shape, shading, color):
    var numberOfShapes: Int {
        switch card.numberOfShapes {
        case .one:
            return 1
        case .two:
            return 2
        case .three:
            return 3
        }
    }
    
    var shading: SetGameModel.Card.Shading = card.shading //Error: Cannot use instance member 'card' within property initializer; property initializers run before 'self' is available
    var isFilled: Bool { shading == .solid ? true : false }
    var isStriped: Bool { shading == .striped ? true : false }
    
    var color: Color {
        switch card.color {
        case .green:
            return Color.green
        case .purple:
            return Color.purple
        case .red:
            return Color.red
        }
    }
    
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 10.0).fill(Color.white)
            RoundedRectangle(cornerRadius: 10.0)
                .stroke(lineWidth: card.isChosen ? 6 : 3)
                .foregroundColor(card.isChosen ? Color.red : Color.orange)
            VStack {
                ForEach(0..<numberOfShapes) { index in
                    if self.card.shape == .diamond {
                        Diamond()//.scale(0.75)
                    }
                    if self.card.shape == .squiggle {
                        Rectangle()//.scale(0.75)
                    }
                    if self.card.shape == .oval {
                        Ellipse()//.scale(0.75)
                    }
                }
            }
            .foregroundColor(self.color)
            .padding()
        }
        .aspectRatio(2/3, contentMode: .fit)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        SetGameView(viewModel: SetGameViewModel())
    }
}

这听起来有点存在主义,但编译器会抱怨,因为您试图将 shading 设置为一个尚不存在的值。当您第一次在视图中设置变量的值时,它们实际上并不存在。你告诉编译器它们将是什么,但编译器坚持认为你所说的变量实际上存在。

有很多方法可以解决这个问题,例如 George E. 建议使用惰性变量。我不想走那条路,因为那超出了初学者的范围。出于您的目的,只要知道,当您定义一个变量时,它必须给出一个实际值。编译器不会抱怨 var x = 1 因为它知道 1 是什么,而且它已经存在了。

您的确切错误是您无法使用实例 card,因为在您尝试使用它时它尚未初始化。换句话说,当您尝试分配它时,它没有任何价值,例如 card.shadingshading

现在,话虽这么说,你为什么需要阴影?你想要的底纹不是一直都是card.shading吗?与其创建一个单独的变量,不如使用您拥有的变量。学会喜欢点符号并保持简单。

除了你原来的问题之外,最后要考虑的是你有一个没有任何状态变量的视图。如果您尝试更改未标记为@State 的变量,您的编译器将在运行时报错。您创建的是固定视图,不能更改或更新。

如果您依赖于属性初始化,您需要在 init 中执行此操作。

struct CardView: View {
    var card: SetGameModel.Card
    // ... other code
    var shading: SetGameModel.Card.Shading = card.shading

    init(card: SetGameModel.Card, ) {
       self.card = card
       self.shading = card.shading         // << here !!
    }

    // ... other code
}

替换那些行

var shading: SetGameModel.Card.Shading = card.shading
var isFilled: Bool { shading == .solid ? true : false }
var isStriped: Bool { shading == .striped ? true : false }

var isFilled: Bool {
    return card.shading == .solid
}
var isStriped: Bool {
    return card.shading == .striped
}

Classes and structures must set all of their stored properties to an appropriate initial value by the time an instance of that class or structure is created. Stored properties cannot be left in an indeterminate state.

阅读此文档以清楚地了解为什么不能使用 card.shadinghttps://docs.swift.org/swift-book/LanguageGuide/Initialization.html