The logic of clicking like in SwiftUI - ios

world! I want to make sure that the like is placed only on one card, and not on all at once. I wrote the logic of painting the like and clicking it. But this applies to all cards, not just the one where I clicked like.
How can I change the logic so that the like is placed only on the card I clicked on?
Below the code CellView() is a cell with a card, which I then use in another view
struct CellView: View{
//MARK: - PROPERTIES
#AppStorage("isLiked") var isLiked: Bool = false
var data: Model
//MARK: - BODY
var body: some View{
VStack{
VStack{
AnimatedImage(url: URL(string: data.photo)!)
.resizable()
.aspectRatio(contentMode: .fit)
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color("GrayWhite"), lineWidth: 0.5)
)
}.background(Color.white)
.cornerRadius(10)
VStack{
HStack{
Text("\(data.price) ₽")
.font(.system(size: 16, weight: .bold))
.foregroundColor(Color("BlackWhite"))
Spacer()
Button(action: {
self.isLiked.toggle()
}, label: {
Image(systemName: isLiked ? "heart.fill" : "heart")
.frame(width: 22, height: 22)
.foregroundColor(isLiked ? .red : .black)
})
}.padding(.bottom, 1)
HStack{
Text(data.company)
.font(.system(size: 14, weight: .bold))
.lineLimit(1)
.foregroundColor(Color("BlackWhite"))
Spacer()
}
HStack{
Text(data.name)
.font(.system(size: 14, weight: .bold))
.lineLimit(1)
.foregroundColor(.gray)
Spacer()
}
}
.padding(.horizontal, 2)
}
.padding(5)
}
}
Thank you in advance for your help

Each cell has to be identified for Swift to know what is liked.
import SwiftUI
struct CardData: Identifiable {
var id: String = UUID().uuidString
// add the rest of the data associated with the cards, company, etc.
}
And if we want to follow the MVVM-structure you create a separate file for the functions (when we check like and unlike each card).
class CellViewModel: ObservableObject {
#Published var card: CardData!
#Published var isLiked = false
func like() {
isLiked.toggle()
}
}
Now all our CellViews can be liked seperately (I removed some your inputs, for test)
struct CellView: View{
//MARK: - PROPERTIES
#ObservedObject var cellViewModel: CellViewModel = CellViewModel()
init(cardData: CardData) {
self.cellViewModel.card = cardData
}
//MARK: - BODY
var body: some View{
VStack{
VStack{
Image(systemName: "circle")
.resizable()
.aspectRatio(contentMode: .fit)
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color("GrayWhite"), lineWidth: 0.5)
)
}.background(Color.white)
.cornerRadius(10)
VStack{
HStack{
Text(" ₽")
.font(.system(size: 16, weight: .bold))
.foregroundColor(Color("BlackWhite"))
Spacer()
Button(action: {
cellViewModel.like()
}, label: {
Image(systemName: cellViewModel.isLiked ? "heart.fill" : "heart")
.frame(width: 22, height: 22)
.foregroundColor(cellViewModel.isLiked ? .red : .black)
})
}.padding(.bottom, 1)
HStack{
Text("company")
.font(.system(size: 14, weight: .bold))
.lineLimit(1)
.foregroundColor(Color("BlackWhite"))
Spacer()
}
HStack{
Text("name")
.font(.system(size: 14, weight: .bold))
.lineLimit(1)
.foregroundColor(.gray)
Spacer()
}
}
.padding(.horizontal, 2)
}
.padding(5)
}
}
Testing liking seperately:
struct ContentView: View {
var body: some View {
VStack {
CellView(cardData: CardData())
CellView(cardData: CardData())
CellView(cardData: CardData())
CellView(cardData: CardData())
}
}
}

Related

iOS 15 Keyboard hides textfield

