为从数组创建的按钮动态更改按钮标签和标题(swift)

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

我的环境: 下面的代码写在我公司 swift Playgrounds 4 IPad.

项目目标: 我正在尝试改进我学校的 class 房间管理工具,它基本上是一个纸质交通信号灯,每个学生都有一个衣夹。我想要一个应用程序,其中我所有的 28 个学生都由一个按钮代表,这些按钮应该在点击 4 次时从绿色变为黄色,如果再点击三次则变为红色。作为一项 nice-to-have 功能,按钮标签(也称为警告)的总数应显示在按钮上。此外,按钮的结构应该有意义(就像我学生的表格;7 x 4 网格)。

我目前的进度: 我写了一个 class(学生),并创建了那个 class 的实例来代表我最喜欢的四个学生。我将它们打包成一个数组(学生),以便轻松地为每个人创建一个按钮。在此 class “state” 是学生被警告的次数(点击按钮),“color” 应该是相应按钮的颜色。

在撰写本文时,我需要一个变量 (col) 来处理颜色。我认为那是不必要的,但没有让颜色变化以不同的方式工作。

在网格中构建后,我为数组中的每个元素(学生)创建了一个按钮。在 buttonPress 上,相应学生的状态和颜色会更新,并且(出于我不知道的原因)col 也会更新。

然后按钮的标签会显示相应学生的姓名、状态和颜色……是吗?

我目前遇到的问题: 状态(显示在按钮上)仅在颜色发生变化时更新,并且当它发生变化时,它会更改所有按钮的颜色,而不仅仅是一个按钮。我希望按钮可以单独更改它们的颜色,并且标签可以在每次按下按钮时更新。遗憾的是,我还没有找到正确的语法。

我的问题:

如何在 class 中设置默认颜色(绿色)? 如何说服我的按钮单独改变颜色? 我如何让我的标签在 buttonPress 上更新?

提前致谢!

代码

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)
    }
}

这里有几个问题。首先是在 SwiftUI 中,通常您希望模型是 struct,因为 SwiftUI 视图对值类型的变化响应良好(像 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)

请注意,我还稍微更改了您的代码以使用 Swift 大写类型名称和小写变量名称的约定。

接下来,由于您要更改 Student 的属性,因此您需要用 @State 数组来表示它们。然后,通过在 ForEach 行上使用新的元素绑定语法(参见 $ 字符),您可以获得对每个可以修改的 Student 的引用。

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)
    }
}

我在您的请求中确定了 2 个问题,它们是您问题的关键。

  1. 如何在 class 中设置默认颜色(绿色)?

只需在 class 定义中创建一个默认值。将 class 的现有 init() 替换为:

public init(first: String, last: String, color: Color = .green)   // Default value, you can even omit the colour in your initializer
  1. 如何说服我的按钮单独更改颜色?

首先,您的视图有一个变量 col,它在整个视图中保持相同的值。那是你按钮的颜色。删除它并使用每个学生的颜色:

.foregroundColor(student.color)

编辑

其次,students是一个结构数组,他们不会发布更改。一种解决方法(由于 SwiftUI 的好处也由 Apple 推广)是创建一个额外的视图,每次 单个 学生更改时都会更改状态。

因此,创建另一个名为“StudentButton”的视图,在该视图中,您可以为单个学生提供 @State 属性。

这是我测试过并有效的代码(我对布局进行了一些修改,但您可以根据需要设置视图):

struct Example: View {

    @State private var students = [john, paul, georg, ringo]
    
    let columnLayout = Array(repeating: GridItem(), count: 4)
    
    var body: some View {
        NavigationView{
            ScrollView{
                LazyVGrid(columns: columnLayout){
                    ForEach(students, id: \.last) { student in
                        
                        // Call another view here
                        StudentButton(student)
                        
                    }
                }
            }.navigationTitle("Classroom Management")
        }.navigationViewStyle(.stack)
    }
}


// Here's the other view
struct StudentButton: View {
    
    // This is the state var of each single student
    @State private var student: Student
    
    init(_ student: Student) {
        self.student = student
    }
    
    var body: some View {
        Button{
            student.state += 1
            
            if (student.state < 4) {
                student.color = .green
            }
            
            else if (student.state >= 7){
                student.color = .red
            }
            
            else {
                student.color = .yellow
            }
            
        } label:{
            
            // I changed this just to visualize better, but just do as you please
            Text("\(student.first) - \(student.state)")
                .foregroundColor(.black)
            .padding()
            .background {
                RoundedRectangle(cornerRadius: 10, style: .continuous)
                    .foregroundColor(student.color)
                    .aspectRatio(2.0, contentMode: .fit)
            }
        }
    }
}