Home > Software design >  How to change value on button press using ForEach Identifiable SwiftUI
How to change value on button press using ForEach Identifiable SwiftUI

Time:03-10

I am developing the app for IOS and I am the first time using SwiftUI, just a beginner. The problem is that I try to change the values of Text() by button click that are made by using ForEach Identifiable. Everything is nice but this is the only thing that I cannot do and find an answer for my self on internet.

This is the view of application

Here is my code


import SwiftUI

private struct SizePreferenceKey: PreferenceKey {
  static var defaultValue: CGSize = .zero
  static func reduce(value: inout CGSize, nextValue: () -> CGSize) {}
}

public struct SemaphoreFlags: View {
    @State private var headerHeight = CGSize()
    
    public var body: some View {
        ZStack{
            
            Image("background")
                .resizable()
                .scaledToFill()
                .frame(maxWidth: UIScreen.screenWidth, maxHeight: UIScreen.screenHeight )
                .edgesIgnoringSafeArea(.bottom)
            
                VStack{
                    
                    Image("header_inv")
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(maxWidth: UIScreen.screenWidth ,alignment: .top)
                        .opacity(0.0)
                        .readSize {
                            height in headerHeight = height
                        }.padding(.bottom, 16)
                    
                    Text("HEADER TEXT")
                        .foregroundColor(.white)
                        .font(.system(size: 24))
                        .fontWeight(.medium)
                        .frame(width: UIScreen.screenWidth - 25, height: UIScreen.screenWidth / 6, alignment: .center)
                        .background(Color(red: 0.29, green: 0.67, blue: 0.88))
                        .padding(.bottom, 4.0)
                    
                    ScrollView{
                        Text(NSLocalizedString("BeaufortScale_Description", comment: "BeaufortScale_Description"))
                            .font(.system(size: 20))
                            .multilineTextAlignment(.center)
                            .background(Color.white)
                            .padding(.horizontal,8)
                            .foregroundColor(.black)
                            .frame(width: .infinity, height: .infinity)
                        
                    }
                    
                    Image("blank")
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: UIScreen.screenWidth - 25, height: UIScreen.screenWidth / 2, alignment: .center)
                    
                    
                    Text("BottomLETTER")
                        .font(.system(size: UIScreen.screenWidth/6))
                        .fontWeight(.medium)
                        .multilineTextAlignment(.center)
                        .foregroundColor(Color(red: 0.09, green: 0.30, blue: 0.47))
                        .frame(width: UIScreen.screenWidth, height: .infinity, alignment: .center)
                    
                    Spacer()
                    
                    ScrollView(.horizontal, showsIndicators: false){
                        HStack{
                            ForEach(semaphoreButtonPropsData) { item in
                                semaphoreFlagsButton(SemaphoreButtonProps: item)
                            }
                        }
                    }.clipped().edgesIgnoringSafeArea(.bottom).padding(.vertical)

                }.frame(width: .infinity, height: .infinity)
            
            
            
            Image("header")
                .resizable()
                .aspectRatio(contentMode: .fit)
                .frame(maxWidth: UIScreen.screenWidth, maxHeight: UIScreen.screenHeight ,alignment: .top)
                
            VStack {
                HStack {
                    Button (action: gotoMenu, label: {
                        Image("back")
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(width: 32, height: headerHeight.height)
                            .padding(.leading, 16.0)
                            
                    })
                    
                    Spacer()
                }
                Spacer()
            }
            
        }.background(Color(red: 0.09, green: 0.30, blue: 0.47).edgesIgnoringSafeArea(.all))
    }
}

struct SemaphoreFlags_Previews: PreviewProvider {
    static var previews: some View {
        SemaphoreFlags()
    }
}

struct semaphoreFlagsButton: View {
    var SemaphoreButtonProps: semaphoreButtonProps
    var body: some View {
        Button(action:{
           // ACTION TO CHANGE THE VALUES OF DESIGN 
                        (HEADER TEXT, DESCRIPTION, IMAGE, BOTTOM LETTER)

        }, label: {
            Text(SemaphoreButtonProps.buttonLetter)
                .font(.system(size: 26))
                .fontWeight(.light)
                .foregroundColor(Color.white)
                .multilineTextAlignment(.leading)
                .padding(.all, 4.0)
                .foregroundColor(.white)
                .frame(width: .infinity, height: 50, alignment: .center)
            
        })
            .frame(width: 100, height: 50 , alignment: .center)
            .background(Color(red: 0.29, green: 0.67, blue: 0.88))
            .cornerRadius(12)
            .padding(.leading, 8)
    }
}

struct semaphoreButtonProps: Identifiable {
    var id = UUID()
    var headerText: String
    var description: String
    var image: Image
    var bottomLetter: String
    var buttonLetter: String
}

let semaphoreButtonPropsData = [
    semaphoreButtonProps(headerText: "About",
                         description: NSLocalizedString("BeaufortScale_Description", comment: "BeaufortScale_Description"),
                         image: Image("blank"),
                         bottomLetter: " ",
                         buttonLetter: "About"),
    
    semaphoreButtonProps(headerText: "Alpha",
                         description: " ",
                         image: Image("a-alpha"),
                         bottomLetter: "A",
                         buttonLetter: "A"),
    
    semaphoreButtonProps(headerText: "Bravo",
                         description: " ",
                         image: Image("b-bravo"),
                         bottomLetter: "B",
                         buttonLetter: "B")
]

Big Thanks!

CodePudding user response:

In order for your data array to be mutable, it should be stored in @State in your View. Then, you can pass it via Binding to your child View.

