SwiftUI实例化的@State变量

我对SwiftUI很陌生。我有一个下面的“计数器”视图,它每秒计数一次。我想在更改颜色时“重置”计数器:

struct MyCounter : View {
  let color: Color
  @State private var count = 0

  init(color:Color) {
    self.color = color
    _count = State(initialValue: 0) 
  }
  var body: some View {
    Text("\(count)").foregroundColor(color)
    .onAppear(){
        Timer.scheduledTimer(withTimeInterval: 1,repeats: true) { _ in self.count = self.count + 1 }
    }
  }
}

这是我使用计数器的主要视图:

struct ContentView: View {
  @State var black = true
  var body: some View {
    VStack {
        MyCounter(color: black ? Color.black : Color.yellow)
        Button(action:{self.black.toggle()}) { Text("Toggle") }
    }
  }
}

当我单击“切换”按钮时,我看到MyCounter构造函数被调用,但是@State计数器仍然存在并且永远不会重置。所以我的问题是如何重置此@State值?请注意,我不希望将counter用作@Binding并在父视图中进行管理,而MyCounter是一个独立的小部件。 (这是一个简化的示例。我正在创建的实际窗口小部件是执行精灵动画的精灵动画制作器,当我交换图像时,我希望动画制作器从第0帧开始)。谢谢!

xsa591017 回答:SwiftUI实例化的@State变量

您需要一个绑定变量:

struct MyCounter : View {
      let color: Color
      @Binding var count: Int
      var body: some View {
      Text("\(count)").foregroundColor(color)
            .onAppear(){
                Timer.scheduledTimer(withTimeInterval: 1,repeats: true) { _ in self.count = self.count + 1 }
            }
       }
}

struct ContentView: View {
    @State var black = true
    @State var count : Int  = 0
    var body: some View {
         VStack {
              MyCounter(color: black ? Color.black : Color.yellow,count: $count)
               Button(action:{self.black.toggle()
                    self.count = 0
                }) { Text("Toggle") }
         }
     }
}

如果您不喜欢绑定,您也可以添加一个状态值innerColor来帮助您。

struct MyCounter : View {
    let color: Color
    @State private var count: Int = 0
    @State private var innerColor: Color?

    init(color: Color) {
        self.color = color

    }

    var body: some View {

        return Text("\(self.count)")
            .onAppear(){
                Timer.scheduledTimer(withTimeInterval: 1,repeats: true) { _ in self.count = self.count + 1 }
            }.foregroundColor(color).onReceive(Just(color),perform: { color in
                if self.innerColor !=  self.color {
                self.count = 0
                self.innerColor = color}
            })
    }
}
,

有两种方法可以解决此问题。一种方法是使用绑定,如E.Coms所述,这是解决问题的最简单方法。

或者,您可以尝试使用ObservableObject作为计时器的视图模型。这是更灵活的解决方案。您可以根据需要传递计时器,也可以将其作为环境对象注入。

class TimerModel: ObservableObject {
    // The @Published property wrapper ensures that objectWillChange signals are automatically emitted.
    @Published var count: Int = 0
    init() {}
    func start() {
        Timer.scheduledTimer(withTimeInterval: 1,repeats: true) { _ in self.count = self.count + 1 }
    }
    func reset() {
        count = 0
    }
}

然后您的计时器视图变为

struct MyCounter : View {
    let color: Color
    @ObservedObject var timer: TimerModel

    init(color: Color,timer: TimerModel) {
        self.color = color
        self.timer = timer
    }
    var body: some View {
        Text("\(timer.count)").foregroundColor(color)
        .onAppear(){
            self.timer.start()
        }
    }
}

您的内容视图变为

struct ContentView: View {
    @State var black = true
    @ObservedObject var timer = TimerModel()
    var body: some View {
        VStack {
            MyCounter(color: black ? Color.black : Color.yellow,timer: self.timer)
            Button(action: {
                self.black.toggle()
                self.timer.reset()
            }) { 
                Text("Toggle") 
            }
        }
    }
}

使用可观察对象的优势在于,您可以更好地跟踪计时器。您可以在模型中添加stop()方法,这会使计时器无效,并且可以在视图的onDisappear块中调用它。

关于此方法,您需要注意的一件事是,当您以独立方式使用计时器时,您在视图构建器中使用MyCounter(color: ...,timer: TimerModel())来创建计时器,每次重新渲染视图时,计时器模型将被替换,因此您必须确保以某种方式保持模型不变。

本文链接:https://www.f2er.com/3106452.html

大家都在问