在支持 iOS 13.0 的同时在 iOS 14.0 中使用 @StateObject

Posted

技术标签:

【中文标题】在支持 iOS 13.0 的同时在 iOS 14.0 中使用 @StateObject【英文标题】:Using @StateObject in iOS 14.0 while supporting iOS 13.0 【发布时间】:2020-11-28 00:11:03 【问题描述】:

我需要帮助找到在 ios 14.0 中支持新 @StateObject 并在 iOS 13.0 中仍然支持某些替代方案的最佳方式。诚然,我不知道 iOS 13.0 中最好的方法是什么。以下是我目前拥有的。

有人对更好的方法有想法吗?

struct HomeView: View 
    let viewModel: HomeViewModel

    var body: some View 
        if #available(iOS 14, *) 
            HomeViewWrapper(viewModel: viewModel)
         else 
            CompatibleHomeViewWrapper(viewModel: viewModel)
        
    


@available(iOS 14, *)
private struct HomeViewWrapper: View 
    @StateObject var viewModel: HomeViewModel

    var body: some View 
        CompatibleHomeView(viewModel: viewModel)
    


private struct CompatibleHomeViewWrapper: View 
    @State var viewModel: HomeViewModel

    var body: some View 
        CompatibleHomeView(viewModel: viewModel)
    


struct CompatibleHomeView: View 
    @ObservedObject var viewModel: HomeViewModel

    var body: some View 
        Text(viewModel.someRandomName)
    

【问题讨论】:

使用@ObservedObject非常相似,iOS 13支持 实际上在这种情况下,我根本看不到 HomeViewWrapper 的必要性。在内部创建对象时需要 StateObject 包装器,但您拥有外部所有权,即使在父视图之外,所以不需要状态对象,因为在视图重建时不会重新创建 HomeViewModel。 @loremipsum。是的,@ObservedObject 是类似的,但它在 Apple 的文档和 WWDC 视频中都是我们不应该使用的。 @ObservedObject 仅适用于我们确实拥有资源的所有权。 @Asperi。虽然 HomeView 不会创建 HomeViewModel,但它确实拥有对它的所有权。 HomeViewModel 被创建并传递给 HomeView,然后 HomeView 将拥有它。 【参考方案1】:

您可以通过将自定义 propertyWrapper 包裹在 @State 和 @ObservedObject 周围来获得 @StateObject 行为,如下所示:

import Combine
import PublishedObject // https://github.com/Amzd/PublishedObject

/// A property wrapper type that instantiates an observable object.
@propertyWrapper
public struct StateObject<ObjectType: ObservableObject>
where ObjectType.ObjectWillChangePublisher == ObservableObjectPublisher 
    
    /// Wrapper that helps with initialising without actually having an ObservableObject yet
    private class ObservedObjectWrapper: ObservableObject 
        @PublishedObject var wrappedObject: ObjectType? = nil
        init() 
    
    
    private var thunk: () -> ObjectType
    @ObservedObject private var observedObject = ObservedObjectWrapper()
    @State private var state = ObservedObjectWrapper()
    
    public var wrappedValue: ObjectType 
        if state.wrappedObject == nil 
            // There is no State yet so we need to initialise the object
            state.wrappedObject = thunk()
        
        if observedObject.wrappedObject == nil 
            // Retrieve the object from State and observe it in ObservedObject
            observedObject.wrappedObject = state.wrappedObject
        
        return state.wrappedObject!
    
    
    public init(wrappedValue thunk: @autoclosure @escaping () -> ObjectType) 
        self.thunk = thunk
    

我自己也使用这个,所以我会保持更新: https://gist.github.com/Amzd/8f0d4d94fcbb6c9548e7cf0c1493eaff


注意:最受好评的评论是 ObservedObject 非常相似,这根本不是真的。

StateObject 在视图初始化之间保留对象并通过 willChangeObserver 将对象更改传递给视图。

这个解释非常粗略,有更好的地方请阅读它,因为它是 SwiftUI 的一个有影响力的部分。

【讨论】:

以上是关于在支持 iOS 13.0 的同时在 iOS 14.0 中使用 @StateObject的主要内容,如果未能解决你的问题,请参考以下文章

iOS 14.3 Odyssey 越狱消息,续签插件复活?

iOS 14.3 Odyssey 越狱消息,续签插件复活?

无法开发应用程序,因为在 iOS 13.0/Xcode 11 beta 5 中“无法在设备上找到应用程序”[重复]

我们需要将 iOS 部署目标设置为 13.0 吗?

使用 SVG 时出现 SF 符号不可用警告

如何在 XCode 中生成 iOS 13 SwiftUI 项目?