Home > Enterprise >  Changing Button Label and Title dynamically for Buttons created from Array (in swift)
Changing Button Label and Title dynamically for Buttons created from Array (in swift)

Time:03-01

My environment: The code below is written in swift Playgrounds 4 on my company IPad.

Goal of the project: I am trying to improve upon the classroom management tool of my school, which is basically a paper traffic light with a clothespin for each student. I want an App in which all of my 28 students are represented by a button each Those buttons are supposed to changes from green to yellow when tapped 4 times and to red if tapped thrice more. As a nice-to-have feature the total number ob button tabs (aka. warnings) should be displayed on the buttons. Also the buttons should be structured in a meaningful way (like the tables of my students; a 7 x 4 grid).

My progress so far: I wrote a class (student), and created instances of that class to represent four of my favorite students. I packed them into an array (students) to easily create a button for each of them. In this class “state” is the amount of times the student has been warned (the button has been tapped) and “color” is supposed to be the color of the respective button.

As of this writing I need a variable (col) for handling of the color. I think that would be unnecessary but did not get the color change to work in a different way.

After structuring in a grid I create a button for each element in the array (students). On buttonPress the state and color are updated for the respective student and (for reasons unknown to me) col is updated as well.

The label of the button then displays the name, state and color of the respective student... or does it?

My problems at this point: The state (as displayed on the buttons) updates only when the color changes and when it does it changes the color for all the buttons and not just the one. I would like the buttons to change their color individually and the label to update on each press of a button. Sadly I have not found the correct syntax to do so.

My questions:

How can I set a default color (green) within my class? How do I persuade my buttons to change color individually? How would I get my labels to update on buttonPress?

Thanks in advance!

Code

import SwiftUI

public class student{
    public var first: String
    public var last: String
    public var state: Int
    public var color: Color
    public init(first: String, last: String, color: Color){
        self.first = first
        self.last = last
        state = Int()
        self.color = color
    }
}
var John = student(first: "John", last: "Lennon", color: .green)
var Paul = student(first: "Paul", last: "McCartney", color: .green)
var Georg = student(first: "Georg", last: "Harrison", color: .green)
var Ringo = student(first: "Ringo", last: "Starr", color: .green)

var students = [John, Paul, Georg, Ringo]

struct ContentView: View {
    
    @State private var col = Color.green
    
    let columnLayout = Array(repeating: GridItem(), count: 7)
    
    
    var body: some View {
        NavigationView{
            ScrollView{
                LazyVGrid(columns: columnLayout){
                    ForEach(students, id: \.last) { student in
                        
                        Button{
                            student.state  = 1
                            //print(student.state)
                            
                            if (student.state < 4) {
                                student.color = .green
                            }
                            
                            else if (student.state >= 7){
                                student.color = .red
                            }
                            
                            else {
                                student.color = .yellow
                            }
                            
                            col = student.color
                            //print(col)
                            
                        } label:{
                            ZStack{
                                RoundedRectangle(cornerRadius: 10, style: .continuous)
                                    .foregroundColor(col)
                                    .aspectRatio(2.0, contentMode: ContentMode.fit)
                                
                                Text("\(student.first) - \(student.state)")
                                    .foregroundColor(.black)
                                //.font(.largeTitle)
                                    .scaledToFit()
                            }
                        }
                    }
                }
            }.navigationTitle("Classroom Management")
        }.navigationViewStyle(.stack)
    }
}

CodePudding user response:

There are a couple of issues going on here. The first is that in SwiftUI, generally you want your model to be a struct, since SwiftUI Views respond well to changes in value types (like a struct) out-of-the-box.

public struct Student {
    public var first: String
    public var last: String
    public var state: Int
    public var color: Color
    public init(first: String, last: String, color: Color){
        self.first = first
        self.last = last
        state = Int()
        self.color = color
    }
}

var john = Student(first: "John", last: "Lennon", color: .green)
var paul = Student(first: "Paul", last: "McCartney", color: .green)
var georg = Student(first: "Georg", last: "Harrison", color: .green)
var ringo = Student(first: "Ringo", last: "Starr", color: .green)

Note that I've also changed your code a bit to use the Swift conventions of capitalizing type names and lowercasing variable names.

Next, since you're trying to change properties on the Students, you'll want to represent them with a @State array. Then, by using the new element binding syntax on the ForEach line (see the $ characters), you can get a reference to each Student that you can modify.

struct ContentView: View {
    
    @State private var students = [john, paul, georg, ringo]
    
    let columnLayout = Array(repeating: GridItem(), count: 7)
    
    var body: some View {
        NavigationView{
            ScrollView{
                LazyVGrid(columns: columnLayout){
                    ForEach($students, id: \.last) { $student in
                        
                        Button{
                            student.state  = 1
                            
                            if (student.state < 4) {
                                student.color = .green
                            }
                            
                            else if (student.state >= 7){
                                student.color = .red
                            }
                            
                            else {
                                student.color = .yellow
                            }
                            
                        } label:{
                            ZStack{
                                RoundedRectangle(cornerRadius: 10, style: .continuous)
                                    .foregroundColor(student.color)
                                    .aspectRatio(2.0, contentMode: .fit)
                                
                                Text("\(student.first) - \(student.state)")
                                    .foregroundColor(.black)
                                    .scaledToFit()
                            }
                        }
                    }
                }
            }.navigationTitle("Classroom Management")
        }.navigationViewStyle(.stack)
    }
}

CodePudding user response:

There are 2 questions that I have identified in your request that are the key to your problem.

  1. How can I set a default color (green) within my class?

Just create a default value inside your class definition. Replace the existing init() of your class with:

public init(first: String, last: String, color: Color = .green)   // Default value, you can even omit the colour in your initializer
  1. How do I persuade my buttons to change color individually?

Your view has a variable col that retains the same value throughout the view. That's the color of your button. Just delete it and use each student's color:

.foregroundColor(student.color)
  • Related