[英]SwiftUI NavigationLink push in onAppear immediately pops the view when using @ObservableObject
[英]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.