修改 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 的建议进行自动修复不会产生有效的解决方案。我觉得我在这里缺少一个核心原则。
正如我在上面的评论中提到的:
timeInputCopy
没有意义——它不是真正的副本,它只是 returns timeInput
[ 的计算 属性 =17=]
在带有选择器的 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.
我正在努力学习 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 的建议进行自动修复不会产生有效的解决方案。我觉得我在这里缺少一个核心原则。
正如我在上面的评论中提到的:
timeInputCopy
没有意义——它不是真正的副本,它只是 returnstimeInput
[ 的计算 属性 =17=]在带有选择器的 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.