为从数组创建的按钮动态更改按钮标签和标题(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 个问题,它们是您问题的关键。
- 如何在 class 中设置默认颜色(绿色)?
只需在 class 定义中创建一个默认值。将 class 的现有 init()
替换为:
public init(first: String, last: String, color: Color = .green) // Default value, you can even omit the colour in your initializer
- 如何说服我的按钮单独更改颜色?
首先,您的视图有一个变量 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)
}
}
}
}
我的环境: 下面的代码写在我公司 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 个问题,它们是您问题的关键。
- 如何在 class 中设置默认颜色(绿色)?
只需在 class 定义中创建一个默认值。将 class 的现有 init()
替换为:
public init(first: String, last: String, color: Color = .green) // Default value, you can even omit the colour in your initializer
- 如何说服我的按钮单独更改颜色?
首先,您的视图有一个变量 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)
}
}
}
}