Swift 从实战到理论:@Publish 和 @ObservedObject
Swift 从实战到理论:@Publish 和 @ObservedObject

Swift 从实战到理论:@Publish 和 @ObservedObject

实战问题

在开发涉及Store Kit 2的过程当中,当我尝试点击“赞助应用”时,因为product.purchase()方法是异步调取的,所以需要等待异步获取成功后,才会弹出内购窗口。

当异步方法在调取的过程中,加载比较缓慢的话,那么用户以为按钮没有反应,就会尝试关闭当前窗口或者点击其他按钮,当异步方法返回后,就会在用户新的操作界面下弹出内购窗口,因此是不合时宜的。

为了解决这一问题,我需要设置一个加载动画,让用户点击之后显示一个overlay覆盖层/遮罩层:

.overlay(
    // 加载内购商品动画层
    Group {
        if IAPManager.shared.loadPurchased {
            ZStack {
                Color.black.opacity(0.3).edgesIgnoringSafeArea(.all)
                ...
            }
        }
    }  
)

这个覆盖层会判断IAPManager.shared.loadPurchased是否为true,当为true时,显示遮罩层,当为false时,隐藏遮罩层。

注意:IAPManager.shared是单例模式,所以可以直接这样设置。

然后在按钮部分添加对IAPManager.shared.loadPurchased的修改:

HStack{
    // 赞助应用的内容
}
.contentShape(Rectangle()) // 扩展点击区域
.onTapGesture {
    print("点击了内购按钮")
    IAPManager.shared.loadPurchased = true
}

当点击按钮时,IAPManager.shared.loadPurchased被设置为true,这时遮罩层显示出来加载动画。

在IAPManager类中,我使用的是单例模式,因此IAPManager.shared.loadPurchased变成了一个全局变量,所有上面的操作都是合理的。

class IAPManager {
    static let shared = IAPManager()
    var loadPurchased = false     // 如果开始内购流程,loadPurchased为true,View视图显示加载画布
    ...
}

但是在实际的操作中,当我点击“赞助应用”按钮时,并不会显示我所设置的遮罩层,而是等待异步方法调取成功后,返回内购界面。

这个问题经过分析发现,问题在于IAPManager.shared.loadPurchased 的值更新后,Settings 视图没有重新刷新导致的。因为更新静态对象的属性(如 IAPManager.shared)不会触发视图的更新。

可以通过@Publish和@observedObject来解决这一问题,接着引出本文的主题内容。

@Published 和 @ObservedObject是什么?

在 Swift 中,@Published 和 @ObservedObject 是两种用于管理和观察数据状态变化的属性包装器。它们在 SwiftUI 中非常重要,因为它们帮助构建响应式 UI,使视图能够自动更新和重新渲染。

@Published

定义: @Published 是用于 ObservableObject 类中的属性包装器。

作用: 当一个 @Published 属性的值发生变化时,任何观察该对象的 SwiftUI 视图都会自动重新渲染。

场景: 通常用于需要在应用程序中全局共享和更新的状态变量。例如,我的应用需要知道一个购买状态是否已经完成。

class IAPManager: ObservableObject {
    static let shared = IAPManager()
    @Published var loadPurchased = false
}

所以,当我的SwiftUI视图需要根据loadPurchased属性判断,是否显示加载动画时。我就需要给loadPurchased添加@Published属性包装器,以便让SwiftUI观察IAPManager的loadPurchased属性并自动重新渲染。

@ObservedObject

定义: @ObservedObject 是一种用于 SwiftUI 视图中的属性包装器,用于引用外部的 ObservableObject。

作用: 它使视图能够观察到某个 ObservableObject 中 @Published 属性的变化。一旦变化发生,视图会自动重新渲染。

场景: 当一个视图需要依赖外部对象(如视图模型)的状态时,可以使用 @ObservedObject 观察这些状态变化。

struct Settings: View {
    @ObservedObject var iapManager = IAPManager.shared
    
    var body: some View {
        VStack {
            if iapManager.loadPurchased {
                Text("Purchase Complete!")
            } else {
                Text("No Purchase Yet")
            }
        }
    }
}

Settings 视图会观察 iapManager 中的 loadPurchased 属性。每当 loadPurchased 发生变化,Settings 视图将自动重新渲染。

为什么 @Published 和 @ObservedObject 结合使用?

使用 @Published 可以确保在 IAPManager 中,loadPurchased 的变化会被监听到。

@ObservedObject 使视图能够观察到这些变化,并触发自动更新。

没有这两个机制,SwiftUI 视图将无法知道状态变化,因此也无法自动更新 UI。

总结

@Published: 通知状态发生变化的属性包装器,必须在 ObservableObject 类中使用。

@ObservedObject: 用于 SwiftUI 视图中的属性包装器,以观察 ObservableObject 中的 @Published 属性变化,从而更新视图。

总的来讲,就是在Swift当中需要设置一个@ObservedObject的属性包装器:

@ObservedObject var iapManager = IAPManager.shared

然后对应的类需要符合ObservableObject协议:

class IAPManager:ObservableObject {}

最后给需要观察的属性添加一个@Published:

@Published var loadPurchased = false

到这里,@Publish和@ObservedObject需要添加的部分就完成了。

主要涉及SwiftUI中需要添加@ObservedObject、观察的类需要添加ObservableObject协议,观察的属性添加@Published。

返回到最开始的实战问题当中,完成上述的全部配置后:

.overlay(
    // 加载内购商品动画层
    Group {
        if iapManager.loadPurchased {
            ...
        }
    }
)

我的遮罩层只需要根据在SwiftUI中定义的iapManager.loadPurchased的变化来显示对应的遮罩层。

最后也是成功的观察到了loadPurchased的变化。

相关资料

Swift通过 @EnvironmentObjec共享和传递数据:https://fangjunyu.com/2024/10/23/swift%e9%80%9a%e8%bf%87-environmentobjec%e5%85%b1%e4%ba%ab%e5%92%8c%e4%bc%a0%e9%80%92%e6%95%b0%e6%8d%ae/

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开放者。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注