(I've also adjusted the capitalization of your types to fit the Swift conventions, so be careful to reflect that in your code if you copy/paste)

public struct SemaphoreFlags: View {
    @State private var semaphoreButtonPropsData = [ //<-- Here
        SemaphoreButtonProps(headerText: "About",
                             description: NSLocalizedString("BeaufortScale_Description", comment: "BeaufortScale_Description"),
                             image: Image("blank"),
                             bottomLetter: " ",
                             buttonLetter: "About"),
        
        SemaphoreButtonProps(headerText: "Alpha",
                             description: " ",
                             image: Image("a-alpha"),
                             bottomLetter: "A",
                             buttonLetter: "A"),
        
        SemaphoreButtonProps(headerText: "Bravo",
                             description: " ",
                             image: Image("b-bravo"),
                             bottomLetter: "B",
                             buttonLetter: "B")
    ]
    
    public var body: some View {
        ScrollView(.horizontal, showsIndicators: false){
            HStack{
                ForEach($semaphoreButtonPropsData) { $item in //<-- Here
                    SemaphoreFlagsButton(semaphoreButtonProps: $item) //<-- Here
                }
            }
        }
    }
}

struct SemaphoreFlagsButton: View {
    @Binding var semaphoreButtonProps: SemaphoreButtonProps //<-- Here

    var body: some View {
        Button(action:{
            semaphoreButtonProps.buttonLetter = "\(Date())" //<-- Here

        }, label: {
            Text(semaphoreButtonProps.buttonLetter)
        })
            .frame(width: 100, height: 50 , alignment: .center)
            .background(Color(red: 0.29, green: 0.67, blue: 0.88))
            .cornerRadius(12)
            .padding(.leading, 8)
    }
}

struct SemaphoreButtonProps: Identifiable { //<-- Here
    var id = UUID()
    var headerText: String
    var description: String
    var image: Image
    var bottomLetter: String
    var buttonLetter: String
}

Update, based on asker's new information:

public struct SemaphoreFlags: View {
    @State private var headerText : String = "HEADER TEXT"
    @State private var semaphoreButtonPropsData = [ //<-- Here
        SemaphoreButtonProps(headerText: "About",
                             description: NSLocalizedString("BeaufortScale_Description", comment: "BeaufortScale_Description"),
                             image: Image("blank"),
                             bottomLetter: " ",
                             buttonLetter: "About"),
        
        SemaphoreButtonProps(headerText: "Alpha",
                             description: " ",
                             image: Image("a-alpha"),
                             bottomLetter: "A",
                             buttonLetter: "A"),
        
        SemaphoreButtonProps(headerText: "Bravo",
                             description: " ",
                             image: Image("b-bravo"),
                             bottomLetter: "B",
                             buttonLetter: "B")
    ]
    
    public var body: some View {
        Text(headerText)
        ScrollView(.horizontal, showsIndicators: false){
            HStack{
                ForEach(semaphoreButtonPropsData) { item in //<-- Here
                    SemaphoreFlagsButton(semaphoreButtonProps: item, headerText: $headerText) //<-- Here
                }
            }
        }
    }
}

struct SemaphoreFlagsButton: View {
    var semaphoreButtonProps: SemaphoreButtonProps
    @Binding var headerText : String

    var body: some View {
        Button(action:{
            headerText = semaphoreButtonProps.headerText
        }, label: {
            Text(semaphoreButtonProps.buttonLetter)
        })
            .frame(width: 100, height: 50 , alignment: .center)
            .background(Color(red: 0.29, green: 0.67, blue: 0.88))
            .cornerRadius(12)
            .padding(.leading, 8)
    }
}

struct SemaphoreButtonProps: Identifiable { //<-- Here
    var id = UUID()
    var headerText: String
    var description: String
    var image: Image
    var bottomLetter: String
    var buttonLetter: String
}

Or, an option that stores the ID of the selected item:

public struct SemaphoreFlags: View {
    @State private var selectedSemaphore : UUID? = nil
    @State private var semaphoreButtonPropsData = [ //<-- Here
        SemaphoreButtonProps(headerText: "About",
                             description: NSLocalizedString("BeaufortScale_Description", comment: "BeaufortScale_Description"),
                             image: Image("blank"),
                             bottomLetter: " ",
                             buttonLetter: "About"),
        
        SemaphoreButtonProps(headerText: "Alpha",
                             description: " ",
                             image: Image("a-alpha"),
                             bottomLetter: "A",
                             buttonLetter: "A"),
        
        SemaphoreButtonProps(headerText: "Bravo",
                             description: " ",
                             image: Image("b-bravo"),
                             bottomLetter: "B",
                             buttonLetter: "B")
    ]
    
    public var body: some View {
        if let selected = semaphoreButtonPropsData.first(where: { $0.id == selectedSemaphore }) {
            Text(selected.headerText)
        }
        ScrollView(.horizontal, showsIndicators: false){
            HStack{
                ForEach(semaphoreButtonPropsData) { item in //<-- Here
                    SemaphoreFlagsButton(semaphoreButtonProps: item, selectedSemaphore: $selectedSemaphore) //<-- Here
                }
            }
        }
    }
}

struct SemaphoreFlagsButton: View {
    var semaphoreButtonProps: SemaphoreButtonProps
    @Binding var selectedSemaphore : UUID?

    var body: some View {
        Button(action:{
            selectedSemaphore = semaphoreButtonProps.id
        }, label: {
            Text(semaphoreButtonProps.buttonLetter)
        })
            .frame(width: 100, height: 50 , alignment: .center)
            .background(Color(red: 0.29, green: 0.67, blue: 0.88))
            .cornerRadius(12)
            .padding(.leading, 8)
    }
}
  • Related