SwiftUI从var body调用变异函数

时间:2019-06-23 06:06:37

标签: ios swift swiftui xcode11

在我遵循的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 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 body标记为mutating var

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

因此,在这一点上,我想在用户每次点击警报按钮时重置xTarget的值,但我不知道此时的良好做法。

4 个答案:

答案 0 :(得分:1)

很难说出您的建议是什么,因为在您的示例中,您没有向我们展示目标变量的实际用途。

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

一切都取决于确定什么是“真理之源”。如果这些目标是视图的内部对象,则使用@State,如果真相的来源位于视图的外部,则可以使用可绑定选项之一。

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

如果您着急,请跳至5:45。但是我建议您看整个过程。最终将节省您的时间。

如果您不知道“真理的源头”是什么。然后,您绝对应该观看会议。

答案 1 :(得分:1)

我正在写同一篇文章。

我没有遇到这个问题,因为我已经在使用@State属性包装器。 正如 kontiki 所建议的,Session 226(通过SwiftUI的数据流)非常适合理解在更新不同数据源时如何使用哪个属性包装器。

如果您想知道@State this answer是什么,那么

这是我的代码:

@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

答案 2 :(得分:0)

这时我发现的唯一解决方案是也将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)

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

答案 3 :(得分:0)

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

在发出警报后,您需要做两件事以重置游戏:

  1. 从功能mutating中删除displayAlert()
  2. 在要修改的变量(即@StaterTargetgTarget)之前添加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
    }
}