我可以将调度程序指定为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
}