ios - SwiftUI 中的 UITextField 导致“在视图更新期间修改状态,这将导致未定义的行为。”
问题描述
我正在尝试在 SwiftUI 中创建一个系统,让我可以控制哪个文本字段是第一响应者,这样我就可以轻松地创建一个响应者链。
我创建了一个UIViewRepresentable
获取Binding
布尔类型的环境值的 a,并且我正在根据这些函数textFieldDidBeginEditing
和textFieldDidEndEditing
.
struct TextField: UIViewRepresentable {
@Binding var text: String
@Environment(\.isSecureField) var isSecureField: Bool
@Environment(\.isFirstResponder) var isFirstResponder: Binding<Bool>?
@Environment(\.placeholder) var placeholder: String?
@Environment(\.keyboardType) var keyboardType: UIKeyboardType
@Environment(\.returnKeyType) var returnKeyType: UIReturnKeyType
@Environment(\.textContentType) var contentType: UITextContentType?
@Environment(\.autoCapitalization) var autoCapitalization: Bool
@Environment(\.autoCorrection) var autoCorrection: Bool
@Environment(\.font) var font: Font?
let onCommit: (() -> Bool)?
init(
text: Binding<String>,
onCommit:(() -> Bool)? = nil) {
self._text = text
self.onCommit = onCommit
}
func makeUIView(context: Context) -> UITextField {
let textField = UITextField()
textField.delegate = context.coordinator
syncValues(context: context, textField: textField)
return textField
}
func updateUIView(_ uiView: UITextField, context: Context) {
syncValues(context: context, textField: uiView)
if let isFirstReposnderValue = isFirstResponder?.wrappedValue {
switch isFirstReposnderValue {
case true:
uiView.becomeFirstResponder()
case false:
uiView.resignFirstResponder()
}
}
}
private func syncValues(context: Context, textField: UITextField) {
textField.text = text
textField.isSecureTextEntry = isSecureField
textField.textContentType = contentType
textField.keyboardType = keyboardType
textField.placeholder = placeholder
textField.font = UIFont.preferredFont(from: font)
textField.autocapitalizationType = autoCapitalization ? .sentences : .none
textField.returnKeyType = returnKeyType
textField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
textField.autocorrectionType = autoCorrection ? .yes : .no
}
func makeCoordinator() -> Coordinator {
return Coordinator( parent: self )
}
class Coordinator: NSObject, UITextFieldDelegate {
private let parent: TextField
init(parent: TextField) {
self.parent = parent
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
return parent.onCommit?() ?? true
}
func textFieldDidChangeSelection(_ textField: UITextField) {
parent.$text.wrappedValue = textField.text ?? ""
}
func textFieldDidBeginEditing(_ textField: UITextField) {
parent.isFirstResponder?.wrappedValue = true
}
func textFieldDidEndEditing(_ textField: UITextField) {
parent.isFirstResponder?.wrappedValue = false
}
}
}
此外,我为具有布尔类型的 Binding 创建了一个新的构造函数,因此我可以使用 Hashable(如枚举),因此我可以有两个以上的状态,并且没有真假。
extension Binding where Value == Bool {
init<V: Hashable>(tag: V, selection: Binding<V?>) {
self.init { () -> Value in
selection.wrappedValue == tag
} set: { (newValue) in
selection.wrappedValue = newValue ? tag : nil
}
}
}
这是我如何使用它的示例:
class TestLoginViewModel:ObservableObject {
@Published var email: String = ""
@Published var password: String = ""
}
struct TestLoginView: View {
enum Field {
case email
case password
}
@StateObject private var viewModel: TestLoginViewModel
@State private var activeField: Field?
init(viewModel: TestLoginViewModel = .init()) {
self._viewModel = StateObject(wrappedValue: viewModel)
}
var body: some View {
VStack(spacing: 8) {
TextField(text: $viewModel.email, onCommit: { () -> Bool in
activeField = .password
return true
})
.placeholder(L10n.email)
.isFirstReponder(.init(tag: .email, selection: $activeField))
.returnKeyType(.next)
.keyboard(.emailAddress)
TextField(text: $viewModel.password, onCommit: { () -> Bool in
activeField = nil
return true
})
.placeholder(L10n.password)
.isSecureField(true)
.isFirstReponder(.init(tag: .password, selection: $activeField))
.returnKeyType(.send)
.keyboard(.emailAddress)
}
}
}
但我得到:
在视图更新期间修改状态,这将导致未定义的行为。
解决方案
推荐阅读
- android - 无法在 Firebase 上为特定用户增加孩子的价值
- php - 如何从 php 中的 youtube id 创建 mysql 表?
- javascript - 在 Component 内部做出反应,为什么我们需要使用“this”。引用一个函数
- regex - 可能包含其他刹车的刹车之间的匹配
- laravel-5 - 在部分页面上显示 SQL 请求而不是视图 Laravel 5.5
- css - :target 伪元素内的动画仅在第一次单击时触发
- angular - 角度“选择”输入,在选择的选项上更改变量
- android - 如何正确自定义 SwitchPreferenceCompat 使其表现得像 Wi-Fi 设置首选项
- shell - 文件到文件夹 .bat 到 .sh
- c# - C#,如何手动设置光标位置