如何使用@Bindable 值动态更新 SwiftUI 视图
How to dynamically update a SwiftUI View with an @Bindable value
我有一个 SwiftUI ProgressBar
视图,它根据您作为可绑定参数输入的百分比值显示进度百分比。 progress percentage-value 是由一个ReminderHelper
class 计算的,它以两个Int 为参数,totalDays
和daysLeft
,输入到ReminderHelper 的值class 来自保存在 Core Data 中的 Reminder
对象。
由于对 SwiftUI/Combine、@Binding、@Published、@State 等的工作原理了解甚少,我对如何构建我的代码来完成此类事情感到非常困惑。
根据下面的代码,我希望看到两个提醒,Cut the Grass
在 20% 和 Power Wash Siding
在 50%。同样,确定总百分比进度的两个 Ints
来自 Core Data 中保存的 Reminder
对象,实际总百分比结果来自 RemindersHelper
class.
知道如何完成我上面描述的内容吗?
型号:
这保存在核心数据中。
class Reminder:Identifiable{
var name = ""
var totalDays = 0
var daysLeft = 0
init(name:String, totalDays:Int, daysLeft:Int){
self.name = name
self.totalDays = totalDays
self.daysLeft = daysLeft
}
}
帮手class
这需要负责计算将传递给 ProgressBar View 的总百分比
来自保存在 Core Data 中的 Reminder 对象。
class ReminderHelper:ObservableObject{
@Published var percentageLeft: Float = 0.80
func calculatePerentageLeft(daysLeft: Int, totalDays:Int)->Float{
percentageLeft = Float(daysLeft / totalDays)
return percentageLeft
}
}
内容视图:
在这里,我调用 calculatePerentageLeft
方法来准备 percentageLeft
属性,然后再显示 ProgressBar。这当然是行不通的。
我看到一个错误:
Static method 'buildBlock' requires that 'Float' conform to 'View'
struct ContentView: View {
var reminders = [Reminder(name: "Cut the Grass", totalDays: 50, daysLeft: 10),
Reminder(name: "Power Wash Siding", totalDays: 30, daysLeft: 15)]
@StateObject var reminderModel = ReminderHelper()
var body: some View {
List {
ForEach(reminders) { reminder in
HStack{
Text(reminder.name)
reminderModel.calculatePerentageLeft(daysLeft: reminder.daysLeft, totalDays: reminder.totalDays)
ProgressBar(progress: reminderModel.percentageLeft)
}
}
}
}
}
进度条视图
这是负责绘制和显示百分比值的视图。
struct ProgressBar: View {
@Binding var progress: Float
var body: some View {
ZStack {
Circle()
.stroke(lineWidth:5.0)
.opacity(0.3)
.foregroundColor(Color.orange)
Circle()
.trim(from: 0.0, to: CGFloat(min(self.progress, 1.0)))
.stroke(style: StrokeStyle(lineWidth: 5.0, lineCap: .round, lineJoin: .round))
.foregroundColor(Color.orange)
.rotationEffect(Angle(degrees: 270.0))
.animation(.linear, value: progress)
VStack{
Text(String(format: "%.0f %%", min(self.progress, 1.0)*100.0))
.font(.caption2)
}
}
}
}
错误出现是因为你的函数 return 是一个 Float 并且在像 HStack 这样的构建块中调用的每个函数都应该 return 一个视图。
您应该更改模型的设计以完成您想要的。
在您的模型中创建一个计算变量(这不会影响 Coredata)。
class Reminder:Identifiable{
var name = ""
var totalDays = 0
var daysLeft = 0
var percentageLeft: Float {
Float(daysLeft) / Float(totalDays)
}
init(name:String, totalDays:Int, daysLeft:Int){
self.name = name
self.totalDays = totalDays
self.daysLeft = daysLeft
}
}
然后用这个变量显示进度:
struct ContentView: View {
var reminders = [Reminder(name: "Cut the Grass", totalDays: 50, daysLeft: 10),
Reminder(name: "Power Wash Siding", totalDays: 30, daysLeft: 15)]
var body: some View {
List {
ForEach(reminders) { reminder in
HStack{
Text(reminder.name)
ProgressBar(progress: reminder.percentageLeft)
}
}
}
}
}
其他更改:
- 根据您的整体设计,您可能不再需要此处的 Viewmodel。
- 我将
Float(daysLeft / totalDays)
更改为 Float(dayLeft) / Float(totalDays)
因为第一个总是会产生 0
- 我把
@Bining var progress: Float
改成了var progress: Float
。你不需要在这里绑定。如果进度值在 ProgressView
内部发生更改并且需要传播到父级 ContentView
,则只需要一个
我有一个 SwiftUI ProgressBar
视图,它根据您作为可绑定参数输入的百分比值显示进度百分比。 progress percentage-value 是由一个ReminderHelper
class 计算的,它以两个Int 为参数,totalDays
和daysLeft
,输入到ReminderHelper 的值class 来自保存在 Core Data 中的 Reminder
对象。
由于对 SwiftUI/Combine、@Binding、@Published、@State 等的工作原理了解甚少,我对如何构建我的代码来完成此类事情感到非常困惑。
根据下面的代码,我希望看到两个提醒,Cut the Grass
在 20% 和 Power Wash Siding
在 50%。同样,确定总百分比进度的两个 Ints
来自 Core Data 中保存的 Reminder
对象,实际总百分比结果来自 RemindersHelper
class.
知道如何完成我上面描述的内容吗?
型号:
这保存在核心数据中。
class Reminder:Identifiable{
var name = ""
var totalDays = 0
var daysLeft = 0
init(name:String, totalDays:Int, daysLeft:Int){
self.name = name
self.totalDays = totalDays
self.daysLeft = daysLeft
}
}
帮手class
这需要负责计算将传递给 ProgressBar View 的总百分比 来自保存在 Core Data 中的 Reminder 对象。
class ReminderHelper:ObservableObject{
@Published var percentageLeft: Float = 0.80
func calculatePerentageLeft(daysLeft: Int, totalDays:Int)->Float{
percentageLeft = Float(daysLeft / totalDays)
return percentageLeft
}
}
内容视图:
在这里,我调用 calculatePerentageLeft
方法来准备 percentageLeft
属性,然后再显示 ProgressBar。这当然是行不通的。
我看到一个错误:
Static method 'buildBlock' requires that 'Float' conform to 'View'
struct ContentView: View {
var reminders = [Reminder(name: "Cut the Grass", totalDays: 50, daysLeft: 10),
Reminder(name: "Power Wash Siding", totalDays: 30, daysLeft: 15)]
@StateObject var reminderModel = ReminderHelper()
var body: some View {
List {
ForEach(reminders) { reminder in
HStack{
Text(reminder.name)
reminderModel.calculatePerentageLeft(daysLeft: reminder.daysLeft, totalDays: reminder.totalDays)
ProgressBar(progress: reminderModel.percentageLeft)
}
}
}
}
}
进度条视图
这是负责绘制和显示百分比值的视图。
struct ProgressBar: View {
@Binding var progress: Float
var body: some View {
ZStack {
Circle()
.stroke(lineWidth:5.0)
.opacity(0.3)
.foregroundColor(Color.orange)
Circle()
.trim(from: 0.0, to: CGFloat(min(self.progress, 1.0)))
.stroke(style: StrokeStyle(lineWidth: 5.0, lineCap: .round, lineJoin: .round))
.foregroundColor(Color.orange)
.rotationEffect(Angle(degrees: 270.0))
.animation(.linear, value: progress)
VStack{
Text(String(format: "%.0f %%", min(self.progress, 1.0)*100.0))
.font(.caption2)
}
}
}
}
错误出现是因为你的函数 return 是一个 Float 并且在像 HStack 这样的构建块中调用的每个函数都应该 return 一个视图。
您应该更改模型的设计以完成您想要的。
在您的模型中创建一个计算变量(这不会影响 Coredata)。
class Reminder:Identifiable{
var name = ""
var totalDays = 0
var daysLeft = 0
var percentageLeft: Float {
Float(daysLeft) / Float(totalDays)
}
init(name:String, totalDays:Int, daysLeft:Int){
self.name = name
self.totalDays = totalDays
self.daysLeft = daysLeft
}
}
然后用这个变量显示进度:
struct ContentView: View {
var reminders = [Reminder(name: "Cut the Grass", totalDays: 50, daysLeft: 10),
Reminder(name: "Power Wash Siding", totalDays: 30, daysLeft: 15)]
var body: some View {
List {
ForEach(reminders) { reminder in
HStack{
Text(reminder.name)
ProgressBar(progress: reminder.percentageLeft)
}
}
}
}
}
其他更改:
- 根据您的整体设计,您可能不再需要此处的 Viewmodel。
- 我将
Float(daysLeft / totalDays)
更改为Float(dayLeft) / Float(totalDays)
因为第一个总是会产生 0 - 我把
@Bining var progress: Float
改成了var progress: Float
。你不需要在这里绑定。如果进度值在ProgressView
内部发生更改并且需要传播到父级ContentView
,则只需要一个