我在.onAppear
中有一些代码,它可以运行,问题是我必须转到菜单上的另一个视图,然后返回以刷新UI视图。该代码有点冗长,但是下面是主要组件,其中,饭食数据来自CoreData并具有一些对象:
转到底部的更新注释以获取更简单的代码示例
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
,然后返回到视图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")
}
}
答案 0 :(得分:0)
您需要了解,您在body{...}
中输入的内容仅是如何显示此视图的说明。在运行时系统中,使用body
闭包创建此View,将其像图片一样存储在内存中,并将此图片固定在其中,并使用您在其中定义的所有存储属性进行构造。 Body
不是存储的属性。它是如何创建图片的唯一说明。
在您的代码中,第ini
t首位。您没有编写它,但是它运行并将所有structs属性设置为默认值= Date()
。之后运行.onAppear
闭包,更改past
的值。然后系统才运行body
闭包以使图像以新的past
值显示。就这样。它不是每秒都在重新创建自己。您需要触发它以检查条件past < futuredate
是否已更改。
当您转到另一个视图并返回时,您完全可以执行此操作-强制系统重新创建视图,这就是它再次检查条件的原因。
如果您希望在经过一定的固定时间后自动更改视图,请使用
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()
}
}
}
}
看到主意了吗?您启动计时器,它将通过发送带有View
的通知来强制Publisher
更新。 View使用.onReceive
获取该通知,并更改@State
变量counter
,并导致View更新。您需要执行类似的操作。