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 Student
s, 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.
- 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
- 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)