TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

Switching field focus 🙄

Forums > SwiftUI

Good afternoon.

Guys, tell me how it is possible to check for switching focus by blocks?

I wanted to make an enumeration array but alas 🤔. I picked something too much...

I used the work of @ygeras in the code.

struct TestModelField {
    var first: String
    var second: String
}

class CustomTextFieldViewModel: ObservableObject {

    @Published var customFields: [TestModelField] = []
    @Published var allSubmitPressed: [[Bool]] = []

    @Published var allSatisfy: [Bool] = []

    init() {
        self.addField()
    }

    private func addField() {
        for _ in 0...2 {
            self.customFields.append(TestModelField(first: "", second: ""))
            self.allSubmitPressed.append([false, false])
            self.allSatisfy.append(false)
        }
    }
}
enum NameFieldsTest: String, CaseIterable {
    case firstField = "First field"
    case secondField = "Second field"
}

struct TestTextFields: View {

    @ObservedObject var vm: CustomTextFieldViewModel = CustomTextFieldViewModel()
    @FocusState private var nameFields: NameFieldsTest?

    @State private var submitPressed = false

    var body: some View {
        VStack(spacing: 10) {
            VStack(spacing: 10) {
        // MARK:  For visual information
                HStack(spacing: 10) {
                    ForEach(vm.allSatisfy.indices, id: \.self) { index in
                        RoundedRectangle(cornerRadius: 7)
                            .fill(vm.allSatisfy[index] ? Color.green : Color.red)
                            .frame(width: 10, height: 10)
                    }
                }
                ForEach(vm.customFields.indices, id: \.self) { index in
                    VStack(spacing: 10) {
                        CustomTextField(text: $vm.customFields[index].first,
                                        focus: $nameFields,
                                        nameFieldFirst: .firstField,
                                        submitPressed: vm.allSubmitPressed[index][0])
                        CustomTextField(text: $vm.customFields[index].second,
                                        focus: $nameFields,
                                        nameFieldFirst: .secondField,
                                        submitPressed: vm.allSubmitPressed[index][1])
                    }
                    .padding(.vertical, 16)
                    .border(Color.black)
                }
            }
            .padding(.vertical, 10)
            .background(
                RoundedRectangle(cornerRadius: 10)
                    .fill(Color.gray).opacity(0.2)
            )
            Button(action: {
                Task.init {
                    await self.showResult()
                }
            }) {
                Text("Answer")
                    .font(.system(size: 16))
                    .foregroundColor(.black)
            }
            .padding(.horizontal, 16)
            .padding(.vertical, 7)
            .background(.gray)
            .cornerRadius(12)
         // MARK:  For visual information
            ForEach(vm.allSubmitPressed.indices, id: \.self) { vert in
                HStack() {
                    ForEach(vm.allSubmitPressed[vert].indices, id: \.self) { horiz in
                        CheckCircle(isCheck: vm.allSubmitPressed[vert][horiz])
                    }
                }
            }
        }
    }

    private func isCheck(_ index: Int)  {
        let mirrorStruct = Mirror(reflecting: vm.customFields[index])
        var count = 0
        for field in mirrorStruct.children  {
            guard let currentField = field.value as? String else {
                return
            }
            if !currentField.isEmpty {
                vm.allSubmitPressed[index][count] = true
            } else {
                vm.allSubmitPressed[index][count] = false
            }
            count += 1
        }
        count = 0
    }

    private func allSatisfy() {
        for (index, _) in vm.allSubmitPressed.enumerated() {
            if vm.allSubmitPressed[index].allSatisfy({
                if $0 {
                    vm.allSatisfy[index] = true
                } else {
                    vm.allSatisfy[index] = false
                }
                return $0
            }) {}
        }
        print(vm.allSatisfy)
    }

