gpt4 book ai didi

swiftui - 在另一个绑定(bind)中包装 SwiftUI 绑定(bind)时无法更新值

转载 作者:行者123 更新时间:2023-12-05 06:19:31 25 4
gpt4 key购买 nike

我想制作一个登录代码屏幕。这由 4 个单独的 UITextField 元素组成,每个元素接受一个字符。我所做的是实现一个系统,每次 UITextField 的一个更改时,它都会验证是否所有值都已填写,如果它们更新了一个 bool 绑定(bind)以告诉父对象代码正确。

为了做到这一点,我将 @State 变量包装在一个自定义绑定(bind)中,该绑定(bind)在 setter 上执行回调,如下所示:

@State private var chars:[String] = ["","","",""]

...
var body: some View {
var bindings:[Binding<String>] = []

for x in 0..<self.chars.count {
let b = Binding<String>(get: {
return self.chars[x]
}, set: {
self.chars[x] = $0
self.validateCode()
})

bindings.append(b)
}

这些绑定(bind)被传递给组件。每次我的文本值更改时,都会调用 validateCode。这非常有效。

但是现在我想添加一个额外的行为:如果用户键入 4 个字符并且代码错误,我想将第一响应者移回第一个文本字段并清除其内容。第一响应者部分工作正常(我也使用 @State 变量管理它,但我不为那些使用绑定(bind)包装器),但是我无法更改代码中的文本。我认为这是因为我的组件使用了包装绑定(bind),而不是包含文本的变量。

这是我的 validateCode 的样子:

    func validateCode() {
let combinedCode = chars.reduce("") { (result, string) -> String in
return result + string
}

self.isValid = value == combinedCode

if !isValid && combinedCode.count == chars.count {

self.hasFocus = [true,false,false,false]
self.chars = ["","","",""]
}
}

hasFocus 正确地完成了它的工作,光标被移动到第一个 UITextField。然而,文本仍保留在文本字段中。我尝试在 init 中创建这些绑定(bind),因此我也可以在我的 validateCode 函数中使用它们,但这会产生各种编译错误,因为我使用的是 self。

知道如何解决这个问题吗?我应该使用 Observables 吗?我刚开始使用 SwiftUI,所以我可能缺少一些可用于此目的的工具。

为了完整性,这里是整个文件的代码:

import SwiftUI

struct CWCodeView: View {
var value:String
@Binding var isValid:Bool

@State private var chars:[String] = ["","","",""]

@State private var hasFocus = [true,false,false,false]
@State private var nothingHasFocus:Bool = false

init(value:String,isValid:Binding<Bool>) {
self.value = value
self._isValid = isValid

}

func validateCode() {
let combinedCode = chars.reduce("") { (result, string) -> String in
return result + string
}
self.isValid = value == combinedCode


if !isValid && combinedCode.count == chars.count {

self.hasFocus = [true,false,false,false]
self.nothingHasFocus = false
self.chars = ["","","",""]
}
}

var body: some View {
var bindings:[Binding<String>] = []

for x in 0..<self.chars.count {
let b = Binding<String>(get: {
return self.chars[x]
}, set: {
self.chars[x] = $0
self.validateCode()
})

bindings.append(b)
}


return GeometryReader { geometry in
ScrollView (.vertical){
VStack{
HStack {
CWNumberField(letter: bindings[0],hasFocus: self.$hasFocus[0], previousHasFocus: self.$nothingHasFocus, nextHasFocus: self.$hasFocus[1])
CWNumberField(letter: bindings[1],hasFocus: self.$hasFocus[1], previousHasFocus: self.$hasFocus[0], nextHasFocus: self.$hasFocus[2])
CWNumberField(letter: bindings[2],hasFocus: self.$hasFocus[2], previousHasFocus: self.$hasFocus[1], nextHasFocus: self.$hasFocus[3])
CWNumberField(letter: bindings[3],hasFocus: self.$hasFocus[3], previousHasFocus: self.$hasFocus[2], nextHasFocus: self.$nothingHasFocus)
}
}
.frame(width: geometry.size.width)
.frame(height: geometry.size.height)
.modifier(AdaptsToSoftwareKeyboard())
}
}
}
}

struct CWCodeView_Previews: PreviewProvider {
static var previews: some View {
CWCodeView(value: "1000", isValid: .constant(false))
}
}

struct CWNumberField : View {
@Binding var letter:String
@Binding var hasFocus:Bool
@Binding var previousHasFocus:Bool
@Binding var nextHasFocus:Bool

var body: some View {
CWSingleCharacterTextField(character:$letter,hasFocus: $hasFocus, previousHasFocus: $previousHasFocus, nextHasFocus: $nextHasFocus)
.frame(width: 46,height:56)
.keyboardType(.numberPad)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color.init("codeBorder"), lineWidth: 1)
)

}
}

