实现效果:
实现原理:
将一个Text
和TextFileld
添加到ZStack
相同的位置,TextFileld
Z轴上的元素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
协议的正确方法,则可能不需要这些。ButtonStyle
,ToggleStyle
协议非常酷,让生活更轻松。
现在让我们回到代码。每次值改变时,我们调用一个方法并检查用户是否输入了最大位数。如果是,我们禁用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")
}
}
}
}
}