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 -