gpt4 book ai didi

swift - 如果在给定时间间隔内收到来自给定可观察对象的新事件,则跳过源可观察对象中的事件

转载 作者:行者123 更新时间:2023-11-30 10:54:18 25 4
gpt4 key购买 nike

我正在尝试在 UIView 扩展上编写一个方法,它将观察给定 View 上的长按。我知道可以使用 UILongPressGestureRecognizer 来完成,但我真的想弄清楚这个问题并这样做。

我尝试使用 takeUntil 运算符,但它完成了一个可观察的操作,但我需要跳过该值并接收进一步的事件。

问题也可以转化为:如何省略 completed 事件并继续接收更多事件?

func observeLongPress(with minimumPressDuration: Double = 1) -> 
Observable<Void> {

let touchesBeganEvent = rx.methodInvoked(#selector(touchesBegan))
let touchesEndedEvents = [#selector(touchesEnded), #selector(touchesCancelled)]
.map(rx.methodInvoked)
let touchesEndedEvent = Observable.merge(touchesEndedEvents)
return touchesBeganEvent
.observeOn(MainScheduler.instance)
.delay(minimumPressDuration, scheduler: MainScheduler.instance)
.takeUntil(touchesEndedEvent)
.map { _ in }
}

这会起作用,但会完成整个序列(正如它预期的那样)。

答案似乎四处流传(就像往常一样),但几个小时后我决定问。 :)

更新

float 答案刚刚飞入内部(大约需要 15 分钟),但我仍然对答案感兴趣,因为也许我在这里缺少一些东西。


func observeLongPress(with minimumPressDuration: Double = 1) -> Observable<Void> {
let touchesBeganEvent = rx.methodInvoked(#selector(touchesBegan))
let touchesEndedEvents = [#selector(touchesEnded), #selector(touchesCancelled)]
.map(rx.methodInvoked)
let touchesEndedEvent = Observable.merge(touchesEndedEvents)

return touchesBeganEvent
.observeOn(MainScheduler.instance)
.flatMapLatest { _ -> Observable<Void> in
return Observable.just(())
.delay(minimumPressDuration, scheduler: MainScheduler.instance)
.takeUntil(touchesEndedEvent)
.void()
}
}

最佳答案

您更新的代码将不起作用。即使您没有从函数中发出已完成的事件,它仍然是从 takeUntil 发出的。因此该运算符将不再发出任何值。

也就是说,这个想法是可以实现的。既然你说你想学,那我写这篇文章的时候就讲一下我的整个思考过程。

首先让我们概述一下我们的输入和输出。对于输入,我们有两个 Observables 和一个持续时间,每当我们处理持续时间时,我们都需要一个调度程序。对于输出,我们只有一个 Observable。

所以函数声明如下所示:

func filterDuration(first: Observable<Void>, second: Observable<Void>, duration: TimeInterval, scheduler: SchedulerType) -> Observable<Void> {
// code goes here.
}

我们将比较两个可观察对象触发的时间,因此我们必须跟踪它:

let firstTime = first.map { scheduler.now }
let secondTime = second.map { scheduler.now }

既然我们正在比较它们,我们就必须以某种方式将它们结合起来。我们可以使用merge , combineLatest ,或zip ...

  • combineLatest每当任一 Observable 触发时都会触发,并为我们提供两个 Observable 的最新值。
  • zip将对来自两个可观察量的事件进行 1 对 1 的交配。这听起来很有趣,但如果其中一个可观察物比另一个可观察物发射得更频繁,就会崩溃,所以它看起来有点脆弱。
  • merge当其中任何一个触发时都会触发,因此我们需要以某种方式跟踪哪一个触发(可能使用枚举)。

让我们使用combineLatest :

Observable.combineLatest(firstTime, secondTime)

这将为我们提供 Observable<(RxTime, RxTime)>现在我们可以绘制两个时间并进行比较。这里的目标是返回 Bool如果第二次比第一次大超过duration,则为真。 .

    .map { arg -> Bool in
let (first, second) = arg
let tickDuration = second.timeIntervalSince(first)
return duration <= tickDuration
}

现在,每次两个输入中的任何一个触发时,上面都会触发,但我们只关心发出 true 的事件。这需要filter .

.filter { $0 } // since the wrapped value is a Bool, this will accomplish our goal.

现在我们的链正在发出 Bools,这将永远是 true,但我们想要一个 Void。我们直接扔掉这个值怎么样?

.map { _ in }

把它们放在一起,我们得到:

func filterDuration(first: Observable<Void>, second: Observable<Void>, duration: TimeInterval, scheduler: SchedulerType) -> Observable<Void> {

let firstTime = first.map { scheduler.now }
let secondTime = second.map { scheduler.now }

return Observable.combineLatest(firstTime, secondTime)
.map { arg -> Bool in
let (first, second) = arg
let tickDuration = second.timeIntervalSince(first)
return duration <= tickDuration
}
.filter { $0 }
.map { _ in }
}

上面隔离了我们的逻辑,并且并非偶然,很容易使用 RxTests 进行测试。现在我们可以将其包装到 UIView 中(这很难测试。)

func observeLongPress(with minimumPressDuration: Double = 1) -> Observable<Void> {

let touchesBeganEvent = rx.methodInvoked(#selector(touchesBegan)).map { _ in }
let touchesEndedEvents = [#selector(touchesEnded), #selector(touchesCancelled)]
.map(rx.methodInvoked)
let touchesEndedEvent = Observable.merge(touchesEndedEvents).map { _ in }
return filterDuration(first: touchesBeganEvent, second: touchesEndedEvent, duration: minimumPressDuration, scheduler: MainScheduler.instance)
}

给你。一个自定义运算符。

关于swift - 如果在给定时间间隔内收到来自给定可观察对象的新事件,则跳过源可观察对象中的事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54130564/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com