0

I hope you're all doing well. I am new to SwiftUI and I have been working on a form UI in SwiftUI. From a UI perspective, it looks correct, but I'm facing some issues. When I fill in some values in the views and click on the button, it doesn't seem to capture the inputted values. Additionally, I'm unable to get the inputted value on preference change either. I've been trying to solve this problem for the past week.

I would greatly appreciate it if someone with knowledge of SwiftUI could review the code and let me know if I'm on the right track or if there's a better way to approach this. Here's the code I've been working on.

Here is the code i have tried:

//
//  PlatFormDataViewModel.swift

import SwiftUI

class PlatFormDataVM: ObservableObject {
   @Published var objpdata: PlatFormData
   init(_ data: PlatFormData) {
     self.objpdata = data
   }
}
struct PlatFormData: Identifiable,Hashable,Equatable
{
        var dateSeen: Date
        var id: String
        var image: String
        var kindof: String
        var subtitle: String
        var platformName: String
        var descriptions: String
        var loginId: String
        var loginPassword: String
        var others: String
        var bankName: String
        var bankBranch: String
        var bankIfsc: String
        var acName: String
        var acNumber: String
        var bankCustId: String
        var bankPassword: String
        var bankTransId: String
        var bankTransPassword: String
        var bankPin: String
        var bankMpin: String
        var bankSecurityQueAns: String
        static func == (lhs: PlatFormData, rhs: PlatFormData) -> Bool {
            lhs.id == rhs.id
        }
}
struct PlatFormDataPreferenceKey: PreferenceKey {
    static var defaultValue: [String:String] = [:]

    static func reduce(value: inout [String : String], nextValue: () -> [String : String]) {
        value = nextValue()
    }
}
//
//  AddTypeView.swift


import SwiftUI

struct AddTypeView: View {
    @Binding var isPresented: Bool
    @ObservedObject var platformdataVM = PlatFormDataVM(commonUtils().PlatFormDataPlaceholder())
    @State var pname:String = ""
    @State var description :String = ""
    @State private var previewIndex1 = 0
    var body: some View {
        
        Form {
                Section
                {
                    Text("Add Platform")
                        .bold()
                        .font(.largeTitle)
                        .clipped()
                        .foregroundColor(.black).padding(-15)
                }
                .listRowBackground(Color.clear)
                Section(header: Text("Platform Name").font(.subheadline).foregroundColor(.gray).padding(-15)) {
                    TextField("Exa. Google", text: $pname).keyboardType(.alphabet)
                    .updatedPlatFormDataValue(inputval: ["platformname":pname])
                    TypeImageSelectionPickerView()
                        .onPreferenceChange(PlatFormDataPreferenceKey.self, perform: { value in
                            if value.count > 0
                            {
                                self.platformdataVM.objpdata.image = value.first!.value
                            }
                        })
                }
                .listRowBackground(Color.white)
                .onPreferenceChange(PlatFormDataPreferenceKey.self, perform: { value in
                    if value.count > 0
                    {
                        self.platformdataVM.objpdata.platformName = value.first!.value
                    }
                })
                Section(header: Text("Description").font(.subheadline).foregroundColor(.gray).padding(-15)) {
                    TextField("Enter Description Here", text: $platformdataVM.objpdata.descriptions).keyboardType(.asciiCapable)
                        .updatedPlatFormDataValue(inputval: ["description":description])
                }
                .listRowBackground(Color.white)
                .onPreferenceChange(PlatFormDataPreferenceKey.self, perform: { value in
                    if value.count > 0
                    {
                        self.platformdataVM.objpdata.descriptions = value.first!.value
                    }
                })
                Section(header: Text("Select Option").font(.subheadline).foregroundColor(.gray).padding(-15))
                {
                    TypeSelectionPickerView()
                        .onPreferenceChange(PlatFormDataPreferenceKey.self) { value in
                        if value.count > 0
                        {
                            switch value.first?.key.lowercased().replacingOccurrences(of: " ", with: "")
                            {
                                case "kindof":
                                    self.platformdataVM.objpdata.kindof = value.first!.value
                                break
                                case "loginid":
                                    self.platformdataVM.objpdata.loginId = value.first!.value
                                break
                                case "loginpassword":
                                    self.platformdataVM.objpdata.loginPassword = value.first!.value
                                break
                                case "a/cname":
                                    self.platformdataVM.objpdata.acName = value.first!.value
                                break
                            case "name":
                                self.platformdataVM.objpdata.bankName = value.first!.value
                                break
                            case "branch":
                                self.platformdataVM.objpdata.bankBranch = value.first!.value
                                break
                            case "ifsc":
                                self.platformdataVM.objpdata.bankIfsc = value.first!.value
                                break
                            case "a/cnumber":
                                self.platformdataVM.objpdata.acNumber = value.first!.value
                                break
                            case "customerid":
                                self.platformdataVM.objpdata.bankCustId = value.first!.value
                                break
                            case "password":
                                self.platformdataVM.objpdata.bankPassword = value.first!.value
                                break
                            case "transactionpassword":
                                self.platformdataVM.objpdata.bankTransPassword = value.first!.value
                                break
                            case "transactionid":
                                self.platformdataVM.objpdata.bankTransId = value.first!.value
                                break
                            case "pin":
                                self.platformdataVM.objpdata.bankPin = value.first!.value
                                break
                            case "mpin":
                                self.platformdataVM.objpdata.bankMpin = value.first!.value
                                break
                            case "securityquestions":
                                self.platformdataVM.objpdata.bankSecurityQueAns = value.first!.value
                                break
                            case "other":
                                self.platformdataVM.objpdata.others = value.first!.value
                                break
                                default:
                                break
                            }
                            print("updated object-----\(self.platformdataVM.objpdata)")
                        }
                    }
                }
                .listRowBackground(Color.clear)
                Section {
                    FormActionView(inputvalues: self.platformdataVM.objpdata, isPresented: isPresented)
                }
                
                .listRowBackground(Color.clear)
            }.scrollContentBackground(.hidden)
            //.background(Color.green.gradient.opacity(0.3))
    }
}

