【问题标题】:How to re-trigger function whenever a variable is updated每当更新变量时如何重新触发函数
【发布时间】:2020-11-29 13:40:51
【问题描述】:

更新

我正在使用 EventKit 从 IOS 日历中提取事件。工作得很好。

这是检索事件的类函数。

@ObservedObject var selectDate = datepicker() // does NOT update as expected.
func loadEvents(completion: @escaping (([EKEvent]?) -> Void)) {
    requestAccess(onGranted: {
        let startOfDay = Calendar.current.startOfDay(for: self.selectDate.selectedDate)
        let endOfDay = Calendar.current.startOfDay(for: self.selectDate.selectedDate.advanced(by: TimeInterval.day))
        let predicate = self.eventStore.predicateForEvents(withStart: startOfDay, end: endOfDay, calendars: Array(self.selectedCalendars ?? []))
        let events = self.eventStore.events(matching: predicate)

        completion(events)
        print("loadEvents triggered for \(self.selectDate.selectedDate)") // This ALWAYS returns the current date
    }) {
        completion(nil)
    }
}

我想根据所选日期更新结果。 所以我有一个日期选择器分配给我的主视图中的@ObservedObject var selectDate

@ObservedObject var selectDate = datepicker()
DatePicker(
    selection: $selectDate.selectedDate,
    in: dateClosedRange,
    displayedComponents: [.date, .hourAndMinute], label: { Text("Is hidden label") })
}

这个类充当中间人。 didSet 方法正在触发函数运行,由上面loadEvents func 中的打印语句确认。

class datepicker: ObservableObject {
    @Published var selectedDate: Date = Date() {
        didSet {
            print("class datepicker date changed to \(selectedDate)") // This returns the changed dates correctly
            EventsRepository.shared.loadAndUpdateEvents()
        }
    }
}

我也尝试过这样传递新日期。

let startOfDay = Calendar.current.startOfDay(for: datepicker.init().selectedDate)

只有相同的持久“当前日期”结果。

每当 datePicker 中的 selectDate.selectedDate 发生更改时,如何将新的 selectedDate 传递给函数?

目前它根本没有更新。始终只返回当天的事件。

Debug 中上述代码中包含的打印语句返回。

"class datepicker date changed to 2020-08-13 19:23:28 +0000" // YEP That's right. I used datePicker to select this date.
"loadEvents triggered for 2020-08-10 19:23:28 +0000" // NOT updated, ALWAYS shows current date/time as first run. 

所以看起来@Published 属性是一个常量,一旦设置就不能更改。有其他选择吗?

【问题讨论】:

    标签: swift swiftui eventkit


    【解决方案1】:

    我怎样才能重新触发 func loadEvents 每当 从 datePicker 中的 selectDate.selectedDate 改变了吗?

    你可以试试didSet:

    class datepicker: ObservableObject{
        @Published var selectedDate: Date = Date() {
            didSet {
                // loadEvents()
            }
        }
    }
    

    使用 Xcode 11.6、iOS 13.6 测试。


    编辑

    您似乎正在使用datepicker 对象的两个 实例。确保您在所有相关位置使用同一个实例

    这个对象只能被创建一次:

    @ObservedObject var selectDate = datepicker()
    

    然后传递到init中的其他地方。

    【讨论】:

    • 这绝对行不通。这里的实际类型是Published<>,即属性包装器,从技术上讲,它永远不会被再次设置。
    • @Procrastin8 不,你错了。请在操场上运行我的代码(你可以在didSetprint)看看会发生什么。我了解 Combine 方式对您来说可能看起来更好,但请不要在没有先测试之前批评我的解决方案。
    • 我可以通过打印语句确认didSetmethod 正在触发函数运行。但是新选择的日期并没有传递给这些函数。所以看起来Published 就像@Procrastin8 建议的那样是一次性的。我已经查看了另一个建议的解决方案,但未能正确实施(我是个新手,所以这肯定是我的失败)但是由于didSet 正在完成触发该功能的工作,剩下的唯一问题是通过新日期。除了Pubished,还有其他选择吗?
    • @My4Paws 很难准确地说出你在做什么。您可以尝试将selectedDate 作为参数传递给loadEvents
    • @pawello2222 谢谢,我已经更新了问题以更好地展示正在发生的事情。
    【解决方案2】:

    一种方法是在创建模型对象时实际订阅您自己的发布者。

    class DateManager: ObservableObject {
      @Published var selectedDate: Date = .init()
    
      private var bag: Set<AnyCancellable> = []
    
      init() {
        $selectedDate // $ prefix gets the projected value, a `Publisher` from `Published<>`
          .receive(on: DispatchQueue.main)
          .sink { [weak self] selectedDate in
            self?.loadEvents(date: selectedDate)
          }
          .store(in: &bag)
      }
    }
    

    这将订阅该发布者并在其更改时触发您的 API 调用。如果用户快速更改日期等,您可以花哨并添加debounce 或小延迟等。您现在拥有组合来控制此数据流的全部功能。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-08-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-09
      • 1970-01-01
      • 2016-04-17
      相关资源
      最近更新 更多