ios - SwiftUI 中的短信验证码设计和功能

标签 ios xcode swiftui sms-verification

我尝试用六个文本字段来实现它,但发现了很多问题,因为做了很多工作,除了第一个文本字段之外的所有初始输入都阻塞了,firstResponder 的延迟移动等等,是什么让我想知道拥有 6 个文本字段是否真的是最好的方法。
困难的部分是功能(即光标平滑移动,来回移动,输入错误时将它们全部变为红色等)我怎样才能实现这种行为/功能?
截屏:-

代码如下:-

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
        }
      }
  }
有人可以向我解释我如何实现这种行为/功能?,我已经尝试实现但还没有结果。
任何帮助将不胜感激。
提前致谢。

最佳答案

代码:-

import SwiftUI
public struct ContentView: View {

var maxDigits: Int = 6
var label = "Enter One Time Password"
@State var pin: String = ""
@State var showPin = true
var handler: (String, (Bool) -> Void) -> Void
public var body: some View {
    VStack {
        Text(label).font(.title)
        ZStack {
            pinDots
            backgroundField
        }
    }
}
private var pinDots: some View {
    HStack {
        Spacer()
        ForEach(0..<maxDigits) { index in
            Image(systemName: self.getImageName(at: index))
                .font(.system(size: 60, weight: .thin, design: .default))
            Spacer()
        }
    }
}

private func getImageName(at index: Int) -> String {
    if index >= self.pin.count {
        return "square"
    }
    if self.showPin {
        return self.pin.digits[index].numberString + ".square"
    }
    return "square"
}

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)
        .accentColor(.clear)
        .foregroundColor(.clear)
        .keyboardType(.numberPad)
}


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() {
    if pin.count == maxDigits {
        handler(pin) { isSuccess in
            if isSuccess {
                print("pin matched, go to next page, no action to perfrom here")
            } else {
                pin = ""
                print("this has to called after showing toast why is the failure")
              }
           }
        }
    }

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        //maxDigits =  Set According to your condition
        //label =  Set Title
        //Pin Count
        ContentView(maxDigits: 6, label: "A", pin: "6", showPin: true) { strng, reponsetrue in
            
        }
    }
}


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

关于ios - SwiftUI 中的短信验证码设计和功能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66203738/

相关文章:

swift - 如何在 WidgetKit 预览 SwiftUI iOS 14 中加载 WKWebView

iOS - 仅使 subview 可点击

ios - 在 native 应用程序中访问 package.json 版本和构建变量的最佳方法?

xcode - 当附件为UITableViewCellAccessoryCheckmark时如何在UITableViewCell中居中放置文本

xcode - 将 NSArray 转换为 NSString 会崩溃?

swift - 如何正确创建 SF 符号?

ios - 在 iOS IKEv2 VPN 配置中使用自定义端口?

ios - 无法访问 protected 成员 'Foundation.NSDictionary.NSDictionary(System.IntPtr)'

xcode - 有关如何为大型团队设置 Xcode 项目的指南?

ios - iOS SwiftUI中ForEach内部的if语句