Change color of lines depending on the focus text field
Asked Answered
R

1

2

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)
      }
}
Repertory answered 2/9, 2021 at 12:3 Comment(0)
G
1

I was working on the answer...

Here I updated the code.

View:

struct ContentView: View {
    
    @StateObject var viewModel = ViewModel()
    
    let textBoxWidth = UIScreen.main.bounds.width / 8
    let textBoxHeight = UIScreen.main.bounds.width / 8
    let spaceBetweenLines: CGFloat = 10
    let paddingOfBox: CGFloat = 1
    var textFieldOriginalWidth: CGFloat {
        (textBoxWidth*6)+(spaceBetweenLines*3)+((paddingOfBox*2)*3)
    }
    
    var body: some View {
        
        VStack {
            
            ZStack {
                
                HStack (spacing: spaceBetweenLines){
                    otpText(text: viewModel.otp1, isNextTyped: $viewModel.isNextTypedArr[0])
                    otpText(text: viewModel.otp2, isNextTyped: $viewModel.isNextTypedArr[1])
                    otpText(text: viewModel.otp3, isNextTyped: $viewModel.isNextTypedArr[2])
                    otpText(text: viewModel.otp4, isNextTyped: $viewModel.isNextTypedArr[3])
                    otpText(text: viewModel.otp5, isNextTyped: $viewModel.isNextTypedArr[4])
                    otpText(text: viewModel.otp6, isNextTyped: $viewModel.isNextTypedArr[5])
                }
                
                
                TextField("", text: $viewModel.otpField) { isEditing in
                    viewModel.isEditing = isEditing
                }
                .frame(width: viewModel.isEditing ? 0 : textFieldOriginalWidth, height: textBoxHeight)
                .textContentType(.oneTimeCode)
                .foregroundColor(.clear)
                .accentColor(.clear)
                .background(Color.clear)
                .keyboardType(.numberPad)
            }
            
        }
    }
    
    private func otpText(text: String, isNextTyped: Binding<Bool>) -> some View {
        
        return Text(text)
            .font(.title)
            .frame(width: textBoxWidth, height: textBoxHeight)
            .background(VStack{
                Spacer()
                RoundedRectangle(cornerRadius: 1)
                    .frame(height: 0.5)
                    .foregroundColor(isNextTyped.wrappedValue ? .green : .black)
            })
            .padding(paddingOfBox)
    }
}

ViewModel:

class ViewModel: ObservableObject {
    
    @Published var otpField = "" {
        didSet {
            isNextTypedArr = Array(repeating: false, count: 6)
            guard otpField.count <= 6,
                  otpField.last?.isNumber ?? true else {
                otpField = oldValue
                return
            }
            if otpField.count < 6 {
                isNextTypedArr[otpField.count] = true
            }
        }
    }
    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 isNextTypedArr = Array(repeating: false, count: 6)
    
    @Published var borderColor: Color = .black
    
    @Published var isEditing = false {
        didSet {
            isNextTypedArr = Array(repeating: false, count: 6)
            if isEditing && otpField.count < 6 {
                isNextTypedArr[otpField.count] = true
            }
        }
    }
}
Guan answered 2/9, 2021 at 12:9 Comment(6)
I'm just learning SwiftUI. May I ask where you learn SwiftUI?Repertory
I started with tutorials (still learning).I would recommend: Apple tutorials , Sean Allen, LetsBuildThatApp, HackingWithSwift. Thats a good start then I would challenge my self with different apps (pet apps).Guan
Thanks @Alhomaidhi. Can you also explain this line and it's used? TextField("", text: $viewModel.otpField) { isEditing in viewModel.isEditing = isEditing }Repertory
Sure, this line is initializing a TextField with a placeholder of ”” and a binding string of $viewModel.otpField And a closure that accepts a Bool as an argument the type would be (Bool)->() the Bool in this case would be isEditing which the system send as a flag. If the user editing = true if user is not = false.Guan
Thanks @Alhomaidhi, For the preview provider I noticed there isn't one and when I tried to add it won't let me. Do you have idea why is that?Repertory
Hi @Alhoumaidhi, I have another question maybe you can help me again. #69041402Repertory

© 2022 - 2024 — McMap. All rights reserved.