#Preview {
    AddTypeView(isPresented: .constant(true))
}

struct TypeImageSelectionPickerView:View
{
    @State private var defaulticon = "house.circle.fill"
    @State private var isOpen = false
    var body:some View
    {
        HStack
        {
            Button(action: {
                withAnimation {
                    isOpen.toggle()
                }
            }, label: {
                HStack {
                    Text("Platform Icon".uppercased()).font(.subheadline).foregroundColor(.gray)
                    Spacer()
                    Image(systemName: defaulticon).font(.title3)
                }
                .updatedPlatFormDataValue(inputval: ["icon":defaulticon])
            }).tint(.gray)
        }
        SFSymbolsPicker(isPresented: $isOpen, icon: $defaulticon, category: .filterdIcons, axis: .vertical, haptic: true)
    }
}

#Preview {
    TypeImageSelectionPickerView()
}

struct TypeSelectionPickerView: View
{
    @State private var previewIndex = 0
    @State private  var selctevalue: String = ""
    var previewOptions = ["Password Only", "Multiple Inputs"]
   
    var body: some View {
        Picker("Type Of Data", selection: $previewIndex) {
            ForEach(0 ..< previewOptions.count,id: \.self) {
                Text(self.previewOptions[$0])
                   
            }
        }
        .updatedPlatFormDataValue(inputval: ["kindof":"\(previewOptions[previewIndex].lowercased().replacingOccurrences(of: " ", with: ""))"])
        .listRowSeparator(.hidden)
        .listRowBackground( Rectangle().frame(height: 45) .foregroundColor(.white).opacity(0.9) .padding(.bottom, 12).padding(.bottom, -12).cornerRadius(12))
 
        if (previewIndex == 0)
        {
            
            MultiInputView(allKeys:  ["Login ID":"" ,"Login Password":""])
        }
        else
        {
            MultiInputView(allKeys: ["Name":"" ,"Branch":"","IFSC":"","A/C Name":"","A/C Number":"","Customer ID":"","Password":"","Transaction ID":"","Transaction Password":"","PIN":"","MPIN":"","Securtiy Questions":"","Other":""])
        }
    }
}

#Preview {
    TypeSelectionPickerView()
}

