我正在尝试创建一个按钮,根据任务的当前状态显示不同的图像/文本。
我发现下面的代码不能工作:
Button {
taskViewModel.completeTask(id: task.id)
} label: {
if task.taskCompleted {
Image(systemName: "square")
.foregroundStyle(.black)
.padding(10)
.background(Color(.white), in: RoundedRectangle(cornerRadius: 10))
} else {
Image(systemName: "checkmark.square")
.foregroundStyle(.black)
.padding(10)
.background(Color(.white), in: RoundedRectangle(cornerRadius: 10))
}
}
但这是可行的:
if task.taskCompleted {
Button {
taskViewModel.completeTask(id: task.id)
} label: {
Image(systemName: "checkmark.square")
.foregroundStyle(.black)
.padding(10)
.background(Color(.white), in: RoundedRectangle(cornerRadius: 10))
}
} else {
Button {
taskViewModel.completeTask(id: task.id)
} label: {
Image(systemName: "square")
.foregroundStyle(.black)
.padding(10)
.background(Color(.white), in: RoundedRectangle(cornerRadius: 10))
}
}
不起作用的代码要干净得多!肯定有比我在这里做的更好的方法吧?
不能工作的完整代码是:
func TasksView(completed: Bool)->some View {
LazyVStack(spacing: 20) {
if let tasks = taskViewModel.filteredTasks{
if tasks.isEmpty{
Text("No Tasks Today")
.offset(y: 100)
}else{
ForEach(tasks){ task in
if completed && task.taskCompleted || !task.taskCompleted {
HStack(alignment: .top, spacing: 20) {
VStack(spacing: 12) {
Circle()
.fill(taskViewModel.isCurrentHour(date: task.taskDate) ? .black : .clear)
.frame(width: 13, height: 13)
.background(
Circle()
.stroke(.black, lineWidth: 2)
.padding(-4)
)
.scaleEffect(taskViewModel.isCurrentHour(date: task.taskDate) ? 1.2 : 0.8)
Rectangle()
.fill(.black)
.frame(width: 3)
}
VStack {
HStack(alignment: .top, spacing: 10) {
VStack(alignment: .leading, spacing: 12) {
Text(task.taskTitle)
.font(.title2.bold())
Text(task.taskDescription)
.font(.callout)
.foregroundStyle(.secondary)
}
.headerLeading()
Text(task.taskDate.formatted(date: .omitted, time: .shortened))
}
HStack(spacing: 8) {
if taskViewModel.isCurrentHour(date: task.taskDate) {
HStack(spacing: 5) {
ForEach(1...3, id: .self) { user in
Image(systemName: "person")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 25, height: 25)
}
}
.headerLeading()
}
Button{
taskViewModel.completeTask(id: task.id)
} label: {
Image(systemName: task.taskCompleted ? "checkmark.square" : "square")
.foregroundStyle(.black)
.padding(10)
.background(Color(.white), in: RoundedRectangle(cornerRadius: 10))
}
Button{
taskViewModel.deleteTask(id: task.id)
} label: {
Image(systemName: "bin.xmark")
.foregroundStyle(.black)
.padding(10)
.background(Color(.white), in: RoundedRectangle(cornerRadius: 10))
}
}
.padding(.top)
.headerTrailing()
}
.padding(taskViewModel.isCurrentHour(date: task.taskDate) ? 15 : 0)
.padding(.bottom, taskViewModel.isCurrentHour(date: task.taskDate) ? 0 : 10)
.headerLeading()
.background(
Color(.black)
.cornerRadius(20)
.opacity(taskViewModel.isCurrentHour(date: task.taskDate) ? 1 : 0)
)
.foregroundColor(taskViewModel.isCurrentHour(date: task.taskDate) ? .white : .black)
}.headerLeading()
}
}
}
}else{
ProgressView()
.offset(y: 100)
}
}
.padding()
.padding(.top)
.onChange(of: taskViewModel.currentDay) { newDate in
taskViewModel.filterTodaysTasks()
}
}
当我尝试运行这个时,Xcode抛出以下错误:
The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
在对象属性的结果中调整视图的最佳方法通常是进行最小的必要调整,而不是更多的细节。在本例中,您可以将更改限制为图像的systemName
:
Button {
taskViewModel.completeTask(id: task.id)
} label: {
Image(systemName: task.taskCompleted ? "checkmark.square" : "square")
.foregroundStyle(.black)
.padding(10)
.background(Color(.white), in: RoundedRectangle(cornerRadius: 10))
}
更有可能发生的是task
没有向SwiftUI渲染系统发出改变的信号,所以在completeTask
运行之后,没有迹象表明视图需要重新评估。
对于SwiftUI,在第一个例子中按钮的标识没有改变。你可以通过添加一个显式的.id(…)
解决这个问题。例如:
Button {
taskViewModel.completeTask(id: task.id)
} label: {
if task.taskCompleted {
Image(systemName: "square")
.foregroundStyle(.black)
.padding(10)
.background(Color(.white), in: RoundedRectangle(cornerRadius: 10))
} else {
Image(systemName: "checkmark.square")
.foregroundStyle(.black)
.padding(10)
.background(Color(.white), in: RoundedRectangle(cornerRadius: 10))
}
}.id("(task.id)-(task.completed)")
只要任务的completed
发生变化,SwiftUI重新评估UI,它就会检测到按钮有不同的ID,然后用新图像重新渲染它。
根据任务的完成状态,有一种更好、更简洁的方法。
Button {
taskViewModel.completeTask(id: task.id)
} label: {
Image(systemName: task.taskCompleted ? "checkmark.square" : "square") // Change image based on state of the task completed
.foregroundStyle(.black)
.padding(10)
.background(Color(.white), in: RoundedRectangle(cornerRadius: 10))
}