使用 SwiftUI 和 Combine 根据授权状态有条件地显示视图?

Posted

技术标签:

【中文标题】使用 SwiftUI 和 Combine 根据授权状态有条件地显示视图?【英文标题】:Using SwiftUI and Combine to conditionally display a view based on authorization status? 【发布时间】:2020-08-10 15:57:55 【问题描述】:

我正在构建一个 UI,根据 HealthKit 是否已获得授权,我希望在视图中显示“启用”按钮或绿色复选标记。我还希望视图具有反应性,以便一旦您授权 HealthKit,视图就会动态地从按钮变为复选标记,但我不知道如何正确地进行这两种通信以及使用哪个属性包装器:

struct SetUpWatchView: View 

    let healthKitAuthManager = HealthKitAuthManager()
@ViewBuilder
    var body: some View 
            VStack(alignment: .leading) 
                HStack 
                     Image(systemName: "heart.circle.fill")
                     .foregroundColor(.red)
                      .font(.system(size: 56.0, weight: .bold))
                      .frame(width: 65, height: 65)
                    VStack(alignment: .leading) 
                        Text("Health Integration")
                            .fontWeight(.bold)
                        Text("Enable in Order to Track your Speed, Distance, and Heart Rate.")
                    
                    Spacer()
                    if healthKitAuthManager.healthKitIsAuthorized 
                        Image(systemName: "checkmark.circle.fill")
                            .foregroundColor(.green)
                            .font(.system(size: 30.0, weight: .bold))
                             .padding(.horizontal)
                     else 
                        Button(action: 
                            healthKitAuthManager.authorizeHealthKit()
                        ) 
                            Text("ENABLE")
                                .fontWeight(.bold)
                                .foregroundColor(Color.black)
                        
                        .padding(.horizontal)
                    
                
                .padding([.leading, .bottom])
            .onAppear 
                healthKitAuthManager.checkWhetherHealthKitDatAvailableAndIfAuthorized()
            
        
    




class HealthKitAuthManager: ObservableObject 
    
    let healthStore = HKHealthStore()
    
    @Published var healthKitIsAuthorized = false
    
     public func checkWhetherHealthKitDatAvailableAndIfAuthorized()  
        
        if HKHealthStore.isHealthDataAvailable() 
            
            let authorizationStatus = healthStore.authorizationStatus(for: HKSampleType.workoutType())
            switch authorizationStatus 
            case .sharingAuthorized:
                
                healthKitIsAuthorized = true
                
            case .sharingDenied: ()
             healthKitIsAuthorized = false
            default:()
              healthKitIsAuthorized = false
            
            
        
        else 
            healthKitIsAuthorized = false 
        
    

    public func authorizeHealthKit() 

        let healthKitTypesToWrite: Set<HKSampleType> = [
            HKObjectType.workoutType(),
            HKSeriesType.workoutRoute(),
            HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!,
            HKObjectType.quantityType(forIdentifier: .heartRate)!,
            HKObjectType.quantityType(forIdentifier: .restingHeartRate)!,
            HKObjectType.quantityType(forIdentifier: .bodyMass)!,
            HKObjectType.quantityType(forIdentifier: .vo2Max)!,
            HKObjectType.quantityType(forIdentifier: .stepCount)!,
            HKObjectType.quantityType(forIdentifier: .distanceWalkingRunning)!]

        let healthKitTypesToRead: Set<HKObjectType> = [
            HKObjectType.workoutType(),
            HKSeriesType.workoutRoute(),
            HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!,
            HKObjectType.quantityType(forIdentifier: .heartRate)!,
            HKObjectType.quantityType(forIdentifier: .restingHeartRate)!,
            HKObjectType.characteristicType(forIdentifier: .dateOfBirth)!,
            HKObjectType.quantityType(forIdentifier: .bodyMass)!,
            HKObjectType.quantityType(forIdentifier: .vo2Max)!,
            HKObjectType.quantityType(forIdentifier: .stepCount)!,
            HKObjectType.quantityType(forIdentifier: .distanceWalkingRunning)!]

        let authorizationStatus = HKHealthStore().authorizationStatus(for: HKSampleType.workoutType())

        switch authorizationStatus 

        case .sharingAuthorized:

            print("Sharing Authorized")
            healthKitIsAuthorized = true

        case .sharingDenied: print("sharing denied")

        //Success does NOT necessarily mean we are authorized, only that the request was successfully delivered.  Also if a user chooses not to authorize, if you call .requestAuthorization again you won't get the action sheet
        HKHealthStore().requestAuthorization(toShare: healthKitTypesToWrite, read: healthKitTypesToRead)  (success, error) in
            if !success 
                print("failed HealthKit Authorization from iPhone SetUpWatchVC \(String(describing: error?.localizedDescription))")
            

            print("Successful HealthKit Authorization from iPhone")
            

        default: print("not determined")

        HKHealthStore().requestAuthorization(toShare: healthKitTypesToWrite, read: healthKitTypesToRead)  (success, error) in
            if !success 
                print("failed HealthKit Authorization from iPhone SetUpWatchVC \(String(describing: error?.localizedDescription))")
            

            print("Successful HealthKit Authorization from iPhone SetUpWatchVC")

            

        



    


【问题讨论】:

【参考方案1】:

尝试添加@ObservedObject var healthKitAuthManager = HealthKitAuthManager() 而不是let healthKitAuthManager = HealthKitAuthManager()。这样@Published 变量将触发新的视图渲染。

【讨论】:

谢谢,在我也将self.healthKitIsAuthorized = true 包裹在一个调用中以获取主队列之后,这确实有效......直到我在控制台中看到一个警告,我才意识到 HealthKit Auth 是一个 aync 调用.

以上是关于使用 SwiftUI 和 Combine 根据授权状态有条件地显示视图?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 SwiftUI 和 Combine 检测 Datepicker 的值变化?

如何使用 Combine 框架和 SwiftUI 发布网络请求的数据

在 SwiftUI 中使用 Combine 进行映射、捕获错误和分配

使用 Combine 和 SwiftUI 在 Realm 中观察收集结果

使用 Combine 和 SwiftUI 对点击的按钮进行更改

异步下载图像时SwiftUI和Combine工作不顺畅