I am trying to create a messaging page layout but the keyboard hides the textfield and moves everything to the the top. I have looked at many tutorials online and replicated them exactly but none of them seem to work.
Here is all the code I have written:
struct MessagingPage: View {
var user: OfficialUserModel
#ObservedObject private var vm = TheUserModel()
#State var screenWidth = UIScreen.main.bounds.width
#State var imageSize = UIScreen.main.bounds.width / 12
#State var text = ""
#State var show = false
#Environment(\.presentationMode) var presentationMode
var body: some View {
NavigationView{
ZStack{
VStack{
HStack{
Spacer()
NavigationLink(destination: UserDisplayPage(user: user)) {
HStack{
WebImage(url: URL(string: user.imageURL ))
.resizable()
.aspectRatio( contentMode: .fill)
.frame(width: imageSize, height: imageSize
)
.cornerRadius(imageSize/2)
VStack(alignment: .leading){
Text(user.FullName)
.font(.body)
.fontWeight(.bold)
.foregroundColor(.white)
Text("\(user.City) , \(user.Country)")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.white)
}
}
}
Spacer()
HStack{
Button{
presentationMode.wrappedValue.dismiss()
} label: {
Image(systemName: "chevron.down")
.font(.title)
.foregroundColor(.myWhite)
}
}
.padding(.trailing)
}
.padding(.top, 30)
.frame(height: 75)
.background(Color.mainBlack)
ScrollView{
Spacer()
ForEach(0..<2){ num in
HStack{
Spacer()
HStack{
Text("Hello \(user.firstName)")
.foregroundColor(.orange)
}
.padding()
.background(Color.mainBlack)
.cornerRadius(15)
.shadow(color: .orange, radius: 2)
}
.padding(.horizontal)
.padding(.top, 8)
}
HStack{Spacer()}
HStack{
HStack{
Text("\(user.FullName) is unable to recieve your message at this time. Please try again at a later time.")
.foregroundColor(.green)
}
.padding()
.background(Color.mainBlack)
.cornerRadius(15)
.shadow(color: .green, radius: 2)
Spacer()
}
.padding(.horizontal)
.padding(.top, 8)
HStack{Spacer()}
}
.background(Color.mainBlack)
ZStack{
HStack{
HStack{
HStack{
TextField("Say Something...", text: self.$text)
.placeholder(when: text.isEmpty) {
Text("Say Something...").foregroundColor(.myWhite.opacity(0.5))
}
.frame(width: screenWidth - 200, height: screenWidth/25)
.foregroundColor(.myCyan)
.accentColor(.myCyan)
.background(Color.mainBlack)
.textContentType(.emailAddress)
if !text.isEmpty{
Button{
print(text)
self.text = ""
}label: {
Image(systemName: "paperplane")
.foregroundColor(.myCyan)
.font(.system(size: 20))
}
}
else{
Button{
print("Show more options")
}label: {
Image(systemName: "plus")
.foregroundColor(.myCyan)
.font(.system(size: 20))
}
}
}
.frame(width: screenWidth - 150, height: screenWidth/25)
.padding()
.background(Color.mainBlack)
.cornerRadius(30)
.shadow(color: .myCyan, radius: 5)
.padding(.bottom,5)
}
}
.padding(.bottom, 50)
.frame(width: screenWidth)
}
}
}
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
.background(Color.mainBlack)
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
}
I want the the textfield to move up as well as the messages in the scrollview but the top HStack with the user image and back/dismiss button should remain in place.
You really have a lot of code in there - and two reasons to clean it up:
To get a good answer, you need to post a minimal, reproducible example, which may also help you debug the code before posting it
Many lines of code are redundant or useless, for example:
Why using things like HStack{Spacer()} instead of Spacer(), which by the way is not needed in this context?
Why having a stack that has only another stack inside?
Coming to your issue, the problem is that one of the redundant modifiers is preventing the text file to move up, and this is the line to delete:
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
Why setting a frame that is as high and large as much as the screen? This is forcing the text field to stay at the bottom.
I tried to do some clean up, I bet some more can be done - see the code below, now the keyboard does not cover the text field anymore:
struct MessagingPage: View {
// Removed some lines of code to reproduce your issue
#State var screenWidth = UIScreen.main.bounds.width
#State var imageSize = UIScreen.main.bounds.width / 12
#State var text = ""
#State var show = false
#Environment(\.presentationMode) var presentationMode
var body: some View {
NavigationView{
// What is the purpose of this ZStack???
// ZStack{
VStack{
HStack{
Spacer()
NavigationLink(destination: Text("Hello")) {
Text("Top bar")
}
Spacer()
HStack{
Button{
presentationMode.wrappedValue.dismiss()
} label: {
Image(systemName: "chevron.down")
.font(.title)
.foregroundColor(.white)
}
}
.padding(.trailing)
}
.padding(.top, 30)
.frame(height: 75)
.background(Color.black)
ScrollView{
// All the views inside the Scrollview need to be inside a VStack
VStack {
// Why this spacer???
// Spacer()
ForEach(0..<2){ num in
HStack{
Spacer()
HStack{
Text("Hello username")
.foregroundColor(.orange)
}
.padding()
.background(Color.black)
.cornerRadius(15)
.shadow(color: .orange, radius: 2)
}
.padding(.horizontal)
.padding(.top, 8)
}
HStack{Spacer()}
HStack{
HStack{
Text("User is unable to receive your message at this time. Please try again at a later time.")
.foregroundColor(.green)
}
.padding()
.background(Color.black)
.cornerRadius(15)
.shadow(color: .green, radius: 2)
Spacer()
}
.padding(.horizontal)
.padding(.top, 8)
// Why a Spacer inside an HStack???
// HStack{Spacer()}
}
.background(Color.black)
}
// What is the purpose of this ZStack???
// ZStack{
// Why an HStack that has only an HStack inside??
// HStack{
// Why an HStack that has only an HStack inside??
// HStack{
HStack{
TextField("Say Something...", text: self.$text)
.frame(width: screenWidth - 200, height: screenWidth/25)
.foregroundColor(.cyan)
.accentColor(.cyan)
.background(Color.white)
.textContentType(.emailAddress)
if !text.isEmpty{
Button{
print(text)
self.text = ""
}label: {
Image(systemName: "paperplane")
.foregroundColor(.cyan)
.font(.system(size: 20))
}
}
else{
Button{
print("Show more options")
}label: {
Image(systemName: "plus")
.foregroundColor(.cyan)
.font(.system(size: 20))
}
}
}
// Are you sure you want set the height of this stack based on the width?
// .frame(width: screenWidth - 150, height: screenWidth/25)
.frame(width: screenWidth - 150)
.padding()
.background(Color.black)
.cornerRadius(30)
.shadow(color: .black, radius: 5)
.padding(.bottom,5)
// }
// }
.padding(.bottom, 50)
.frame(width: screenWidth)
// }
}
// }
// Why setting a frame that is high and large as much as the screen??
// .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
.background(Color.black)
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
}

how to show only unique item when append for my Cart View SWIFTUI

This are my code.
Im Creating Cash Register IPAD
What is see:
When I tap the item twice with the same item name it adds another
row instead
What I expect:
My expectation is it only adds one row of item and its quantity counter
Hope Someone can help me. I already research cannot find sample or any info to direct for what I want to achieved. Thanks all
import Foundation
import SwiftUI
class OrderViewModel: ObservableObject {
#Published var orderItems = [Cart]()
#State var count: Int = 1
func AddToCart(item: item){
orderItems.append(
Cart(product_name: item.product_name, price:item.price, quantity: quantity )
)
}
}
struct Cart: Identifiable{
var id = UUID()
var product_name: String
var price: String
var quantity: Int
}
This are my cartView the will print all #EnvironmentObject from class OrderViewModel
import SwiftUI
struct CartContainer: View {
#EnvironmentObject var order: OrderViewModel
var body: some View{
ZStack{
Color("lightGrayColor")
.ignoresSafeArea()
VStack(alignment: .leading) {
VStack{
HStack{
Text("Item Name")
.font(Font.system(size: 16, weight: .light))
.frame(width:200)
Spacer()
Text("Qty.").font(Font.system(size: 16, weight: .light))
.padding(.leading, -78)
Spacer()
Text("Subtotal.").font(Font.system(size: 16, weight: .light))
.padding(.leading, -78)
}
}
.padding(.top, 10)
ScrollView{
VStack(alignment: .leading){
ForEach(order.orderItems, id:\.id){ list in
HStack{
Text(list.product_name)
.font(Font.system(size: 16, weight: .light))
.frame(width:200)
Spacer()
Text(list.price).font(Font.system(size: 16, weight: .light))
.padding(.leading, -78)
Spacer()
Text("\(list.quantity)").font(Font.system(size: 16, weight: .light))
.padding(.leading, -78)
}
.padding(.top, 10)
}
}.padding()
}.padding()
Text("Grand Total :")
.font(Font.system(size: 30, weight: .bold))
.padding(.bottom, 30)
.padding(.leading, 40)
Button(action: {
}) {
Text("Charge")
.foregroundColor(.white)
.font(Font.system(size: 30, weight: .bold))
.frame(minWidth: 0, maxWidth: .infinity)
.padding(.top, 35)
.padding(.bottom, 35)
.background(Color("ButtonGreen"))
}.padding(.bottom, -10)
}
}
.frame(width: 550)
}
}
struct CartContainer_Previews: PreviewProvider {
static var previews: some View {
CartContainer().environmentObject(OrderViewModel())
}
}
And this are the Product List which I pullout from API
import SwiftUI
struct ProductsListContainer: View {
var body: some View{
ZStack{
Color(.white)
VStack{
ProductList()
}
}
.frame(maxWidth: .infinity)
}
}
struct ProductList: View{
var body: some View{
VStack {
SearchItem()
ItemListHeader()
ShowItemList()
.padding(5)
}
}
}
struct ItemListHeader: View {
var body: some View {
HStack{
Text("Item Name")
.font(Font.system(size: 20, weight: .heavy))
.frame(width:300)
Spacer()
Text("Price").font(Font.system(size: 20, weight: .heavy))
.padding(.leading, -78)
Spacer()
Text("Qty.").font(Font.system(size: 20, weight: .heavy))
.padding(.leading, -78)
}
Divider()
}
}
struct ShowItemList: View {
#StateObject var itemVM = ProductViewListModel()
#EnvironmentObject var order: OrderViewModel
var body: some View {
ScrollView {
ForEach(itemVM.item) { dataPoint in
ZStack{
HStack{
Text(dataPoint.product_name)
.font(.headline)
.frame(width:300)
Spacer()
Text(dataPoint.price)
Spacer()
Text("\(dataPoint.quantity)")
}
.padding()
.padding(.leading, -20)
.padding(.trailing, 20)
.padding(.top, 5)
.padding(.bottom, 5)
}.onTapGesture {
self.order.AddToCart(item: dataPoint.product)
}
Divider()
}
}
.padding()
}
}
When you use "AddToCart(...)", you need to check if you have the item already.
Try something similar to this:
class OrderViewModel: ObservableObject {
#Published var orderItems = [Cart]()
// note: comparing product_name, assuming product_name is unique here
func AddToCart(item: Item) {
if let ndx = orderItems.firstIndex(where: {$0.product_name == item.product_name}) {
// already have this cart/item, do an update here
orderItems[ndx].price = item.price
orderItems[ndx].quantity = item.quantity
} else {
// add a new cart/item
orderItems.append(Cart(product_name: item.product_name, price:item.price, quantity: item.quantity))
}
}
}
and probably call it like this:
.onTapGesture {
self.order.AddToCart(item: dataPoint)
}

iOS: How to make my image logo at the top of the page SwiftUI

firstly I am really new to iOS development and Swift (2 weeks coming here from PHP :))
I am building my simple Login page and wondering how to make my Logo image at the top of the page. Also I am wondering if I have done my layout wrong to get the desired layout as in the screenshot. Would appreciate the help on this.
(Logo scribbled out in the screen shot needs to go to the top outside the white background)
Thanks
LoginView:
import SwiftUI
struct LoginView: View {
#State private var email: String = ""
#State private var password: String = ""
let verticalPaddingForForm = 40
var body: some View {
ZStack {
Color(red: 20/225.0 ,green: 22/225.0 , blue: 25/225.0)
VStack(spacing: CGFloat(verticalPaddingForForm)) {
Image("logo")
.resizable()
.scaledToFit()
Divider()
VStack {
TextField("Email", text: $email)
.padding(.horizontal, 30).padding(.top, 20)
Divider()
.padding(.horizontal, 30)
SecureField("Password", text: $password)
.padding(.horizontal, 30).padding(.top, 20)
Divider()
.padding(.horizontal, 30)
}
.background(Color(.white))
Text("Forgotten Password")
.foregroundColor(.blue)
.font(.system(size: 15))
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
Text("Login")
.padding()
.font(.system(size: 20))
}
.background(Color.black)
.foregroundColor(Color.white)
.cornerRadius(10)
.padding(.top, 0)
.padding(.bottom, 20)
}
.padding(.horizontal, CGFloat(verticalPaddingForForm))
.background(Color(.white))
VStack{
Spacer()
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
Text("Register")
.padding()
.font(.system(size: 40))
}
.background(Color(red: 20/225.0 ,green: 22/225.0 , blue: 25/225.0))
.foregroundColor(Color.white)
.cornerRadius(10)
.padding()
}
}.ignoresSafeArea()
};
}
ContentView:
import SwiftUI
struct ContentView: View {
var body: some View {
LoginView()
}
}
extension UIDevice {
var hasNotch: Bool {
let bottom = UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0
return bottom > 0
}
}
Try below code-:
Use a ZStack to give a backgroundColor to your view, and give that a modifier of .ignoresSafeArea().
Use VStack inside ZStack to layout other view components. I have done few modifications on my side.
struct LoginView: View {
#State private var email: String = ""
#State private var password: String = ""
let verticalPaddingForForm = 40
var body: some View {
ZStack() {
Color(red: 20/225.0 ,green: 22/225.0 , blue: 25/225.0).ignoresSafeArea()
VStack(spacing:15){
Image(systemName: “logo")
.resizable()
.scaledToFit()
.frame( height: 200)
.foregroundColor(Color.white)
.padding([.top],15)
VStack(spacing: CGFloat(verticalPaddingForForm)) {
VStack {
TextField("Email", text: $email)
.padding(.horizontal, 30).padding(.top, 20)
Divider()
.padding(.horizontal, 30)
SecureField("Password", text: $password)
.padding(.horizontal, 30).padding(.top, 20)
Divider()
.padding(.horizontal, 30)
}
.background(Color(.white))
.padding([.top])
Text("Forgotten Password")
.foregroundColor(.blue)
.font(.system(size: 15))
Button(action: {}) {
Text("Login")
.padding()
.font(.system(size: 20))
}
.background(Color.black)
.foregroundColor(Color.white)
.cornerRadius(10)
.padding([.bottom])
}
.background(Color(.white))
Spacer()
Button(action: {}) {
Text("Register")
.padding()
.font(.system(size: 40))
}
.foregroundColor(Color.white)
.cornerRadius(10)
}
}
};
}
struct Test1: View {
var body: some View {
LoginView()
}
}
extension UIDevice {
var hasNotch: Bool {
let bottom = UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0
return bottom > 0
}
}
You can use ZStack (alignment: .top) to set the alignment from the top. And then you can use a Spacer() at the end of the second VStack to expand the view.
The following code works for me. It's your code with little changes. Also, I suggest you create a subview to better organize your code, instead of writing all views within the same view.
import SwiftUI
struct LoginView: View {
#State private var email: String = ""
#State private var password: String = ""
let verticalPaddingForForm = 40
var body: some View {
ZStack (alignment: .top){
Color(red: 20/225.0 ,green: 22/225.0 , blue: 25/225.0)
VStack(spacing: CGFloat(verticalPaddingForForm)) {
Image(systemName: "gear")
.resizable()
.scaledToFit()
.frame(width: 100)
Divider()
VStack {
TextField("Email", text: $email)
.padding(.horizontal, 30).padding(.top, 20)
Divider()
.padding(.horizontal, 30)
SecureField("Password", text: $password)
.padding(.horizontal, 30).padding(.top, 20)
Divider()
.padding(.horizontal, 30)
}
.background(Color(.white))
Text("Forgotten Password")
.foregroundColor(.blue)
.font(.system(size: 15))
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
Text("Login")
.padding()
.font(.system(size: 20))
}
.background(Color.black)
.foregroundColor(Color.white)
.cornerRadius(10)
.padding(.top, 0)
.padding(.bottom, 20)
Spacer()
}
.padding(.horizontal, CGFloat(verticalPaddingForForm))
.background(Color(.white))
VStack{
Spacer()
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
Text("Register")
.padding()
.font(.system(size: 40))
}
.background(Color(red: 20/225.0 ,green: 22/225.0 , blue: 25/225.0))
.foregroundColor(Color.white)
.cornerRadius(10)
.padding()
}
}
}
}
struct LoginView_Previews: PreviewProvider {
static var previews: some View {
LoginView()
}
}
Just put Spacer() between Image("logo") and Divider() to push your logo to the top of the screen