    private func showResult() async {
        for (index, _) in vm.customFields.enumerated() {
            let mirrorStruct = Mirror(reflecting: vm.customFields[index])
            var count = 0
            for field in mirrorStruct.children  {
                guard let currentField = field.label else {
                    return
                }
                if currentField == "first" {
                    nameFields = .firstField
                } else if currentField == "second" {
                    nameFields = .secondField
                } else {
                    nameFields = nil
                }
                count += 1
            }
            count = 0
            self.isCheck(index)
        }
        self.allSatisfy()
    }
}

struct CustomTextField: View {

    @Binding var text: String
    @FocusState.Binding var focus: NameFieldsTest?
    var nameFieldFirst: NameFieldsTest
    var submitPressed: Bool

    var body: some View {
        HStack(spacing: 5) {
            TextField("", text: $text)
                .foregroundColor(.black)
                .tint(.black)
                .focused($focus, equals: nameFieldFirst)
        }
        .padding(.horizontal, 16)
        .padding(.vertical, 16)
        .background(
            RoundedRectangle(cornerRadius: 7)
                .fill(text.isEmpty && submitPressed
                      ? Color.red.opacity(0.4)
                      : Color.gray.opacity(0.4))
        )
        .cornerRadius(12)
    }
}
struct CheckCircle: View {

    var isCheck: Bool

    var body: some View {
        Circle()
            .fill(isCheck ? Color.green : Color.red)
            .frame(width: 6, height: 6)
    }
}
#if DEBUG
struct TestTextFields_Previews: PreviewProvider {
    static var previews: some View {
        TestTextFields()
    }
}
#endif

3      

I still figured out how to switch focus. Which does not depend on the number of fields and the number of blocks.

All that remains is to somehow bring this disgrace into a readable form and connect the masks to the number, date and email.

I'd like to figure out how to jump over fields 🙄

Video

import Foundation

struct TestModelField {
    var first: String
    var second: String
}

enum CheckField: String, CaseIterable {
    case activeField = "Active field"
    case inactiveField = "Inactive field"

}

struct FieldsSave {
    var text: String
    var submit: Bool
    var checkField: CheckField
}

class CustomTextFieldViewModel: ObservableObject {

    @Published var customFields: [TestModelField] = []
    @Published var arrayCheckField: [[FieldsSave]] = []
    @Published var count = 0

    init() {
        self.addField() { result in
            if result {
                self.calculate()
                self.countElem()
            }
        }
    }

    private func addField(returnResult: (Bool) -> Void) {
        for _ in 0...1 {
            self.customFields.append(TestModelField(first: "", second: ""))
        }
        returnResult(true)
    }

    private func calculate() {
        for index in 0...1 {
            let mirrorStruct = Mirror(reflecting: self.customFields[index])
            var tempArray: [FieldsSave] = []
            var count = 0
            for _ in mirrorStruct.children  {
                if index == 0 && count == 0 {
                    tempArray.append(FieldsSave(text: "",
                                                submit: false,
                                                checkField: .activeField))
                } else {
                    tempArray.append(FieldsSave(text: "",
                                                submit: false,
                                                checkField: .inactiveField))
                }
                count += 1
            }
            self.arrayCheckField.append(tempArray)
        }
    }

    private func countElem() {
        self.count = self.arrayCheckField.flatMap { $0 }.count
    }
}
struct TestTextFields: View {

    @ObservedObject var vm: CustomTextFieldViewModel = CustomTextFieldViewModel()

    @State var submit: SubmitLabel = .continue

    var body: some View {
        ZStack {
            Color.white
            VStack(spacing: 10) {
                VStack(spacing: 10) {
                    ForEach(vm.arrayCheckField.indices, id: \.self) { index in
                        VStack(spacing: 0) {
                            createBlockField(index)
                        }
                        .frame(maxWidth: .infinity)
                        .background(
                            RoundedRectangle(cornerRadius: 10)
                                .fill(Color.gray).opacity(0.2)
                        )
                    }
                    .onSubmit {
                        check()
                    }
                }
            }
        }
    }

