I am trying to create an OTP page that has dashed lines at the bottom of each digit. I used one text field for each digit and a total of 6 digits for 6 text fields are used. I want the next line to have it's color changed before the user enters an integer.
The line will change it's color and so forth, depending on which one is the active text field. Note that the active textfield should not have a different color. Only the next text field should. So if I key in the first digit, the 2nd one should already be green.
Then when I input a number in 2nd text field, the 3rd one should be green.
See screenshot.
I am using Xcode 12.3 only and I cannot use FocusState APIs and other APIs that are used in Xcode Beta.
import SwiftUI
class ViewModel: ObservableObject {
@Published var otpField = "" {
didSet {
guard otpField.count <= 6,
otpField.last?.isNumber ?? true else {
otpField = oldValue
return
}
}
}
var otp1: String {
guard otpField.count >= 1 else {
return ""
}
return String(Array(otpField)[0])
}
var otp2: String {
guard otpField.count >= 2 else {
return ""
}
return String(Array(otpField)[1])
}
var otp3: String {
guard otpField.count >= 3 else {
return ""
}
return String(Array(otpField)[2])
}
var otp4: String {
guard otpField.count >= 4 else {
return ""
}
return String(Array(otpField)[3])
}
var otp5: String {
guard otpField.count >= 5 else {
return ""
}
return String(Array(otpField)[4])
}
var otp6: String {
guard otpField.count >= 6 else {
return ""
}
return String(Array(otpField)[5])
}
@Published var borderColor: Color = .black
@Published var isTextFieldDisabled = false
var successCompletionHandler: (()->())?
}
struct ContentView: View {
@StateObject var viewModel = ViewModel()
@State var isFocused = false
let textBoxWidth = UIScreen.main.bounds.width / 8
let textBoxHeight = UIScreen.main.bounds.width / 8
let spaceBetweenBoxes: CGFloat = 10
let paddingOfBox: CGFloat = 1
var textFieldOriginalWidth: CGFloat {
(textBoxWidth*6)+(spaceBetweenBoxes*3)+((paddingOfBox*2)*3)
}
var body: some View {
VStack {
ZStack {
HStack (spacing: spaceBetweenBoxes){
otpText(text: viewModel.otp1)
otpText(text: viewModel.otp2)
otpText(text: viewModel.otp3)
otpText(text: viewModel.otp4)
otpText(text: viewModel.otp5)
otpText(text: viewModel.otp6)
}
TextField("", text: $viewModel.otpField)
.frame(width: isFocused ? 0 : textFieldOriginalWidth, height: textBoxHeight)
.disabled(viewModel.isTextFieldDisabled)
.textContentType(.oneTimeCode)
.foregroundColor(.clear)
.accentColor(.clear)
.background(Color.clear)
.keyboardType(.numberPad)
}
}
}
private func otpText(text: String) -> some View {
return Text(text)
.font(.title)
.frame(width: textBoxWidth, height: textBoxHeight)
.background(VStack{
Spacer()
RoundedRectangle(cornerRadius: 1)
.frame(height: 1)
.overlay(Capsule().stroke(Color.green))
})
.padding(paddingOfBox)
}
}