TextField Copies all Fields the same in SwiftUI - swiftui

When I type some text in one of the text fields, somehow it adds to all the text fields the same text at the same time. I am trying to separate them from each other so I can assign them correctly.
struct CardInfo : View {
#State var creditCard : CreditCard
#State var isSaved: Bool = false
#State private(set) var text = ""
var body: some View {
VStack {
CustomTextField(data: $text, tFtext: "Kartin Uzerindeki Isim", tFImage: "user")
.textContentType(.givenName)
.onReceive(Just(text)) { data in
self.creditCard.cardOwnerName = self.text
}
CustomTextField(data: $text, tFtext: "Kredi Kart Numarasi", tFImage: "credit")
.textContentType(.oneTimeCode)
.keyboardType(.numberPad)
.onReceive(Just(text)) { data in
self.creditCard.cardNumber = self.text
}
struct CustomTextField: View {
#Binding var data : String
var tFtext: String = ""
var tFImage: String = ""
var body: some View {
HStack {
Image(tFImage)
.resizable()
.frame(width: 20, height: 20)
.padding()
TextField(tFtext, text: $data)
.padding()
.font(Font.custom("SFCompactDisplay", size: 16))
.foregroundColor(.black)
}
.background(RoundedRectangle(cornerRadius: 10))
.foregroundColor(Color(#colorLiteral(red: 0.9647058824, green: 0.9725490196, blue: 0.9882352941, alpha: 1)))
}
}

You need to use separate #State variables for each TextField:
struct CardInfo : View {
#State var creditCard : CreditCard
#State var isSaved: Bool = false
#State private(set) var cardOwnerName = ""
#State private(set) var cardNumber = ""
var body: some View {
VStack {
CustomTextField(data: $cardOwnerName, tFtext: "Kartin Uzerindeki Isim", tFImage: "user")
.textContentType(.givenName)
.onReceive(Just(cardOwnerName)) { data in
self.creditCard.cardOwnerName = data
}
CustomTextField(data: $cardNumber, tFtext: "Kredi Kart Numarasi", tFImage: "credit")
.textContentType(.oneTimeCode)
.keyboardType(.numberPad)
.onReceive(Just(cardNumber)) { data in
self.creditCard.cardNumber = data
}
...
}
}
}

Related

Can't transfer variable from one watchos view to another view, Using swiftui

I am trying to get data from one view to another.
I can not figure out how to get values from the fourth view array into the Third view.
I am not using storyboards. I tried using #EnvironmentObject but can not make it work. New to coding. In xcode I am using watchos without app.
I tried to strip out most of the code and leave just the important stuff that can be tested. I used NavigationLink(destination: )to transfer between views.
enter code here
class viewFromEveryWhere: ObservableObject {
#Published var testname2: String = "testTTname"
}
struct secondView: View {
var body: some View {
Text("second view")
List(1..<7) {
Text("\($0)")
}
}
}
struct thirdView: View {
#EnvironmentObject var testname2: viewFromEveryWhere
#EnvironmentObject var testSixTestArray: viewFromEveryWhere
#State var sixTestArray:[String] = ["ww","GS","DW","CD","TS","JW",]
var body: some View {
List(sixTestArray, id:\.self) {
Text($0)
}
}
}
struct fourthView: View {
#StateObject var testname2 = viewFromEveryWhere()
#State private var name: String = ""
#State var testSixTestArray:[String] = []
func collectName () {
print("collectName triggered")
if testSixTestArray.count < 5 {
// testSixTestArray.append(name)
print(name)
print(testSixTestArray)
}
// .enviromentObject(testSixTestArray)
}
var body: some View {
VStack(alignment: . leading) {
Text("Type a name")
TextField("Enter your name", text: $name)
Text("Click to add, \(name)!")
// Button("click this if \(name) is correct") {}
Button(action:{
print("Button Tapped")
collectName()
print(testSixTestArray.count)
name = ""
}) {
Text("Add \(name) to list")
}
// .buttonStyle(GrowingButton1())
}
Text("forth view")
// testSixTestArray.append(name)
.environmentObject(testname2)
}
}
/*func presentTextInputControllerWithSuggestions(forLanguage suggestionsHandler:
((String)-> [Any]?)?,
allowedInputMode inputMode:
WKTextInputMode,
completion: #escaping ([Any]?) -> Void) {}
*/
struct ContentView: View {
#State var sixNameArray:[String] = ["","","","","","",]
#State var messageTextBox: String = "Start"
#State var button1: String = "Button 1"
#State var button2: String = "Button 2"
#State var button3: String = "Button 3"
var body: some View {
NavigationView {
VStack{
Text(messageTextBox)
.frame(width: 120, height: 15, alignment: .center)
.truncationMode(.tail)
.padding()
NavigationLink(destination: secondView(),
label:{
Text(button1)
})
.navigationBarTitle("Main Page")
NavigationLink(destination: thirdView(),
label:{
Text(button2)
})
NavigationLink(destination: fourthView(),
label:{
Text(button3)
})
}
}
}
}
enter code here

SwiftUI- passing #State variables to multiple views trouble

Im trying to make an app similar to iphones reminders app in terms of UI. I have a view where I have a list that I can add and delete items from, I also have a view for adding an item that allows me to name the item and select some options, and I have another view for when an item in the list is selected and I want to be able to show the name and options I made but it doesn't display. The code for the list view
struct DotView: View {
enum ActiveSheet: String, Identifiable {
case SwiftUIView, EditView
var id: String {
return self.rawValue
}
}
#EnvironmentObject var listViewModel: ListViewModel
#AppStorage("dotApiKey") var selectedDotApi: String = ""
#State var dotName:String = ""
#State var dotNumber:String = ""
#State var selection:String = ""
#State var triggerSelection:String = ""
#State var searchText:String = ""
#State var plugUsername:String = ""
#State var plugPassword:String = ""
#State var toggleNotification:Bool
#State var activeSheet : ActiveSheet? = nil
#Binding var show : Bool
var body: some View {
ZStack{
HStack {
EditButton()
.padding(.leading)
Spacer()
Button(action: {
self.activeSheet = .SwiftUIView
}, label: {
Text("Add")
})
.padding(.trailing)
}
ZStack {
List {
ForEach(listViewModel.dotitems, id:\.dotId){ dotitem in
Button(action: { self.activeSheet = .EditView }, label: {
DotItemListView(dotitem: dotitem)
})
}
.onDelete(perform: listViewModel.deleteItem)
.onMove(perform: listViewModel.moveItem)
.listRowBackground(Color("textBG"))
}.listStyle(PlainListStyle())
.background(Color("textBG"))
.frame(height: 300)
.cornerRadius(10)
.padding(.horizontal)
}
}
.sheet(item: $activeSheet){ sheet in
switch sheet {
case .SwiftUIView:
SwiftUIView(dotName: dotName, dotNumber: dotNumber, selection: selection, triggerSelection: triggerSelection, searchText: searchText, plugUsername: plugUsername, plugPassword: plugPassword, show: $show)
case .EditView:
EditView(show:$show)
}
}
When I add an item to the list it shows this in each row
struct DotItemListView:View {
let dotitem: DotItem
var body: some View{
HStack {
Text(dotitem.dotName)
Spacer()
Text(dotitem.selection)
Spacer()
Text(dotitem.dotNumber)
Spacer()
}
}
}
This is how I'm adding each item to the list
struct DotItem:Equatable, Codable{
var dotId = UUID().uuidString
let dotName:String
let dotNumber:String
let selection:String
}
class ListViewModel: ObservableObject {
#Published var dotitems: [DotItem] = [] {
didSet {
saveItem()
}
}
let dotitemsKey: String = "dotitems_list"
init() {
getDotItems()
}
func getDotItems() {
guard
let data = UserDefaults.standard.data(forKey: dotitemsKey),
let savedDotItems = try? JSONDecoder().decode([DotItem].self, from: data)
else { return }
self.dotitems = savedDotItems
}
func deleteItem(indexSet: IndexSet){
dotitems.remove(atOffsets: indexSet)
}
func moveItem(from: IndexSet, to: Int){
dotitems.move(fromOffsets: from, toOffset: to)
}
func addItem(dotName: String, dotNumber: String, selection: String){
let newItem = DotItem(dotName: dotName, dotNumber: dotNumber, selection: selection)
dotitems.append(newItem)
print(newItem)
}
func saveItem() {
if let encodedData = try? JSONEncoder().encode(dotitems) {
UserDefaults.standard.set(encodedData, forKey: dotitemsKey)
}
}
}
This is the view that I'm entering the data for each item
struct SwiftUIView: View {
#Environment(\.presentationMode) var presentationMode
#EnvironmentObject var listViewModel: ListViewModel
#AppStorage("dotApiKey") var selectedDotApi: String = ""
#State var dotName:String
#State var dotNumber:String
#State var selection:String
#State var triggerSelection:String
#State var selectedColor = Color.black
#State var searchText:String
#State var plugUsername:String
#State var plugPassword:String
#ObservedObject var vm = getDeviceNames()
#State var triggerDot:Bool = false
#State var toggleOnOff:Bool = false
#State var toggleLightColor:Bool = false
#State var isSearching:Bool = false
#StateObject var camera = CameraModel()
#Binding var show : Bool
var body: some View {
NavigationView {
Form {
Section(header: Text("Info")) {
TextField("Name", text: $dotName)
TextField("Number", text: $dotNumber)
Picker(selection: $selection, label: Text("Discover Plug")) {
ForEach(vm.dataSet, id:\.self) { item in
Text(item.Device).tag(item.Device)
}
}
Toggle(isOn: $isSearching, label: {
Text("Have smart plugs?")
})
if isSearching {
HStack{
Text("Casa")
Spacer()
Button(action: {sendPlugDict()}, label: {
Text("Login")
})
}
TextField("Username", text: $plugUsername)
TextField("Password", text: $plugPassword)
}
}
Section {
Toggle(isOn: $toggleOnOff, label: {
Text("On/Off")
})
}
Section {
Toggle(isOn: $toggleLightColor, label: {
Text("Light Color")
})
if toggleLightColor {
ColorPicker("Choose Light Color", selection: $selectedColor)
}
}
Section {
if listViewModel.dotitems.isEmpty == false {
Toggle(isOn: $triggerDot, label: {
Text("Add a DOT to trigger")
})
if triggerDot {
Picker(selection: $triggerSelection, label: Text("Select DOT")) {
ForEach(listViewModel.dotitems, id:\.dotId){ dotitem in
DotItemListView(dotitem: dotitem)
}
}
}
}
}
}
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
}
and this is the view that I'm trying to show the data when any list item is selected which is basically the same as above except in a few places
struct EditView: View {
#Environment(\.presentationMode) var presentationMode
#EnvironmentObject var listViewModel: ListViewModel
#ObservedObject var vm = getDeviceNames()
#State var dataSet = [Result]()
#State var dotName:String = ""
#State var dotNumber:String = ""
#State var selection:String = ""
#State var triggerSelection:String = ""
#State var selectedColor = Color.black
#State var searchText:String = ""
#State var plugUsername:String = ""
#State var plugPassword:String = ""
#State var triggerDot:Bool = false
#State var toggleOnOff:Bool = false
#State var toggleLightColor:Bool = false
#State var isSearching:Bool = false
#Binding var show : Bool
var body: some View {
NavigationView {
Form {
Section(header: Text("Info")) {
TextField(dotName, text: $dotName)
TextField(dotNumber, text: $dotNumber)
Picker(selection: $selection, label: Text("Discorver Plug")) {
ForEach(dataSet, id:\.self) { item in
Text(item.Device).tag(item.Device)
}
}
Toggle(isOn: $isSearching, label: {
Text("Have smart plugs?")
})
if isSearching {
HStack{
Text("Casa")
Spacer()
Button(action: {
SwiftUIView(dotName: dotName, dotNumber: dotNumber, selection: selection, triggerSelection: triggerSelection, searchText: searchText, plugUsername: plugUsername, plugPassword: plugPassword, show: $show).sendPlugDict()}, label: {
Text("Login")
})
}
TextField("Username", text: $plugUsername)
TextField("Password", text: $plugPassword)
}
}
Section {
Toggle(isOn: $toggleOnOff, label: {
Text("On/Off")
})
}
Section {
Toggle(isOn: $toggleLightColor, label: {
Text("Light Color")
})
if toggleLightColor {
ColorPicker("Choose Light Color", selection: $selectedColor)
}
}
Section {
if listViewModel.dotitems.isEmpty == false {
Toggle(isOn: $triggerDot, label: {
Text("Add a DOT to trigger")
})
if triggerDot {
Picker(selection: $triggerSelection, label: Text("Select DOT")) {
ForEach(listViewModel.dotitems, id:\.dotId){ dotitem in
DotItemListView(dotitem: dotitem)
}
}
}
}
}
}
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
}
I know this is a lot to go through but any help would be greatly appreciated
use #Binding on the view that you want to pass state to .
View1 :
#State var a = true
View2(a: $a)
View2 :
#Binding var a : Bool
for passing data globally use #EnvoirmentObject :
example :
#main
struct YourApp: App {
#StateObject var session = Session()
var body: some Scene {
WindowGroup {
SplashView()
.environmentObject(session)
}
}
}
Session :
class Session: ObservableObject {
/// user signin state
#Published var isSignedIn : Bool = false
}
View1 ,View2 , View3 .... :
struct SplashView: View {
#EnvironmentObject var session : Session
var body: some View {
VStack{
SignInView()
.opacity(session.isSignedIn ? 0:1)
}
.background(Color.background.ignoresSafeArea())
}
}

How to observe updates in binded values SwiftUI

I'm not sure if I created my custom TextField properly, because I am unable to observe the value changes to an #Binded text. Running the following code, you may observe that print(text) is not executed when you manually enter text into the text field.
import SwiftUI
#main
struct TestOutWeirdTextFieldApp: App {
#State var text: String = "" {
didSet {
print(text)
}
}
var body: some Scene {
WindowGroup {
StandardTextField(placeholderText: "Enter text", defaultText: $text)
}
}
}
struct StandardTextField: View {
#State var placeholderText: String {
didSet {
print(#line)
print(placeholderText)
}
}
#Binding var defaultText: String {
didSet {
print(#line)
print(defaultText)
}
}
#State var systemImage: String?
#State var underlineColor: Color = .accentColor
#State var edges: Edge.Set = .all
#State var length: CGFloat? = nil
#State var secure: Bool = false
var body: some View {
HStack {
if secure {
SecureField(placeholderText, text: $defaultText)
.foregroundColor(underlineColor)
} else {
TextField(placeholderText, text: $defaultText)
.foregroundColor(underlineColor)
}
if let systemImage = systemImage {
Image(systemName: systemImage)
.foregroundColor(.white)
}
}
.overlay(
Rectangle()
.frame(height: 2)
.padding(.top, 35)
)
.foregroundColor(underlineColor)
.padding(edges, length)
}
}
struct StandardTextView_Previews: PreviewProvider {
static var previews: some View {
StandardTextField(placeholderText: "Placement text", defaultText: .constant("")).previewLayout(.sizeThatFits)
}
}
Instead of didSet you need to use .onChange(of: modifier, like
HStack {
// ... your code here
}
.onChange(of: defaultText) { print($0) } // << this !!
.overlay(

SwiftUI different actions on the same Custom Alert actions

I created a custom alert. I want the product to be added to the basket when the Ok button on Alert is clicked on the first screen. When the Ok button is pressed on the second screen, the purchase of the product is requested. I called the same alert on 2 pages and I want it to take different actions. I couldn't do that with #Escaping.
AlertView
struct AlertView: View {
#Binding var openShowAlert: Bool
#State var closeShowAlert: Bool = false
#State var openState: CGFloat = -UIScreen.main.bounds.height
#State var closeState: CGFloat = UIScreen.main.bounds.height
var title: String = ""
var message: String = ""
var okButtonText: String = ""
var cancelButtonText: String = ""
var body: some View {
VStack {
Text(title)
.michromaFont(size: 20)
.padding(.top)
Spacer()
Text(message)
.michromaFont(size: 18)
Spacer()
HStack {
Button(action: {
self.openShowAlert = false
openState = -UIScreen.main.bounds.height
closeState = UIScreen.main.bounds.height
}) {
Text(cancelButtonText)
.foregroundColor(.red)
}
Spacer()
Button(action: {}) {
Text(okButtonText)
}
}
.michromaFont(size: 18)
.padding([.horizontal, .bottom])
}
.neumorphisimBackground(width: 300, height: 200)
.offset(y: self.openShowAlert ? self.openState : self.closeState)
.animation(.easeInOut)
.onChange(of: self.openShowAlert, perform: { value in
if value {
self.openState = .zero
}
})
}
}
DetailView
On this screen, click Alert presentation to add the product to the cart.
struct DetailView: View {
#Environment(\.presentationMode) var presentationMode
var device = UIDevice.current.userInterfaceIdiom
#State var width: CGFloat = 300
#State var height: CGFloat = 450
#Binding var text: String
#State var showAlert: Bool = false
var body: some View {
ZStack() {
......
AlertView(openShowAlert: self.$showAlert)
}
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
}
}
CartView Click I am providing an alert on this screen to purchase the product.
struct CartView: View {
#State var cartList = [1,2,3,4,5,6,7,8,9]
#Environment(\.presentationMode) var presentationMode
#State var showAlert: Bool = false
var body: some View {
ZStack(alignment: .top) {
.....
AlertView(openShowAlert: self.$showAlert)
}
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
}
}
How can I send two different actions in the same alert.
Hmm, I don't see why it shouldn't work with a closure. Have you tried passing over a closure like so?
struct AlertView: View {
...
var okButtonAction: () -> ()
var body: some View {
...
Button(action: okButtonAction) {
Text(okButtonText)
}
}
}
Usage
AlertView(openShowAlert: self.$showAlert) {
// Your custom code
}
Alternative Idea
You could work with Combine and create a publisher with a specific key to identify the sender screen. Then you can put your custom code inside .onReceive().

How do I show a SwiftUI overlay programmatically?

I have a form the User completes which includes an image upload, as this takes a little time to upload to the server I want to show a progress view of the upload.
I have created the view and connected it to the upload progress data which all works fine, and I can have it as a permanent overlay, but I would like to overlay it only when the upload button is pressed.
How do I do that?
my progress view
struct ProgressIndicator: View {
#State var imageUploadAmount = 0.0
var progress: Progress
var body: some View {
VStack {
ProgressView("Uploading…", value: imageUploadAmount, total: 1)
}.onReceive(progress.$upload, perform: { _ in
imageUploadAmount = progress.upload
})
}
}
the form view
struct AddAClimb: View {
#EnvironmentObject var screenCoordinator: ScreenCoordinator
#ObservedObject var progress = Progress()
#State private var showingImagePicker = false
#State private var selectedTypeIndex: Int = 0
#State private var name = ""
#State private var grade = ""
#State private var description = ""
#State private var country = ""
#State private var region = ""
#State private var crag = ""
#State private var section = ""
#State private var height = ""
#State private var long = "0.0"
#State private var lat = "0.0"
#State private var stars = 0
#State private var image: Image? //= "default"
#State private var imageUrl = "default"
#State private var inputImage: UIImage?
#State private var icons = [""]
#State var pitchCount = 1
#State private var pitch = ["",""]
let cloudinary = CLDCloudinary(configuration: ImageController().config)
private var climbTypeOptions = ["Trad", "Sport", "Boulder", "Solo", "Aid"]
var body: some View {
Form{
Section(header: Text("Climb Detial")) {
HStack{
TextField("Climb Name", text: $name)
}
Picker("Climb Type", selection: $selectedTypeIndex){
ForEach(0..<climbTypeOptions.count){
Text(self.climbTypeOptions[$0])
}
}.pickerStyle(SegmentedPickerStyle())
HStack{
Text("Grade:")
TextField("enter the grade", text: $grade)
}
HStack{
Text("Height:")
TextField("enter the height", text: $height)
}
Text("Description:")
TextEditor(text: $description)
HStack{
Text("Pitches:")
Spacer()
Stepper(value: $pitchCount,
in: 1...100,
label: {
Text(" \(self.pitchCount)")
})
}
ForEach(0..<pitchCount){ x in
HStack{
Text("\(x + 1):")
TextField("Pitch Description", text: $pitch[x] )
}
}
}
Section(header: Text("Location")) {
TextField("Country", text: $country )
TextField("Region", text: $region )
TextField("Crag", text: $crag)
TextField("Sector", text: $section)
HStack{
Text("Longitude:")
TextField("Longitude", text: $long).keyboardType(.numbersAndPunctuation)
}
HStack{
Text("Latitude:")
TextField("Latitude", text: $lat).keyboardType(.numbersAndPunctuation)
}
}
Section(header: Text("Images/Videos")) {
image?.resizable().aspectRatio(contentMode: .fit)
HStack{
Spacer()
Button(action: {
self.showingImagePicker = true
}, label: {
Text("Select an Image")
})
Spacer()
}
}
Section(header: Text("Save")) {
HStack{
Spacer()
Button(action:{
ClimbController().addNewClimbToDB(name: name, desc: description, country: country, region: region, crag: crag, sector: section, long: Double(long) ?? 0.0, lat: Double(lat) ?? 0.0, pitches: pitch, stars: stars, grade: grade, climbType: climbTypeOptions[selectedTypeIndex], icons: icons, imageUrl: imageUrl, height: Double(height) ?? 1, inputImage: inputImage, progress: progress, screenCoordinator: screenCoordinator)
print("x")
}){
Text("Add Climb")
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 5, maxHeight: 10)
.font(.title)
.padding()
.background(Color.yellow)
.foregroundColor(.white)
.padding(10)
.border(Color.yellow, width: 5).cornerRadius(8)
}
Spacer()
}
}
}.navigationBarTitle("Add a Climb", displayMode:.inline).sheet(isPresented: $showingImagePicker, onDismiss: loadImage) {
ImagePicker(image: self.$inputImage)
}
}
func loadImage() {
guard let inputImage = inputImage else { return }
image = Image(uiImage: inputImage)
}
}
It is not all components present, but it could be like the following
}.navigationBarTitle("Add a Climb", displayMode:.inline).sheet(isPresented: $showingImagePicker, onDismiss: loadImage) {
ImagePicker(image: self.$inputImage)
}
.overlay(Group {
if self.progress.isUploading {
ProgressIndicator(progress: self.progress) // << here !!
}
})