SwiftUI Pin 输入的验证码字段

2021-07-08  本文已影响0人  Bepawbikvy
实现效果:
image.png
实现原理:

将一个TextTextFileld添加到ZStack相同的位置,TextFileldZ轴上的元素textField设置clear颜色

ZStack {
   pinDots
   backgroundField
}
// 
return TextField("", text: boundPin, onCommit: submitPin)
     .accentColor(.clear)
     .foregroundColor(.clear)
     .keyboardType(.numberPad)

与 数量相等的图像(maxDigits默认为 4)位于 a 内HStack。根据用户输入的字符数,我们显示圆形边框或实心圆圈。点击showPin看起来像眼睛的按钮,将显示用户输入的数字。

没有delegate得到变化的用户类型textField。因此,我们正在使用Binding<String>. 即使这整个PasscodeField是一种解决方法。如果 Apple 提供了符合TextFieldStyle 协议的正确方法,则可能不需要这些。ButtonStyleToggleStyle协议非常酷,让生活更轻松。

现在让我们回到代码。每次值改变时,我们调用一个方法并检查用户是否输入了最大位数。如果是,我们禁用textField并调用完成处理程序块。

private func submitPin() {
        if pin.count > maxDigits {
            // 如果超出长度就忽略本次输入
            pin = String(pin.prefix(maxDigits))
        } else if pin.count == maxDigits {
            // 验证
            handler(pin) { isSuccess in
                if isSuccess {
                    logger.debug("pin matched, go to next page, no action to perfrom here")
                } else {
                    pin = ""
                    logger.debug("this has to called after showing toast why is the failure")
                }
            }
        }
    }
如何调用:
CaptchaView(maxDigits: 6, pin: .constant("6"), showPin: true) { strng, reponsetrue in
        }

完整代码:
import SwiftUI

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

struct CaptchaView: View {
    var maxDigits: Int = 6
    @Binding var pin: String
    @State var showPin = true
    var handler: (String, (Bool) -> Void) -> Void
    
    var body: some View {
        VStack {
            ZStack {
                pinDots
                backgroundField
            }
        }
    }
    
    private var pinDots: some View {
        HStack {
            ForEach(0..<maxDigits) { index in
                Text(self.getNumber(at: index))
                    .font(.system(size: 24).weight(.bold))
                    .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 50, alignment: .center)
                    .padding(EdgeInsets(top: 7, leading: 0, bottom: 7, trailing: 0))
                    .border((index == self.pin.count) ? Color.OasisThemeColor : Color.OasisDisableColor, width: 0.5)
            }
        }
    }
    private func getNumber(at index: Int) -> String {
        
        if index == self.pin.count {
            return "_"
        }
        if index >= self.pin.count {
            return " "
        }
        if self.pin.count > maxDigits {
            self.showPin = false
        }
        if self.showPin {
            return self.pin.digits[index].numberString
        }
        return " "
    }
    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 func submitPin() {
        if pin.count > maxDigits {
            // 如果超出长度就忽略本次输入
            pin = String(pin.prefix(maxDigits))
        } else if pin.count == maxDigits {
            // 验证
            handler(pin) { isSuccess in
                if isSuccess {
                    logger.debug("pin matched, go to next page, no action to perfrom here")
                } else {
                    pin = ""
                    logger.debug("this has to called after showing toast why is the failure")
                  }
               }
            }
        }
}

参考:SwiftUI — Passcode field for OTP and Pin entry

上一篇下一篇

猜你喜欢

热点阅读