清除按钮按下swiftui时如何自动上一个文本字段

Posted

技术标签:

【中文标题】清除按钮按下swiftui时如何自动上一个文本字段【英文标题】:How to automatically previous textfield when clear button press swiftui 【发布时间】:2021-02-03 15:17:02 【问题描述】:

我正在尝试创建 OTP 屏幕,当用户在第一个文本字段中输入数字并且光标自动移动到下一个文本字段后,我得到了它的功能,但是当用户删除文本时我被卡住了当前文本字段以及如何自动移动到上一个文本字段。

屏幕截图:-

代码:-

import SwiftUI

struct VerficationCode: View 
@State private var numberOfCells: Int = 6
@State private var currentlySelectedCell = 0

var body: some View 
    HStack 
        Group 
            ForEach(0 ..< self.numberOfCells)  index in
                CharacterInputCell(currentlySelectedCell: self.$currentlySelectedCell, index: index)
            
        .frame(width:15,height: 56)
        .padding(.horizontal)
        .foregroundColor(.white)
        .cornerRadius(10)
        .keyboardType(.numberPad)
       
    
 

struct CharacterInputCell: View 
@State private var textValue: String = ""
@Binding var currentlySelectedCell: Int

var index: Int

var responder: Bool 
    return index == currentlySelectedCell


var body: some View 
    CustomTextField(text: $textValue, currentlySelectedCell: $currentlySelectedCell, isFirstResponder: responder)
    
 

   struct CustomTextField: UIViewRepresentable 

class Coordinator: NSObject, UITextFieldDelegate 
    
    @Binding var text: String
    @Binding var currentlySelectedCell: Int
    
    var didBecomeFirstResponder = false
    
    init(text: Binding<String>, currentlySelectedCell: Binding<Int>) 
        _text = text
        _currentlySelectedCell = currentlySelectedCell
    
    
    func textFieldDidChangeSelection(_ textField: UITextField) 
        DispatchQueue.main.async 
            self.text = textField.text ?? ""
        
    
    
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool 
        let currentText = textField.text ?? ""
        guard let stringRange = Range(range, in: currentText) else  return false 
        let updatedText = currentText.replacingCharacters(in: stringRange, with: string)
        if updatedText.count <= 1 
            self.currentlySelectedCell += 1
        
        return updatedText.count <= 1
    


@Binding var text: String
@Binding var currentlySelectedCell: Int
var isFirstResponder: Bool = false

func makeUIView(context: UIViewRepresentableContext<CustomTextField>) -> UITextField 
    let textField = UITextField(frame: .zero)
    textField.delegate = context.coordinator
    textField.textAlignment = .center
    textField.keyboardType = .decimalPad
    return textField


func makeCoordinator() -> CustomTextField.Coordinator 
    return Coordinator(text: $text, currentlySelectedCell: $currentlySelectedCell)


func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<CustomTextField>) 
    uiView.text = text
    if isFirstResponder && !context.coordinator.didBecomeFirstResponder  
        uiView.becomeFirstResponder()
        context.coordinator.didBecomeFirstResponder = true
        
     

有人可以向我解释一下如何在单击清除按钮时自动返回,我已经尝试实施但还没有结果。

任何帮助将不胜感激。

提前致谢。

【问题讨论】:

离题了,但是当当前字段被清除时,光标移动到上一个字段是不是有点反直觉? 【参考方案1】:

试试看

        import SwiftUI
    public struct PasscodeField: View 
        
        var maxDigits: Int = 6
        var label = "Enter One Time Password"
        
        @State var pin: String = ""
        @State var showPin = true
        //@State var isDisabled = false
        
        
        var handler: (String, (Bool) -> Void) -> Void
        
        public var body: some View 
            VStack
                Text(label).font(.title)
                ZStack 
                    pinDots
                    backgroundField
                
                showPinStack
            
            
        
        
        private var pinDots: some View 
            HStack 
                Spacer()
                ForEach(0..<maxDigits)  index in
                    Image(systemName: self.getImageName(at: index))
                        .font(.system(size: 60))
                    Spacer()
                .frame(minWidth: 0, maxWidth: .infinity)
                .padding(.trailing, -24)
            
        
        
        private var backgroundField: some View 
            let boundPin = Binding<String>(get:  self.pin , set:  newValue in
                self.pin = newValue
                self.submitPin()
            )
            
            return TextField("", text: boundPin, onCommit: submitPin)
          
          // Introspect library can used to make the textField become first resonder on appearing
          // if you decide to add the pod 'Introspect' and import it, comment #50 to #53 and uncomment #55 to #61
          
               .accentColor(.clear)
               .foregroundColor(.clear)
               .keyboardType(.numberPad)
               //.disabled(isDisabled)
          
    //             .introspectTextField  textField in
    //                 textField.tintColor = .clear
    //                 textField.textColor = .clear
    //                 textField.keyboardType = .numberPad
    //                 textField.becomeFirstResponder()
    //                 textField.isEnabled = !self.isDisabled
    //         
        
        
        private var showPinStack: some View 
            HStack 
                Spacer()
                if !pin.isEmpty 
                    showPinButton
                
            
            .frame(height: 100)
            .padding([.trailing])
        
        
        private var showPinButton: some View 
            Button(action: 
                self.showPin.toggle()
            , label: 
                self.showPin ?
                    Image(systemName: "eye.slash.fill").foregroundColor(.primary) :
                    Image(systemName: "eye.fill").foregroundColor(.primary)
            )
        
        
        private func submitPin() 
            //guard !pin.isEmpty else 
                //showPin = false
                //return
            //
            
            if pin.count == maxDigits 
                //isDisabled = true
                
                handler(pin)  isSuccess in
                    if isSuccess 
                        print("pin matched, go to next page, no action to perfrom here")
                     else 
                        pin = ""
                        //isDisabled = false
                        print("this has to called after showing toast why is the failure")
                    
                
            
            
            // this code is never reached under  normal circumstances. If the user pastes a text with count higher than the
            // max digits, we remove the additional characters and make a recursive call.
            if pin.count > maxDigits 
                pin = String(pin.prefix(maxDigits))
                submitPin()
            
        
        
        private func getImageName(at index: Int) -> String 
            if index >= self.pin.count 
                return "circle"
            
            
            if self.showPin 
                return self.pin.digits[index].numberString + ".circle"
            
            
            return "circle.fill"
        
    

    extension String 
        
        var digits: [Int] 
            var result = [Int]()
            
            for char in self 
                if let number = Int(String(char)) 
                    result.append(number)
                
            
            
            return result
        
        
    

    extension Int 
        
        var numberString: String 
            
            guard self < 10 else  return "0" 
            
            return String(self)
        
    

【讨论】:

谢谢您的回复,但我正在尝试在我的项目中实现您的代码,但是当我在文本字段中输入所有数字时我很困惑,然后如何创建所有文本字段的单个字符串文本。你能帮帮我吗? 你想只有一个文本字段吗? 不,我有 6 个文本字段,请看上面的截图 请看上面的屏幕截图,当我在所有文本字段中输入所有文本并自动调用 api 是否可能? 是的,有可能

以上是关于清除按钮按下swiftui时如何自动上一个文本字段的主要内容,如果未能解决你的问题,请参考以下文章

按下按钮时在 UITableView 单元格中清除 UITextField 的文本

只要按下键盘上的x按钮,如何从一个文本字段中删除值

如何从另一个视图swiftui中传递字段文本值

SwiftUI 清除文本字段 FirstResponder

清除和退出按钮

在 SwiftUI 中按下按钮时更改字符串的文本