如何解决如何在 SwiftUI 中引用外部 iOS 系统状态更新?
这个问题有很多可能的变体,但以CNAuthorizationStatus
返回的CNContactStore.authorizationStatus(for: .contacts)
为例,可以是notDetermined
、restricted
、{{1} },或denied
。我的目标是始终在我的应用界面中显示当前的授权状态。
为了将其公开给 SwiftUI,我可能会使用 authorized
属性创建一个名为 ObservableObject
的 ModelData
:
contacts
其中 final class ModelData: ObservableObject {
@Published var contacts = Contacts.shared
}
包含我的联系人特定型号代码,包括授权:
contacts
class Contacts {
fileprivate let store = CNContactStore()
static let shared = Contacts()
enum Authorization {
case notDetermined
case restricted
case denied
case authorized
}
var authorization: Authorization {
switch CNContactStore.authorizationStatus(for: .contacts) {
case .notDetermined:
return .notDetermined
case .restricted:
return .restricted
case .denied:
return .denied
case .authorized:
return .authorized
@unkNown default:
return .notDetermined
}
}
}
为了简单起见,我的观点只是:
func requestAccess(handler: @escaping (Bool,Error?) -> Void) {
store.requestAccess(for: .contacts) { (granted,error) in
// Todo: tell SwiftUI views to re-check authorization
dispatchQueue.main.async {
handler(granted,error)
}
}
}
所以我的问题是:
- 鉴于
Text(String(describing: modelData.contacts.authorization))
调用的是 getter 函数,而不是属性,当我知道它已更改(例如,Todo 在ModelData().contacts.authorization
函数中的位置)时,如何通知 SwiftUI 视图? - 鉴于用户可以在“设置”应用中切换权限(即,值可能会从我下面更改),我如何确保视图状态始终更新? (我是否需要订阅 NSNotification 并类似地强制刷新?或者有更好的方法吗?)
解决方法
正如@jnpdx 指出的那样 - 将 @Published
与类(尤其是从不改变的单例)一起使用可能不会产生任何有用的结果
@Published
的行为与 CurrentValueSubject
类似,只有在它在后台存储/观察的值发生变化时才会触发更新。由于它存储对 Contacts.shared
实例的引用,因此不会为授权状态更改提供/触发任何更新。
现在回答你的问题——
鉴于 ModelData().contacts.authorization
调用的是 getter 函数,而不是属性,当我知道它已更改时如何通知 SwiftUI 视图
只要您直接访问 getter ModelData().contacts.authorization
之外的值,它只是一个不提供任何可观察性的 Contacts.Authorization
类型的值。
因此,即使值随时间变化(从 .notDetermined
=> .authorized
),也没有存储(参考点)可以用来比较它自上次以来是否发生变化。
我们必须定义一个可以比较旧/新值并根据需要触发更新的存储。这可以通过将 authorization
标记为 @Published
来实现,如下所示 -
import SwiftUI
import Contacts
final class Contacts: ObservableObject {
fileprivate let store = CNContactStore()
static let shared = Contacts()
enum Authorization {
case notDetermined
case restricted
case denied
case authorized
}
/// Since we have a storage (and hence a way to compare old/new status values)
/// Anytime a new ( != old ) value is assigned to this
/// It triggers `.send()` which triggers an update
@Published var authorization: Authorization = .notDetermined
init() {
self.refreshAuthorizationStatus()
}
private func refreshAuthorizationStatus() {
authorization = self.currentAuthorization()
}
private func currentAuthorization() -> Authorization {
switch CNContactStore.authorizationStatus(for: .contacts) {
case .notDetermined:
return .notDetermined
case .restricted:
return .restricted
case .denied:
return .denied
case .authorized:
return .authorized
@unknown default:
return .notDetermined
}
}
func requestAccess() {
store.requestAccess(for: .contacts) { [weak self] (granted,error) in
DispatchQueue.main.async {
self?.refreshAuthorizationStatus()
}
}
}
}
struct ContentView: View {
@ObservedObject var contacts = Contacts.shared
var body: some View {
VStack(spacing: 16) {
Text(String(describing: contacts.authorization))
if contacts.authorization == .notDetermined {
Button("Request Access",action: {
contacts.requestAccess()
})
}
}
}
}
,
我认为您已经完成了所有工作。 当用户从“设置”应用更改访问级别时,会调用此行。
Text(String(describing: modelData.contacts.authorization))
因此您的视图始终显示当前状态。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。