Add delete function to Grid layout in swiftUI for iOS

I've managed to build a user form to get inputs and display them in grids. Right now I have them displayed in a scroll view. I'm avoiding lists so as to get a card like appearance. However, I'm not able to get a delete feature to work as it is done with Lists. I'm new to swift and not sure how to implement delete feature in this grid layout. I would like for users to be able to delete entries. I'm using Core Data, Swift and SwiftUI. Thanks.
struct ContentView: View {
// MARK: PROPERTIES
#Environment(\.managedObjectContext) var managedObjectContext
#State private var showingAddChangeView: Bool = false
#State private var dateAdded: Date = Date()
#State private var showingActionSheet = false
#FetchRequest(
entity: Agent.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \Agent.dateAdded, ascending: false)]) var cas: FetchedResults<Agent>
let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .medium
return formatter
}()
private var gridItemLayout = [GridItem(.flexible())]
// MARK: BODY VIEW
var body: some View {
NavigationView {
ScrollView {
LazyVGrid(columns: gridItemLayout , alignment: .center, spacing: 30) {
ForEach(self.cas, id: \.self) { caRequest in
VStack(alignment: .leading) {
HStack {
Image(systemName: "calendar")
Text(caRequest.dateAdded ?? "Missing Dates")
.font(.callout).fontWeight(.bold)
Spacer()
Button(action: {
}) {
Image(systemName: "trash.fill")
.foregroundColor(.black)
}
.padding(.trailing, 20)
}
//.padding()
.padding(.leading, 20)
.padding(.top, 25)
Divider().frame(height: 1).background(Color.gray)
.padding()
HStack{
Image(systemName: "paintbrush.fill")
.foregroundColor(Color(.systemGreen))
.padding(.leading, 20)
.padding(.bottom, 5)
Text(caRequest.intention ?? "No Intentions Recorded")
.font(.callout).multilineTextAlignment(.leading)
.padding(.leading, 5)
.padding(.bottom, 5)
}
HStack{
Image(systemName: "checkmark.seal.fill")
.foregroundColor(Color(.systemBlue))
.padding(.leading, 20)
.padding(.bottom, 5)
Text(caRequest.affirmation ?? "No Affirmation Recorded")
.font(.callout).multilineTextAlignment(.leading)
.padding(.leading, 5)
.padding(.bottom, 5)
}
HStack{
Image(systemName: "heart.fill")
.foregroundColor(Color(.systemPink))
.padding(.leading, 20)
.padding(.bottom, 5)
Text(caRequest.gratitudeOne ?? "Missing Gratitude")
.font(.callout).multilineTextAlignment(.leading)
.padding(.leading, 5)
.padding(.bottom, 5)
}
HStack{
Image(systemName: "heart.fill")
.foregroundColor(Color(.systemPink))
.padding(.leading, 20)
.padding(.bottom, 5)
Text(caRequest.gratitudeTwo ?? "Missing Gratitude")
.font(.callout).multilineTextAlignment(.leading)
.padding(.leading, 5)
.padding(.bottom, 5)
}
HStack{
Image(systemName: "heart.fill")
.foregroundColor(Color(.systemPink))
.padding(.leading, 20)
.padding(.bottom, 20)
Text(caRequest.gratitudeThree ?? "Missing Gratitude")
.font(.callout).multilineTextAlignment(.leading)
.padding(.leading, 5)
.padding(.bottom, 20)
}
}
// .frame(width: 350, height: 300)
.frame(minWidth: 0, maxWidth: 350, minHeight: 0, maxHeight: .infinity)
} // END: FOR EACH
} // END: LAZYVGRID
} // END: SCROLLVIEW
.padding(.top, 100)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
HStack {
Image(systemName: "person.crop.square.fill")
.foregroundColor(.white)
.font(.title)
Text("Change Agent")
.foregroundColor(.white)
.font(.title2)
.fontWeight(.bold)
.shadow(radius: 20)
}
}
}
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(
trailing:
Button(action: {
self.showingAddChangeView.toggle()
}) {
Text("Add")
Image(systemName: "plus")
.imageScale(.medium)
} // END: ADD BUTTON
.foregroundColor(.white)
.sheet(isPresented: $showingAddChangeView) {
AddChangeView()
}
.navigationBarItems(trailing: EditButton())
) // END: NAVIGATION BAR ITEMS
} // END: NAVIGATIONVIEW
} // END: BODY VIEW
} // END: STRUCT

