繁体   English   中英

当.onAppear 运行 SwiftUI 时视图未刷新

[英]View not refreshed when .onAppear runs SwiftUI

我在.onAppear中有一些代码并且它运行,问题是我必须 go 到菜单上的另一个视图,然后回来刷新 UI 视图。 代码有点长,但主要组件如下,其中 mealData 来自 CoreData 并有一些对象:

Go 到底部的更新注释以获得更简单的代码示例

VStack(alignment: .leading) {

            ScrollView(.vertical, showsIndicators: false) {
                VStack(spacing: 2) {
                    ForEach(mealData, id: \.self) { meal in
                        VStack(alignment: .leading) { ... }
                        .onAppear {

                            // If not first button and count < total amt of objects
                            if((self.settingsData.count != 0) && (self.settingsData.count < self.mealData.count)){
                                let updateSettings = Settings(context: self.managedObjectContext)

                                // If will_eat time isn't nill and it's time is overdue and meal status isn't done
                                if ((meal.will_eat != nil) && (IsItOverDue(date: meal.will_eat!) == true) && (meal.status! != "done")){
                                    self.mealData[self.settingsData.count].status = "overdue"

                                    print(self.mealData[self.settingsData.count])

                                        if(self.settingsData.count != self.mealData.count-1) {
                                            // "Breakfast": "done" = active - Add active to next meal
                                            self.mealData[self.settingsData.count+1].status = "active"
                                        }

                                        updateSettings.count += 1

                                    if self.managedObjectContext.hasChanges {
                                        // Save the context whenever is appropriate
                                        do {
                                            try self.managedObjectContext.save()
                                        } catch let error as NSError {
                                            print("Error loading: \(error.localizedDescription), \(error.userInfo)")
                                        }
                                    }
                                }
                            }
                        }
                    }
                 }
             }
         }

很可能是因为 UI 没有自动刷新,所以我做错了什么,但是什么?

更新:

我做了一个小例子来复制正在发生的事情,如果你运行它,然后单击set future date ,然后等待 5 秒,你会看到该框没有改变颜色,之后,单击Go to view 2和go 返回视图 1,您将看到盒子颜色如何变化......这也是上面发生的事情:

import SwiftUI

struct ContentView: View {

@State var past = Date()
@State var futuredate = Date()


var body: some View {

    NavigationView {
        VStack {
            NavigationLink(destination: DetailView())
            { Text("Go to view 2") }

            Button("set future date") {
                self.futuredate = self.past.addingTimeInterval(5)
            }

            VStack {
                if (past < futuredate) {
                    Button(action: {
                    }) {
                        Text("")
                    }
                    .padding()
                    .background(Color.blue)
                } else {
                    Button(action: {
                    }) {
                        Text("")
                    }
                    .padding()
                    .background(Color.black)
                }
            }
        }
        .onAppear {
            self.past = Date()
        }
    }
}

}


struct DetailView: View {

@Environment(\.presentationMode) var presentationMode: Binding

var body: some View {
   Text("View 2")
}
}

您需要了解您在body{...}中输入的所有内容只是如何显示此视图的说明。 在运行时系统中,使用body闭包创建此视图,在 memory 中获取它,就像图片一样,并将此图片固定到您在其中定义的所有存储属性的结构中。 Body不是存储属性。 它唯一的说明如何创建该图片。

在您的代码中,首先进行ini 您没有编写它,但它运行并将所有 structs 属性设置为默认值= Date() 之后运行.onAppear闭包,它会改变past的值。 然后系统才运行body闭合以使图像以新的past值显示。 就这样。 它不是每秒钟都在重新创建自己。 您需要触发它以检查past < futuredate的条件是否发生了变化。

当您将 go 转到另一个视图并返回时,您正是这样做的 - 强制系统重新创建视图,这就是它再次检查条件的原因。

如果您希望 View 在花费一些固定时间后自动更改,请使用

DispatchQueue.main.asyncAfter(deadline: .now() + Double(2)) {
    //change @State variable to force View to update
}

对于更复杂的情况,您可以像这样使用 Timer:

class MyTimer: ObservableObject {
    @Published var currentTimePublisher: Timer.TimerPublisher
    var cancellable: Cancellable?
    let interval: Double
    var isOn: Bool{
        get{if self.cancellable == nil{
                return false
            }else{
                return true
            }
        }
        set{if newValue == false{
                self.stop()
            }else{
                self.start()
            }
        }
    }

    init(interval: Double, autoStart: Bool = true) {
        self.interval = interval
        let publisher = Timer.TimerPublisher(interval: interval, runLoop: .main, mode: .default)
        self.currentTimePublisher = publisher
        if autoStart{
            self.start()
        }
    }
    func start(){
        if self.cancellable == nil{
            self.currentTimePublisher = Timer.TimerPublisher(interval: self.interval, runLoop: .main, mode: .default)
            self.cancellable = self.currentTimePublisher.connect()
        }else{
            print("timer is already started")
        }
    }
    func stop(){
        if self.cancellable != nil{
            self.cancellable!.cancel()
            self.cancellable = nil
        }else{
            print("timer is not started (tried to stop)")
        }
    }
    deinit {
        if self.cancellable != nil{
            self.cancellable!.cancel()
            self.cancellable = nil
        }
    }
}
struct TimerView: View {
    @State var counter: Double
    let timerStep: Double
    @ObservedObject var timer: TypeTimer
    init(timerStep: Double = 0.1, counter: Double = 10.0){
        self.timerStep = timerStep
        self._counter = State<Double>(initialValue: counter)
        self.timer = MyTimer(interval: timerStep, autoStart: false)
    }
    var body: some View {
        Text("\(counter)")
            .onReceive(timer.currentTimePublisher) {newTime in
                print(newTime.description)
                if self.counter > self.timerStep {
                    self.counter -=  self.timerStep
                }else{
                    self.timer.stop()
                }
            }
        .onTapGesture {
            if self.timer.isOn {
                self.timer.stop()
            }else{
                self.timer.start()
            }
         }
    }
}

看到这个想法了吗? 您启动计时器,它将通过向Publisher发送通知来强制View更新。 View 通过.onReceive获取通知并更改@State变量counter ,这会导致 View 自行更新。 你需要做这样的事情。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM