SwiftUI:强制更新

Chr*_*all 25 swift swiftui

通常,我们被限制讨论 Apple 预发布的内容,但我已经看到很多 SwiftUI 的讨论,所以我怀疑这没关系;就这一次。

我正在学习其中一个教程(我这样做)。

我在“与 UIKit 交互”教程中的可滑动屏幕下方添加了一对按钮:https : //developer.apple.com/tutorials/swiftui/interface-with-uikit

这些是“下一个”和“上一个”按钮。当在一端或另一端时,相应的按钮会隐藏。我有那个工作正常。

我遇到的问题是访问由 PageViewController 表示的 UIPageViewController 实例。

我更改了 currentPage 属性(通过使 PageViewController 成为 UIPageViewController 的委托),但我需要强制 UIPageViewController 以编程方式更改。

我知道我可以通过重绘 PageView 主体来“蛮力”显示,反映一个新的 currentPage,但我不确定如何做到这一点。

struct PageView<Page: View>: View {
    var viewControllers: [UIHostingController<Page>]
    @State var currentPage = 0

    init(_ views: [Page]) {
        self.viewControllers = views.map { UIHostingController(rootView: $0) }
    }

    var body: some View {
        VStack {
            PageViewController(controllers: viewControllers, currentPage: $currentPage)

            HStack(alignment: .center) {
                Spacer()

                if 0 < currentPage {
                    Button(action: {
                        self.prevPage()
                    }) {
                        Text("Prev")
                    }

                    Spacer()
                }

                Text(verbatim: "Page \(currentPage)")

                if currentPage < viewControllers.count - 1 {
                    Spacer()

                    Button(action: {
                        self.nextPage()
                    }) {
                        Text("Next")
                    }
                }

                Spacer()
            }
        }
    }

    func nextPage() {
        if currentPage < viewControllers.count - 1 {
            currentPage += 1
        }
    }

    func prevPage() {
        if 0 < currentPage {
            currentPage -= 1
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我知道答案应该是显而易见的,但我很难弄清楚如何以编程方式刷新 VStack 或主体。

And*_*rew 24

2021 SWIFT 1 和 2:

重要的事情!如果你搜索这个 hack,可能你做错了什么!请在阅读黑客解决方案之前阅读此块!!!!!!!!!!!!

你的 UI 没有自动更新,因为你错过了一些重要的东西。

  • 你的 ViewModel 必须被class包装成ObservableObject/Observed对象
  • ViewModel 中的任何字段都必须是STRUCT. 不是一个CLASS!!!Swift UI 不适用于类!
  • 必须正确使用修饰符(状态、可观察/观察对象、已发布、绑定等)
  • 如果您class的视图模型中需要一个属性(出于某种原因)-您需要将其标记为ObservableObject/ Observedobject 并将它们分配到 View 的对象中!!!!!!!!!里面init()View。!!!!!!!!!
  • 有时需要使用hacks。但这真的是非常非常独特的情况!大多数情况下这是错误的方式!再一次:请使用structs 而不是类!

如果以上所有内容都正确使用,您的 UI 将自动刷新。

以及主题问题的答案。

(黑客解决方案 - 最好不要使用这个)

方式1:在视图内部声明:

@State var updater: Bool = false
Run Code Online (Sandbox Code Playgroud)

所有你需要更新的是工具()它: updater.toogle()


方式二:从ViewModel刷新

适用于 SwiftUI 2

public class ViewModelSample : ObservableObject
    func updateView(){
        self.objectWillChange.send()
    }
}
Run Code Online (Sandbox Code Playgroud)

方式三:从ViewModel刷新:

适用于 SwiftUI 1

import Combine
import SwiftUI

class ViewModelSample: ObservableObject {
    private let objectWillChange = ObservableObjectPublisher()

    func updateView(){
        objectWillChange.send()
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @Andrew:非常好的答案**“如果你搜索这个黑客,可能你做错了什么!”** (4认同)
  • 如果我们不在主体变量中使用更新器状态,则方法 1 不起作用 (2认同)

Lu_*_*Lu_ 9

设置currentPage,因为它是一个@State,将重新加载整个身体。


Hat*_*nzō 8

这是另一个对我有用的解决方案,使用 id() 标识符。基本上,我们并不是真正令人耳目一新的看法。我们正在用新的视图替换视图。

import SwiftUI

struct ManualUpdatedTextField: View {
    @State var name: String
    
    var body: some View {
        VStack {
            TextField("", text: $name)
            Text("Hello, \(name)!")
        }
    }
}

struct MainView: View {
    
    @State private var name: String = "Tim"
    @State private var theId = 0

    var body: some View {
        
        VStack {
            Button {
                name += " Cook"
                theId += 1
            } label: {
                Text("update Text")
                    .padding()
                    .background(Color.blue)
            }
            
            ManualUpdatedTextField(name: name)
                .id(theId)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 您还可以使用日期(时间戳)作为 id,而不是递增值 (3认同)