SwiftUI:更新由其他@State变量组成的@Binding变量的最佳方法

时间:2019-07-10 15:11:06

标签: swift swiftui

我遇到了SwiftUI中的第一个复杂控件。这是一个日期频率选择器,用户可以在其中选择每周定期事件的星期几,还是每月定期事件的月几。我有@State变量来跟踪我的细分控制器中的每周vs每月,以及用户选择在一周中的哪几日。当这些值更改时,我想更新@Binding频率。下面的代码可以正常工作,但是让我感觉不舒服,无法更新正文调用中的频率。

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

好奇是否有人找到了更好的方法?

1 个答案:

答案 0 :(得分:1)

首先,将weekDaymonthDay的访问器添加到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上添加扩展名以与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 {
           ...