11

在我正在关注的 Ray 的教程中,我设置了以下属性

struct ContentView : View {

    var rTarget = Double.random(in: 0..<1)
    var gTarget = Double.random(in: 0..<1)
    var bTarget = Double.random(in: 0..<1)
}

这些当然是不可变的,所以我不能从 func 修改它们,除非我将该 func 标记为 mutating

func reset() {
    rTarget = Double.random(in: 0..<1)
    gTarget = Double.random(in: 0..<1)
    bTarget = Double.random(in: 0..<1)
}

Cannot assign to property: 'self' is immutable

但我称这个函数来自var body

mutating func reset() {
    rTarget = Double.random(in: 0..<1)
    gTarget = Double.random(in: 0..<1)
    bTarget = Double.random(in: 0..<1)
}

fileprivate mutating func displayAlert() -> Alert {
    return Alert(title: Text("Your Score"), message: Text("\(computeScore())"), dismissButton: Alert.Button.destructive(Text("Ok"), onTrigger: {
        self.reset()
    }))
}

 var body: some View {
    Button(action: {
        self.showAlert = true
    }) {
        Text("Hit Me!")
        }.presentation($showAlert) {
            displayAlert()
    }
}

Cannot use mutating member on immutable value: 'self' is immutable

但我不能标记var bodymutatingvar

'mutating' may only be used on 'func' declarations

所以在这一点上,我想xTarget在用户每次点击警报按钮时重置这些值,我不知道此时会有什么好的做法。

4

4 回答 4

9

我正在写同一篇文章。

我没有遇到这个问题,因为我已经在使用@State属性包装器了。正如kontiki Session 226 (Data Flow Through SwiftUI) 所建议的那样,对于了解在更新不同源的数据时如何使用哪个属性包装器非常有用。

如果你想知道@State 是什么,这个答案就可以了。

这是我的代码:

@State var rTarget = Double.random(in: 0..<1)
@State var gTarget = Double.random(in: 0..<1)
@State var bTarget = Double.random(in: 0..<1)
@State var rGuess: Double
@State var gGuess: Double
@State var bGuess: Double
于 2019-06-23T09:59:41.097 回答
3

我在这一点上找到的唯一解决方案是将xTarget道具标记为@State并修改它们而不改变函数

@State private var rTarget = Double.random(in: 0..<1)
@State private var gTarget = Double.random(in: 0..<1)
@State private var bTarget = Double.random(in: 0..<1)

但我不清楚这是一种好习惯。

于 2019-06-23T06:23:53.680 回答
3

很难说出在您的情况下推荐什么,因为在您的示例中,您没有向我们展示目标变量的实际用途。

但是,我认为可以肯定地说,在 SwiftUI 视图中,需要随时间变化的变量必须是 @State 或您可用的绑定类型之一。否则,它很可能需要是不可变的。

这一切都归结为确定什么是“真相的来源”。如果这些目标是视图内部的东西,那么使用@State,如果事实来源在视图外部,那么您将选择可绑定选项之一。

我强烈建议您花 37 分钟观看 WWDC2019,Session 226(通过 SwiftUI 的数据流)。它将清除您对此的所有问题。

如果您赶时间,请跳到 5:45。但我确实建议你观看整件事。它最终会节省你的时间。

如果你不知道“真相的来源”是什么。那么您绝对应该观看会议。

于 2019-06-23T09:44:58.057 回答
2

这个问题很老,但是如果没有更多的上下文,很难理解和理解答案。问题来自RayWenderlich - SwiftUI: Getting Started

在出现警报后,您需要做两件事才能重置游戏:

  1. mutating从您的功能中删除displayAlert()
  2. @State在要修改的变量前面添加(即rTarget, gTarget, bTarget

完整代码供参考 - 请注意,我使用重置游戏func resetGame()

import SwiftUI

struct ContentView: View {
    // In SwiftUI, when a @State variable changes,
    // the view invalidates its appearance and recomputes the body.
    @State var randomRed = Double.random(in: 0..<1)
    @State var randomGreen = Double.random(in: 0..<1)
    @State var randomBlue = Double.random(in: 0..<1)

    @State var redGuess: Double
    @State var greenGuess: Double
    @State var blueGuess: Double

    @State var showAlert: Bool = false

    func calculateScore() -> String {
        // The diff value is just the distance between two points in three-dimensional space.
        // You subtract it from 1, then scale it to a value out of 100.
        // Smaller diff yields a higher score.
        let redDiff = redGuess - randomRed
        let greenDiff = greenGuess - randomGreen
        let blueDiff = blueGuess - randomBlue
        let diff = sqrt(redDiff * redDiff + greenDiff * greenDiff + blueDiff * blueDiff)

        return "\(Int((1.0 - diff) * 100.0 + 0.5))"
    }

     func resetGame() {
        randomRed = Double.random(in: 0..<1)
        randomGreen = Double.random(in: 0..<1)
        randomBlue = Double.random(in: 0..<1)

        redGuess = 0.5
        greenGuess = 0.5
        blueGuess = 0.5
    }

    var body: some View {
        VStack {
            HStack {
                VStack {
                    Color(red: randomRed, green: randomGreen, blue: randomBlue)
                    Text("Match this color")
                }
                VStack {
                    Color(red: redGuess, green: greenGuess, blue: blueGuess)
                    Text("R: \(Int(redGuess * 255))  G: \(Int(greenGuess * 255))  B: \(Int(blueGuess * 255))")
                }
            }

            Button(action: {self.showAlert = true} ) {
                Text("Hit me")
            }.alert(isPresented: $showAlert, content: {
                Alert(title: Text("Your Score"), message: Text(self.calculateScore()),
                      dismissButton: Alert.Button.default(Text("OK"), action: { self.resetGame()
                }))
            }).padding()

            ColorSlider(value: $redGuess, textColor: .red)
            ColorSlider(value: $greenGuess, textColor: .green)
            ColorSlider(value: $blueGuess, textColor: .blue)
        }
    }
}

struct ColorSlider: View {
    // Use @Binding instead of @State, because the ColorSlider view
    // doesn't own this data—it receives an initial value from its parent view and mutates it.
    @Binding var value: Double
    var textColor: Color

    var body: some View {
        HStack {
            Text("0").foregroundColor(textColor)
            Slider(value: $value)
            Text("255").foregroundColor(textColor)
        }.padding(.horizontal)  // Add some space before & after the text
    }
}
于 2019-11-16T05:29:24.547 回答