我可以将调度程序指定为RunLoop.main
,但是我找不到一种本机的方式来提供关联的RunLoop.Mode
模式来接收来自发布者的元素。
为什么需要这样做:我正在从发布者更新一个tableView单元格,但是如果用户正在滚动,则UI不会更新,然后在用户交互或滚动停止时会立即更新。 This is a known behaviour for scrollViews,但我希望尽快显示我的内容,并且能够指定运行循环跟踪模式可以解决此问题。
组合API:我认为receive(on:options:)
方法have any matching options不能提供此功能。我在内部认为,如果我呼叫receive(on:RunLoop.main)
,则会呼叫RunLoop.main.perform { }
。此perform方法可以使用the mode as parameter,但是未对Combine API公开。
当前想法:要解决此问题,我可以自己执行perform操作,而不使用Combine 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?
}
}
答案 0 :(得分:2)
实际上,您所请求的是自定义Scheduler
,因为RunLoop是Scheduler
,并且以特定模式而不是.default
运行它,只是该调度程序的附加配置。 / p>
我认为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
}
答案 1 :(得分:0)
您可以将DispatchQueue.main
传递给receive(on:options:)
,因为DispatchQueue
也符合Scheduler
协议。
它以某种方式使滚动时传递事件。
类似以下内容:
cancellable = stringFuture.receive(on: DispatchQueue.main)
.sink { string in
cell.textLabel.text = string
}