    private func check() {
        var isBreak: Bool = false
        outerLoop: for (indexBlock, _) in vm.arrayCheckField.enumerated() {
            for (indexElement, _) in vm.arrayCheckField[indexBlock].enumerated() {
                if vm.arrayCheckField[indexBlock][indexElement].text.isEmpty {
                    vm.arrayCheckField[indexBlock][indexElement].checkField = .activeField
                    isBreak = true
                } else {
                    vm.arrayCheckField[indexBlock][indexElement].checkField = .inactiveField
                    vm.arrayCheckField[indexBlock][indexElement].submit = true
                }
                if isBreak {
                    break outerLoop
                }
            }
        }
        countElement()
        if !isBreak {
            print("Next view.")
        }
    }

    private func countElement() {
        var count = 0
        vm.arrayCheckField.map { elem in
            elem.map { field in
                if field.submit {
                    count += 1
                }
            }
        }
        if count == vm.count - 1 {
            self.submit = .done
        }
    }

    @ViewBuilder
    private func createBlockField(_ block: Int) -> some View {
        VStack(spacing: 10) {
            ForEach(vm.arrayCheckField[block].indices, id: \.self) { index in
                let currentElement = vm.arrayCheckField[block][index]
                VStack(spacing: 0) {
                    CustomTextField(text: $vm.arrayCheckField[block][index].text,
                                    nameField: $vm.arrayCheckField[block][index].checkField,
                                    submit: submit
                    )
                    HStack(spacing: 16) {
                        HStack(spacing: 10) {
                            Circle()
                                .fill(currentElement.submit ? Color.green : Color.red)
                                .frame(width: 10, height: 10)
                            Circle()
                                .fill(checkColor(currentElement.checkField))
                                .frame(width: 10, height: 10)
                        }
                        Text("\(currentElement.checkField.rawValue)")
                            .font(.system(size: 8))
                        Spacer()
                    }
                    .padding(.horizontal, 16)
                }
                if index == 0 {
                    RoundedRectangle(cornerRadius: 1)
                        .fill(Color.gray)
                        .frame(maxWidth: .infinity)
                        .frame(height: 2)
                        .padding(.horizontal, 16)
                }
            }
        }
        .padding(.horizontal, 10)
        .padding(.vertical, 10)
    }

    private func checkColor(_ active: CheckField) -> Color {
        switch active {
        case .activeField:
            return Color.green
        case .inactiveField:
            return Color.red
        }
    }
}
struct CustomTextField: View {

    @Binding var text: String
    @Binding var nameField: CheckField
    @FocusState private var nameFieldTest: Bool

    var submit: SubmitLabel

    var body: some View {
        HStack(spacing: 5) {
            VStack(spacing: 0) {
                TextField("", text: $text)
                    .focused($nameFieldTest)
                    .foregroundColor(.black)
                    .tint(.black)
                    .border(Color.black)
                    .submitLabel(submit)
            }
        }
        .padding(.horizontal, 16)
        .padding(.vertical, 16)
        .cornerRadius(12)
        .onChange(of: $nameField.wrappedValue) { _ in
            checkFocus()
        }
    }

    func checkFocus() {
        switch nameField {
        case .activeField:
            self.nameFieldTest = true
        case .inactiveField:
            self.nameFieldTest = false
        }
    }
}
#if DEBUG
struct TestTextFields_Previews: PreviewProvider {
    static var previews: some View {
        TestTextFields()
    }
}
#endif

3      

Solved a problem. wrapped in

DispatchQueue.main.async {
    check()
}

3      

Hacking with Swift is sponsored by Blaze.

SPONSORED Still waiting on your CI build? Speed it up ~3x with Blaze - change one line, pay less, keep your existing GitHub workflows. First 25 HWS readers to use code HACKING at checkout get 50% off the first year. Try it now for free!

Reserve your spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.