SwiftUI:如果 @ObservedObject 是 UIViewController 的子类,则不会重新加载视图内容。这是一个错误还是我错过了啥?
Posted
技术标签:
【中文标题】SwiftUI:如果 @ObservedObject 是 UIViewController 的子类,则不会重新加载视图内容。这是一个错误还是我错过了啥?【英文标题】:SwiftUI: View content not reloading if @ObservedObject is a subclass of UIViewController. Is this a bug or am I missing something?SwiftUI:如果 @ObservedObject 是 UIViewController 的子类,则不会重新加载视图内容。这是一个错误还是我错过了什么? 【发布时间】:2019-08-29 02:15:38 【问题描述】:我有一个 SwiftUI 视图,它显示和更新源自 @ObservedObject 的数据。下面的代码示例按预期工作,除非 @ObservedObject 恰好是 UIViewController 的子类。在这种情况下,@ObservedObject 会进行数据更新,但不会按预期触发视图内容的重新加载。
这是 SwiftUI 视图:
struct ContentView : View
@ObservedObject var scene: SceneViewController
var body: some View
VStack
Text("The time is: \(scene.currentSimTimestamp.description(with: .current))")
Button(action: self.scene.currentSimTimestamp = Date(),
label: Text("Update"))
这是@ObservedObject:
class SceneViewController: UIViewController, ObservableObject
@Published var currentSimTimestamp: Date = Date()
按下“更新”按钮将导致scene.currentSimTimestamp中存储的值更新,但是ContentView不会重新加载(屏幕不会更新以反映数据更改)。
将class SceneViewController: UIViewController, ObservableObject
更改为class SceneViewController: ObservableObject
将导致更新按预期显示。
这似乎是一个错误,因为我看到的 Apple 文档和视频似乎表明任何类都可以采用 ObservableObject,并且确实没有产生编译器问题。但我错过了什么吗?
(添加了示例 SceneDelegate 代码以将示例代码复制到项目中,如下)
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate
var window: UIWindow?
let sceneController: SceneViewController = SceneViewController()
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
// Create the SwiftUI view that provides the window contents.
let contentView = ContentView(scene: sceneController)
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
...
【问题讨论】:
您如何使用UIViewController
?我没有看到像UIViewControllerRepresentable
这样的参考。我认为这是在 SwiftUI
项目中使用 UIViewController
的唯一方法。 (如果没有,请发布足够的代码让我们重现您的问题。)
在实际项目中,UIViewController 包含 SwiftUI 视图(使用 UIHosingtController)。然而,在我的测试中,我发现这并不重要——即使 UIViewController 与 SwiftUI 视图不在同一个视图层次结构中,行为仍然与描述的一样。例如,在一个最简单的示例中,您可以使用默认的 Swift“使用 Swift UI 项目”SceneDelegate,如下所示:
【参考方案1】:
作为一种解决方法,我发现我可以从视图控制器中取出 @Published 属性包装器,并将其移动到一个新的(非 UIViewController)ObservableObject 类,该类仅用于发布此属性。有了这个作为唯一的变化,它按预期运行。显然,解决方法很笨拙,但它确实允许在 SwiftUI 视图中按预期使用现有 UIViewController 拥有的数据。这是更新:
class NewClass: ObservableObject
@Published var timestamp: Date = Date()
class SceneViewController: UIViewController
var currentSimTimestamp: NewClass()
override func viewDidLoad()
super.viewDidLoad()
// Shown here with our SwiftUI view as a child in our ViewController's hierarchy
let contentView = ContentView(newClass: currentSimTimestamp)
let contentViewController = UIHostingController(rootView: contentView)
addChild(contentViewController)
view.addSubview(contentViewController.view)
...
struct ContentView : View
@ObservedObject var newClass: NewClass
var body: some View
VStack
Text("The time is: \(newClass.timestamp.description(with: .current))")
Button(action: self.newClass.timestamp = Date(),
label: Text("Update"))
【讨论】:
事实上,只要你的 ObservableObject 是一个子类,它就不起作用,我对 viewModel 的子类有完全相同的问题。 我不觉得这个笨重。看起来非常接近 ViewModel。您甚至可以将其声明为struct
并使用 @State/Binding
【参考方案2】:
另外两种获得通知的方式:
最简单的就是调用发布者(AFAIK scene.currentSimTimestamp
不一定是@Published
):
Button(action:
self.scene.currentSimTimestamp = Date()
self.scene.objectWillChange.send()
,
label: Text("Update"))
涉及更多,但恕我直言,Combine 方式稍微干净一些: https://***.com/a/60126962/301790
【讨论】:
以上是关于SwiftUI:如果 @ObservedObject 是 UIViewController 的子类,则不会重新加载视图内容。这是一个错误还是我错过了啥?的主要内容,如果未能解决你的问题,请参考以下文章
如果它不可见,SwiftUI 如何使 NavigationLink 工作?