SwiftUI:是否可以在输入1个字符后自动移动到下一个文本字段?

人气:201 发布:2022-10-16 标签: swiftui textfield

问题描述

我试图制作一个SwiftUI应用程序,在TextField中输入一个字母后,光标会自动移动到下一个TextField。用户界面与this非常相似。

在SWIFT/IB中,这看起来是通过委托和添加一个目标完成的,如下所示: How to move to the next UITextField automatically in Swift

但找不到有关在SwiftUI中使用委托/目标的任何文档。

我试着关注这篇帖子: SwiftUI TextField max length 但这对我来说并不管用。设置.prefix(1)似乎没有什么不同。TextField仍然接受任意数量的字符,并且当移动到下一个TextField时,不会将输入的字符减少到只有第一个字符。

在SwiftUI的当前状态下,是否可以在输入1个字符后自动移动到下一个TextField

谢谢您的帮助!

推荐答案

在iOS 15中可以使用FocusState

import SwiftUI
///Sample usage
@available(iOS 15.0, *)
struct PinParentView: View {
    @State var pin: Int = 12356
    var body: some View {
        VStack{
            Text(pin.description)
            PinView(pin: $pin)
        }
    }
}
@available(iOS 15.0, *)
struct PinView: View {
    @Binding var pin: Int
    @State var pinDict: [UniqueCharacter] = []
    @FocusState private var focusedField: UniqueCharacter?
    var body: some View{
        HStack{
            ForEach($pinDict, id: .id, content: { $char in
                TextField("pin digit", text:
                            Binding(get: {
                    char.char.description
                }, set: { newValue in
                    let newest: Character = newValue.last  ?? "0"
                    //This check is only needed if you only want numbers
                    if Int(newest.description) != nil{
                        char.char = newest
                    }
                    //Set the new focus
                    DispatchQueue.main.async {
                        setFocus()
                    }
                })
                ).textFieldStyle(.roundedBorder)
                    .focused($focusedField, equals: char)
            })
            
        }.onAppear(perform: {
            //Set the initial value of the text fields
            //By using unique characters you can keep the order
            pinDict = pin.description.uniqueCharacters()
            
        })
    }
    func setFocus(){
        //Default to the first box when focus is not set or the user reaches the last box
        if focusedField == nil || focusedField == pinDict.last{
            focusedField = pinDict.first
        }else{
            //find the index of the current character
            let idx = pinDict.firstIndex(of: focusedField!)
            //Another safety check for the index
            if idx == nil || pinDict.last == pinDict[idx!]{
                focusedField = pinDict.first
            }else{
                focusedField = pinDict[idx! + 1]
            }
        }
        //Update the Binding that came from the parent
        setPinBinding()
    }
    ///Updates the binding from the parent
    func setPinBinding(){
        var newPinInt = 0
        for n in pinDict{
            if n == pinDict.first{
                newPinInt = Int(n.char.description) ?? 0
            }else{
                newPinInt = Int(String(newPinInt) + n.char.description) ?? 0
            }
        }
        pin = newPinInt
    }
}


//Convert String to Unique characers
extension String{
    func uniqueCharacters() -> [UniqueCharacter]{
        let array: [Character] = Array(self)
        return array.uniqueCharacters()
    }
    func numberOnly() -> String {
        self.trimmingCharacters(in: CharacterSet(charactersIn: "-0123456789.").inverted)
    }
    
}
extension Array where Element == Character {
    
    func uniqueCharacters() -> [UniqueCharacter]{
        var array: [UniqueCharacter] = []
        
        for char in self{
            array.append(UniqueCharacter(char: char))
        }
        return array
    }
    
}

//String/Characters can be repeating so yu have to make them a unique value
struct UniqueCharacter: Identifiable, Equatable, Hashable{
    var char: Character
    var id: UUID = UUID()
}

@available(iOS 15.0, *)
struct PinView_Previews: PreviewProvider {
    static var previews: some View {
        PinParentView()
    }
}

210