修改 Swift 中的用户输入

Modifying user input in Swift

我正在努力学习 Swift 并且已经学习了几个教程,但是,我似乎在兜圈子,需要一些关于如何解决这个问题的指导。

My goal is to create a decrementing timer, given a user-input (in seconds), in this case I've chosen a Stepper to -/+ the value. Then begin decrementing the timer on a button press, in this case "Decrement". The counter is displayed on the label.

如果我对起始值进行硬编码,这个问题就超级简单了,但是这对 UI 测试有什么用呢。所以,这是我能够想到的“具有挑战性”的任务,以帮助理解 SwiftUI 的工作原理。

我遇到的问题是用户传递的变量是不可变的。我曾尝试制作它的副本或将其分配给其他变量进行操作,但似乎在兜圈子。朝着正确的方向推动或潜在的解决方案将大有帮助。

struct ContentView: View {
    @State private var timeInput: Int = 0
    var timer = Timer()
    var timeInputCopy: Int {
        timeInput
    }
    var body: some View {
        Stepper("Input", value: $timeInput, in: 0...150)
        Button("Decrement", action: decrementFunction)
        Label(String(timeInputCopy), image: "")
            .labelStyle(TitleOnlyLabelStyle())
    }
func decrementFunction() {
    timer.invalidate()
    timer = Timer.schedulerTimer(timeInterval: 1, 
                  target: self, 
                  selector: #selector(ContentView.timerClass), 
                  userInfo: nil, 
                  repeats: true)
}
func timerClass() {
    timeInputCopy -= timeInputCopy
    if (timeInputCopy == 0) {
        timer.invalidate()
    }
}
> Cannot assign to property: 'self' is immutable
> Mark method 'mutating' to make 'self' mutable

尝试按照 Xcode 的建议进行自动修复不会产生有效的解决方案。我觉得我在这里缺少一个核心原则。

正如我在上面的评论中提到的:

  1. timeInputCopy 没有意义——它不是真正的副本,它只是 returns timeInput[ 的计算 属性 =17=]

  2. 在带有选择器的 SwiftUI 中使用这种形式的 Timer 不会有太大的运气。相反,请查看 Timer 发布者。

这是一种解决方案:

import Combine
import SwiftUI

class TimerManager : ObservableObject {
    @Published var timeRemaining = 0
    
    private var cancellable : AnyCancellable?
    
    func startTimer(initial: Int) {
        timeRemaining = initial
        cancellable = Timer.publish(every: 1, on: .main, in: .common)
            .autoconnect()
            .sink { _ in
                self.timeRemaining -= 1
                if self.timeRemaining == 0 {
                    self.cancellable?.cancel()
                }
            }
    }
}

struct ContentView: View {
    @StateObject private var timerManager = TimerManager()
    @State private var stepperValue = 60
    
    
    var body: some View {
        Stepper("Input \(stepperValue)", value: $stepperValue, in: 0...150)
        Button("Start") {
            timerManager.startTimer(initial: stepperValue)
        }
        Label("\(timerManager.timeRemaining)", image: "")
            .labelStyle(TitleOnlyLabelStyle())
    }
}

可以 全部在 View 中完成,但是使用 ObservableObject 可以很好地分离管理计时器状态与状态UI.