SwiftUI:更新由其他@State 变量组成的@Binding 变量的最佳方式
SwiftUI: Best way to update a @Binding variable made up of other @State variables
我已经 运行 进入我在 SwiftUI 中的第一个复杂控件。它是一个日期频率选择器,用户可以在其中为每周重复发生的事件选择星期几,或者为每月重复发生的事件选择每月的某一天。我有 @State 变量来跟踪我的段控制器中的每周和每月,以及用户选择的一周或一个月的哪一天。当这些值发生变化时,我想更新我的@Binding 频率。下面的代码有效,但我觉得更新 body 调用中的频率很脏。
enum Frequency {
case weekly(on: Int)
case monthly(on: Int)
}
struct FrequencyView : View {
@Binding var frequency: Frequency
@State var segment: Int
@State var weekDay: Int
@State var monthDay: Int
var displayFrequency: Frequency {
if segment == 0 {
return .weekly(on: weekDay)
} else {
return .monthly(on: monthDay)
}
}
var body: some View {
frequency = displayFrequency
return NavigationView {
VStack {
Text(displayFrequency.long).fontWeight(.bold)
SegmentedControl(selection: $segment) {
Text("Weekly").tag(0)
Text("Monthly").tag(1)
}.padding()
if segment == 0 {
Text("Day of the week").foregroundColor(Acorns.stone)
ExpensesWeeklyPickerView(day: $weekDay).padding()
} else {
Text("Day of the month").foregroundColor(Acorns.stone)
ExpensesMonthlyPickerView(day: $monthDay).padding()
}
Spacer()
}.navigationBarTitle(Text("Frequency"))
}
}
}
想知道是否有人找到了更好的方法来做到这一点?
首先,将 weekDay
和 monthDay
的访问器添加到 Frequency
:
extension Frequency {
var weekDay: Int {
get {
if case .weekly(on: let day) = self { return day }
else { return 0 }
}
set { self = .weekly(on: newValue) }
}
var monthDay: Int {
get {
if case .monthly(on: let day) = self { return day }
else { return 0 }
}
set { self = .monthly(on: newValue) }
}
}
现在您可以使用这些访问器为您的子视图创建绑定:
if segment == 0 {
Text("Day of the week").foregroundColor(Acorns.stone)
ExpensesWeeklyPickerView(day: $frequency.weekDay).padding()
} else {
Text("Day of the month").foregroundColor(Acorns.stone)
ExpensesMonthlyPickerView(day: $frequency.monthDay).padding()
}
要在单击片段时更改频率,您需要为 SegmentedControl
提供更改频率的绑定。我会介绍另一个(私人)enum
用于段:
fileprivate enum Segment: Int, Hashable {
case weekly
case monthly
}
然后在Frequency
中添加扩展名来翻译to/from Segment
:
extension Frequency {
fileprivate var segment: ContentView.Segment {
get {
switch self {
case .weekly(on: _): return .weekly
case .monthly(on: _): return .monthly
}
}
set {
switch newValue {
case .weekly: self = .weekly(on: 0)
case .monthly: self = .monthly(on: 0)
}
}
}
}
最后,摆脱你的 segment
属性 并使用 frequency.segment
代替:
SegmentedControl(selection: $frequency.segment) {
Text("Weekly").tag(Segment.weekly)
Text("Monthly").tag(Segment.monthly)
}.padding()
if frequency.segment == .weekly {
...
我已经 运行 进入我在 SwiftUI 中的第一个复杂控件。它是一个日期频率选择器,用户可以在其中为每周重复发生的事件选择星期几,或者为每月重复发生的事件选择每月的某一天。我有 @State 变量来跟踪我的段控制器中的每周和每月,以及用户选择的一周或一个月的哪一天。当这些值发生变化时,我想更新我的@Binding 频率。下面的代码有效,但我觉得更新 body 调用中的频率很脏。
enum Frequency {
case weekly(on: Int)
case monthly(on: Int)
}
struct FrequencyView : View {
@Binding var frequency: Frequency
@State var segment: Int
@State var weekDay: Int
@State var monthDay: Int
var displayFrequency: Frequency {
if segment == 0 {
return .weekly(on: weekDay)
} else {
return .monthly(on: monthDay)
}
}
var body: some View {
frequency = displayFrequency
return NavigationView {
VStack {
Text(displayFrequency.long).fontWeight(.bold)
SegmentedControl(selection: $segment) {
Text("Weekly").tag(0)
Text("Monthly").tag(1)
}.padding()
if segment == 0 {
Text("Day of the week").foregroundColor(Acorns.stone)
ExpensesWeeklyPickerView(day: $weekDay).padding()
} else {
Text("Day of the month").foregroundColor(Acorns.stone)
ExpensesMonthlyPickerView(day: $monthDay).padding()
}
Spacer()
}.navigationBarTitle(Text("Frequency"))
}
}
}
想知道是否有人找到了更好的方法来做到这一点?
首先,将 weekDay
和 monthDay
的访问器添加到 Frequency
:
extension Frequency {
var weekDay: Int {
get {
if case .weekly(on: let day) = self { return day }
else { return 0 }
}
set { self = .weekly(on: newValue) }
}
var monthDay: Int {
get {
if case .monthly(on: let day) = self { return day }
else { return 0 }
}
set { self = .monthly(on: newValue) }
}
}
现在您可以使用这些访问器为您的子视图创建绑定:
if segment == 0 {
Text("Day of the week").foregroundColor(Acorns.stone)
ExpensesWeeklyPickerView(day: $frequency.weekDay).padding()
} else {
Text("Day of the month").foregroundColor(Acorns.stone)
ExpensesMonthlyPickerView(day: $frequency.monthDay).padding()
}
要在单击片段时更改频率,您需要为 SegmentedControl
提供更改频率的绑定。我会介绍另一个(私人)enum
用于段:
fileprivate enum Segment: Int, Hashable {
case weekly
case monthly
}
然后在Frequency
中添加扩展名来翻译to/from Segment
:
extension Frequency {
fileprivate var segment: ContentView.Segment {
get {
switch self {
case .weekly(on: _): return .weekly
case .monthly(on: _): return .monthly
}
}
set {
switch newValue {
case .weekly: self = .weekly(on: 0)
case .monthly: self = .monthly(on: 0)
}
}
}
}
最后,摆脱你的 segment
属性 并使用 frequency.segment
代替:
SegmentedControl(selection: $frequency.segment) {
Text("Weekly").tag(Segment.weekly)
Text("Monthly").tag(Segment.monthly)
}.padding()
if frequency.segment == .weekly {
...