struct FormActionView:View
{
    @State var inputvalues: PlatFormData
    @State  var isPresented:Bool
    var body: some View
    {
        HStack {
            Button(action: {
                print(inputvalues)
            }) {
            HStack {
                Image(systemName: .bookicon)
                Text("Save")
            }.foregroundColor(Color.white)
            .fontWeight(.bold)
            .font(.title2)
            .frame(maxWidth: .infinity)
            }.buttonStyle(.bordered)
            .background(Gradient(colors: [.indigo, .purple]))
            .cornerRadius(10.0)
            .padding(-18)
            Spacer().frame(width:50).background(Color.clear)
            Button(action: {
                        isPresented = false
            }) {
            HStack {
                Image(systemName: .closeicon).resizable().frame(width: 15,height: 15)
                Text("Close")
            }.foregroundColor(Color.white).fontWeight(.bold).font(.title3)
            }.buttonStyle(.bordered)
            .background(.indigo)
            .cornerRadius(10.0)
            .padding(-18)
        }
    }
}

#Preview {
    FormActionView(inputvalues: commonUtils().PlatFormDataPlaceholder(), isPresented: true)
}

//
//  MultiInputView.swift


import SwiftUI
extension Binding where Value == String? {
    var optionalBind: Binding<String> {
        .init(
            get: {
                wrappedValue ?? ""
            }, set: {
                wrappedValue = $0
            }
        )
    }
}
struct MultiInputView: View
{
    @State var allKeys:[String:String]
    @State private var isSecured: Bool = true
    var body: some View {
      
        let bindingToggle = Binding<Bool>(
                    get: { self.isSecured },
                    set: {
                        self.isSecured = $0;
                    }
                )
//        ZStack {
            List {
                ForEach(allKeys.sorted(by: <), id: \.key) { key, value in
                    VStack
                    {
                        
                        Text(key)
                            .frame(maxWidth: .infinity, alignment: .leading)
                            .listRowBackground(Color.clear)
                            .foregroundColor(.gray)
                        HStack
                        {
                            if key.contains("Password")
                            {
                                SecureField("Enter \(key)", text: $allKeys[key].optionalBind ).padding(10).background(Color.gray.opacity(0.3)).cornerRadius(6)
                                    .updatedPlatFormDataValue(inputval: [key:$allKeys[key].optionalBind.wrappedValue])
                                Button {
                                    bindingToggle.wrappedValue.toggle()
                                }
                            label: {
                                Image(systemName: bindingToggle.wrappedValue == true ? .eyecircleiconslash : .eyecircleicon).foregroundColor(.black)
                                    .frame(width: 27,height: 27)
                            }.buttonStyle(.bordered).background(.gray.opacity(0.3)).cornerRadius(6)
                            }
                            else
                            {
                                TextField("Enter \(key)", text: $allKeys[key].optionalBind).padding(10).background(Color.gray.opacity(0.3)).cornerRadius(6)
                                    .updatedPlatFormDataValue(inputval: [key:$allKeys[key].optionalBind.wrappedValue])
                            }
                        }
                    }
                   
                }
            .listRowSeparator(.hidden)
            .scrollContentBackground(.hidden)
        }
        .padding(10)
        .background(.white).cornerRadius(12.0).padding(EdgeInsets(top: -5, leading: -18, bottom: -12, trailing: -18))
    }
    private func textFieldChanged(_ text: String) {
        print(text)
    }
}

#Preview {
    MultiInputView(allKeys: ["Name":"" ,"Branch":"","IFSC":"","A/C Name":"","A/C Number":"","Customer ID":"","Password":"","Transaction ID":"","Transaction Password":"","PIN":"","MPIN":"","Securtiy Questions":"","Other":""])
}

Thank you so much for your time and assistance. Your help is crucial for me to overcome this challenge in SwiftUI.

Below is the screens link for the reference -

Code output link

Priya M.
  • 1
  • 2
  • Welcome to SO. Rather than providing a link to lengthy code hosted on an external site, please provide a [mre] here. See [ask] for more details. – jnpdx Jul 22 '23 at 16:49
  • Is this really a [mre]? Has everything irrelevant from the question been removed? For example, does `PlatFormData` rely on all of those fields? Is all of the styling required? – jnpdx Jul 22 '23 at 17:23
  • 1
    MultiInputView will not work, instead of trying to be clever create two separate views for each use case and use proper State properties for each individual value that can be edited or make use of your view model. Using a dictionary isn’t a good solution and by looking at your code you’re not even close to making it work. – Joakim Danielson Jul 22 '23 at 19:56
  • Hi @JoakimDanielson, I had created separate and later i have added preference key handling i have seen that both are almost same so that i have combine it in one. Also can you see handling of preference key - is that okay ? I have created key for this struct PlatFormData. You have any other idea to do this instead of preference key? or any demo code link to achieve same ? – Priya M. Jul 24 '23 at 06:16

0 Answers0