有没有办法指定运行循环和模式以从发布者接收元素



我可以将调度程序指定为RunLoop.main,但我找不到提供关联的RunLoop.Mode模式以从发布者接收元素的本机方法。

为什么需要这个:我正在更新发布商的 tableView 单元格,但如果用户滚动,UI 不会更新,它会在用户交互或滚动停止后立即更新。这是 scrollView 的已知行为,但我希望我的内容尽快显示,并且能够指定运行循环跟踪模式可以解决此问题。

组合 API:我认为receive(on:options:)方法没有任何匹配选项来提供此功能。我认为在内部,如果我打电话给receive(on:RunLoop.main)那么RunLoop.main.perform { }就会被召唤。此执行方法可以将模式作为参数,但这不会向组合 API 公开。


当前的想法:为了解决这个问题,我可以自己执行操作,而不使用组合 API,所以而不是这样做:

cancellable = stringFuture.receive(on: RunLoop.main) // I cannot specify the mode here
.sink { string in
cell.textLabel.text = string
}

我可以这样做:

cancellable = stringFuture.sink { string in
RunLoop.main.perform(inModes: [RunLoop.Mode.common]) { // I can specify it here
cell.textLabel.text = string
}
}

但这并不理想。

理想的解决方案:我想知道如何将其包装到我自己的发布者函数实现中,以具有如下所示的内容:

cancellable = stringFuture.receive(on: RunLoop.main, inMode: RunLoop.Mode.common)
.sink { string in
cell.textLabel.text = string
}

如果这个函数的 API 可以是这样的:

extension Publisher {
public func receive(on runLoop: RunLoop, inMode: RunLoop.Mode) -> AnyPublisher<Future.Output, Future.Failure> {
// How to implement this?
}
}

实际上您请求的是自定义Scheduler,因为 RunLoop 是一个Scheduler,并且在特定模式下运行它,而不是.default,只是该调度程序的附加配置。

我认为Apple将在下一次更新中在其RunLoop调度程序中添加这种可能性,但是现在以下包装RunLoop的简单自定义调度程序对我有用。希望对您有所帮助。

用法:

.receive(on: MyScheduler(runLoop: RunLoop.main, modes: [RunLoop.Mode(rawValue: "myMode")]))

.delay(for: 10.0, scheduler: MyScheduler(runLoop: RunLoop.main, modes: [.common]))

调度程序代码:

struct MyScheduler: Scheduler {
var runLoop: RunLoop
var modes: [RunLoop.Mode] = [.default]
func schedule(after date: RunLoop.SchedulerTimeType, interval: RunLoop.SchedulerTimeType.Stride,
tolerance: RunLoop.SchedulerTimeType.Stride, options: Never?,
_ action: @escaping () -> Void) -> Cancellable {
let timer = Timer(fire: date.date, interval: interval.magnitude, repeats: true) { timer in
action()
}
for mode in modes {
runLoop.add(timer, forMode: mode)
}
return AnyCancellable {
timer.invalidate()
}
}
func schedule(after date: RunLoop.SchedulerTimeType, tolerance: RunLoop.SchedulerTimeType.Stride,
options: Never?, _ action: @escaping () -> Void) {
let timer = Timer(fire: date.date, interval: 0, repeats: false) { timer in
timer.invalidate()
action()
}
for mode in modes {
runLoop.add(timer, forMode: mode)
}
}
func schedule(options: Never?, _ action: @escaping () -> Void) {
runLoop.perform(inModes: modes, block: action)
}
var now: RunLoop.SchedulerTimeType { RunLoop.SchedulerTimeType(Date()) }
var minimumTolerance: RunLoop.SchedulerTimeType.Stride { RunLoop.SchedulerTimeType.Stride(0.1) }
typealias SchedulerTimeType = RunLoop.SchedulerTimeType
typealias SchedulerOptions = Never
}

您可以将DispatchQueue.main传递给receive(on:options:)DispatchQueue因为它也符合Scheduler协议。

不知何故,它会在滚动时传递事件。

如下所示:

cancellable = stringFuture.receive(on: DispatchQueue.main)
.sink { string in
cell.textLabel.text = string
}

相关内容

  • 没有找到相关文章

最新更新