How can I change a global variable's value from inside a function?

I have a #State boolean in my code inside the struct which I have a button that toggle()s it. However, I want to use that variable in my other views as well. So I need to have it outside struct so I can use it another place. My question is that how can I now change its value? Previously my button's action toggled it. How can I change it now?
Here is my code:
struct ContentView: View {
#State var checked: Bool = false
var body: some View {
HStack {
Button(action: {checked.toggle()}) {
Image(systemName: checked ? "checkmark.square": "square")
.resizable()
.aspectRatio(contentMode: .fit)
.scaledToFit()
.foregroundColor(.black)
.frame(height: 20)
}
Text("I agree")
.fontWeight(.bold)
Spacer()
}
.padding(.horizontal, 20)
}
}
However, I want this to work:
var checked:Bool = false
struct ContentView: View {
var body: some View {
HStack {
Button(action: {checked.toggle()}) {
Image(systemName: checked ? "checkmark.square": "square")
.resizable()
.aspectRatio(contentMode: .fit)
.scaledToFit()
.foregroundColor(.black)
.frame(height: 20)
}
Text("I agree")
.fontWeight(.bold)
Spacer()
}
.padding(.horizontal, 20)
}
}
The possible solution is to use shared observable object with wrapped this property, then it can be observed in all dependent views.
Here is a demo. Tested with Xcode 12.1 / iOS 14.1
class GlobalState: ObservableObject {
static let shared = GlobalState()
#Published var checked:Bool = false
}
struct myView: View {
#ObservedObject var gs = GlobalState.shared
var body: some View {
HStack {
Button(action: {gs.checked.toggle()}) {
Image(systemName: gs.checked ? "checkmark.square": "square")
.resizable()
.aspectRatio(contentMode: .fit)
.scaledToFit()
.foregroundColor(.black)
.frame(height: 20)
}
Text("I agree")
.fontWeight(.bold)
Spacer()
}
.padding(.horizontal, 20)
Spacer()
}
}

Resources