在主体外访问 SwiftUI 视图中的状态变量

Posted

技术标签:

【中文标题】在主体外访问 SwiftUI 视图中的状态变量【英文标题】:Accessing state variable in SwiftUI View outside the body 【发布时间】:2019-09-05 15:35:32 【问题描述】:

我有一个内容视图,我想动态跟踪高度。

我已经实现了这个解决方案,但我遇到了这个异常:

致命错误:访问 View.body 外部的状态:文件

我该怎么做才能让键盘高度动态更新我的 UI

struct ContentView: View 

    @State var keyboardHeight: CGFloat = 0
    var cancellables: Set<AnyCancellable> = []

    init() 
        NotificationCenter.default.publisher(for: UIResponder.keyboardWillChangeFrameNotification)
        .merge(with: NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification))
        .compactMap( notification in
          guard let keyboardFrameValue: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else  return nil 
          let keyboardFrame = keyboardFrameValue.cgRectValue
          if keyboardFrame.origin.y == UIScreen.main.bounds.height 
            return 0
           else 
            return keyboardFrame.height - (UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0)
          
        )
        .assign(to: \.keyboardHeight, on: self)
        .store(in: &cancellables)
    

    var body: some View 
        VStack
            ZStack(alignment: Alignment.bottom) 
                List 
                    Text("Default text").foregroundColor(Color.red)
                
                TextField("Placeholder", text: .constant(""))
                    .frame(minHeight: 30)
                    .cornerRadius(8.0)
                    .padding(10)
                    .background(Color.blue)
            
            Spacer()
                .frame(height: keyboardHeight)
        
    

【问题讨论】:

您可以将发布者创建为私有属性(没有assignTo),并使用onReceive 在视图主体中订阅它。我相信这会给你想要的。 【参考方案1】:

错误消息几乎准确地说明了问题所在。但是,尽管您无法访问主体函数(或从主体调用的函数)之外的状态变量,但您可以访问可观察对象。因此,只需通过 @ObservedObject 更改 @State 并定义一个小类,就足以使其工作。

请注意,此解决方案旨在保持您的工作流程理念不变。虽然,还有其他方法可以完成同样的事情。

import SwiftUI
import Combine

class Model: ObservableObject 
    @Published var keyboardHeight: CGFloat = 0


struct ContentView: View 
    @ObservedObject var model = Model()

    var cancellables: Set<AnyCancellable> = []

    init() 
        NotificationCenter.default.publisher(for: UIResponder.keyboardWillChangeFrameNotification)
            .merge(with: NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification))
            .compactMap( notification in
                guard let keyboardFrameValue: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else  return nil 

                let keyboardFrame = keyboardFrameValue.cgRectValue

                if keyboardFrame.origin.y == UIScreen.main.bounds.height 
                    return 0
                 else 
                    return keyboardFrame.height - (UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0)
                
            )
            .assign(to: \.keyboardHeight, on: model)
            .store(in: &cancellables)
    

    var body: some View 
        VStack
            ZStack(alignment: Alignment.bottom) 
                List 
                    Text("Default text").foregroundColor(Color.red)
                
                TextField("Placeholder", text: .constant(""))
                    .frame(minHeight: 30)
                    .cornerRadius(8.0)
                    .padding(10)
                    .background(Color.blue)
            
            Spacer()
                .frame(height: model.keyboardHeight)
        
    

【讨论】:

做同样事情的其他方法?是的,但是这种方式有什么问题吗?

以上是关于在主体外访问 SwiftUI 视图中的状态变量的主要内容,如果未能解决你的问题,请参考以下文章

导航栏中的 SwiftUI 元素不响应状态

视图主体中的 swiftui 三元运算符

SwiftUI Picker 在同一视图中更改第二个状态变量

SwiftUI 状态变量未更新

在视图外触发时如何绑定 SwiftUI.Alert 的呈现?

如何在 swiftui 中为子视图设置私有状态变量?