在SwiftUI视图之外访问状态变量

5

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

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

致命错误:在 View.body 外部访问 State:文件

我该如何才能使键盘高度是动态的,并更新我的用户界面?

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)
        }
    }
}

2
你可以将发布者创建为一个私有属性(不使用assignTo),并在视图主体中使用onReceive订阅它。我相信这会给你想要的结果。 - Benjamin Kindle
1个回答

8

这个错误信息已经明确指出了问题所在。然而,虽然你不能在body函数之外(或从body调用的函数中)访问State变量,但是你可以访问observable对象。所以只需将@State更改为@ObservedObject并定义一个小类即可使其正常工作。

请注意,这个解决方案是为了保持你的工作流思路不变。虽然还有其他方法可以实现同样的效果。

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)
        }
    }
}

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接