struct CWSingleCharacterTextField : UIViewRepresentable {
@Binding var character: String
@Binding var hasFocus:Bool
@Binding var previousHasFocus:Bool
@Binding var nextHasFocus:Bool

func makeUIView(context: Context) -> UITextField {
let textField = UITextField.init()
//textField.isSecureTextEntry = true
textField.keyboardType = .numberPad
textField.delegate = context.coordinator
textField.textAlignment = .center
textField.font = UIFont.systemFont(ofSize: 16)
textField.tintColor = .black
textField.text = character

return textField
}

func updateUIView(_ uiView: UITextField, context: Context) {
if hasFocus {
DispatchQueue.main.async {
uiView.becomeFirstResponder()
}
}
}

func makeCoordinator() -> Coordinator {
return Coordinator(self)
}

class Coordinator : NSObject, UITextFieldDelegate {
var parent:CWSingleCharacterTextField

init(_ parent:CWSingleCharacterTextField) {
self.parent = parent
}

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let result = (textField.text! as NSString).replacingCharacters(in: range, with: string)

if result.count > 0 {
DispatchQueue.main.async{
self.parent.hasFocus = false
self.parent.nextHasFocus = true
}
} else {
DispatchQueue.main.async{
self.parent.hasFocus = false
self.parent.previousHasFocus = true
}
}

if result.count <= 1 {
parent.character = string
return true
}

return false
}
}
}

谢谢!

最佳答案

你只是犯了一个小错误,但我不敢相信你只是“开始”了 SwiftUI ;)

1.) 只构建一次文本域,所以我把它作为一个成员变量而不是总是构建一个新的2.) 更新 updateuiview 中的文本 -> 就是这样3.) ...几乎:仍然存在焦点/更新问题...四个文本字段中的最后一个无法正确更新...我认为这是焦点问题....

试试这个:

struct CWSingleCharacterTextField : UIViewRepresentable {
@Binding var character: String
@Binding var hasFocus:Bool
@Binding var previousHasFocus:Bool
@Binding var nextHasFocus:Bool

let textField = UITextField.init()

func makeUIView(context: Context) -> UITextField {
//textField.isSecureTextEntry = true
textField.keyboardType = .numberPad
textField.delegate = context.coordinator
textField.textAlignment = .center
textField.font = UIFont.systemFont(ofSize: 16)
textField.tintColor = .black
textField.text = character

return textField
}

func updateUIView(_ uiView: UITextField, context: Context) {

uiView.text = character

if hasFocus {
DispatchQueue.main.async {
uiView.becomeFirstResponder()
}
}
}

func makeCoordinator() -> Coordinator {
return Coordinator(self)
}

class Coordinator : NSObject, UITextFieldDelegate {
var parent:CWSingleCharacterTextField

init(_ parent:CWSingleCharacterTextField) {
self.parent = parent
}

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let result = (textField.text! as NSString).replacingCharacters(in: range, with: string)

if result.count > 0 {
DispatchQueue.main.async{
self.parent.hasFocus = false
self.parent.nextHasFocus = true
}
} else {
DispatchQueue.main.async{
self.parent.hasFocus = false
self.parent.previousHasFocus = true
}
}

if result.count <= 1 {
parent.character = string
return true
}

return false
}
}
}

关于swiftui - 在另一个绑定(bind)中包装 SwiftUI 绑定(bind)时无法更新值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60810012/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com