在 UICollectionViewCell 中为 UIButton 设置 rx.tap 的问题 - RxSwift 3
Posted
技术标签:
【中文标题】在 UICollectionViewCell 中为 UIButton 设置 rx.tap 的问题 - RxSwift 3【英文标题】:issue with having rx.tap for UIButton in UICollectionViewCell - RxSwift 3 【发布时间】:2017-04-04 12:09:49 【问题描述】:我为 1 个 UIButton 订阅了 2 次:
-
首次订阅,每次点击都会更新 UI
二次订阅,累计点击后每1秒更新一次Web Service上的值。
代码:
class ProductionSize
var id : Int?
var size: Int = 0
var name: String = ""
class ProductionCell: UICollectionViewCell
var rxBag = DisposeBag()
// this will be set in the (cellForItemAt indexPath: IndexPath) of collection view
var productionSize: ProductionSize?
didSet
showProductionSize()
prepareButton()
func showProductionSize()
// ... code for showing ProductionSize in labels
func prepareButton()
// This for subscribing for every click for displaying purpose
btn_increase.rx.tap
.subscribe()event in
self.increaseClicked()
.addDisposableTo(rxBag)
// this for subscribing for sending webservice request after 1 second of clicking the button (so that if user click it quickly i send only last request)
btn_increase.rx.tap
.debounce(1.0, scheduler: MainScheduler.instance)
.subscribe() event in self.updateOnWS()
.addDisposableTo(rxBag)
func increaseClicked()
productionSize.size = productionSize.size + 1
showProductionSize()
func updateOnWS()
// code for updating on webservice with Moya, RxSwift and Alamofire§
// when scrolling it gets called to dispose subscribtions
override func prepareForReuse()
rxBag = DisposeBag()
问题:
由于处理发生在prepareForReuse()
,如果我多次单击按钮并立即滚动,webservice 调用将被处理而不更新。
我尝试过的:
在父 ViewController DisposableBag 中添加了addDisposableTo(vc?.rx_disposableBag)
。
问题,订阅累积,每次点击updateWS()
都会被调用很多次,每次滚动都订阅了,从未被释放。
我已尝试从prepareForReuse()
中删除disposableBag 重新初始化。
问题,再次重复和累积按钮的订阅,并且每次点击都会调用许多 Web 服务调用。
问题:
我怎样才能让debounce
订阅被调用到最后并且从不重复多个订阅(如果是addDisposableTo
viewController Bag)?
【问题讨论】:
不确定您的情况,所以请澄清一下:您的updateOnWS
中是否使用了productionSize
?这样用户可以多次点击按钮,然后当他们停止点击时,数字(即点击5次)5用于网络呼叫?当您向下滚动并处置单元格时,行为应该是什么?网络请求应该被处理还是应该仍然执行?生产尺寸 UI 应该在用户点击时更新,还是在网络请求成功响应时更新?
另外,请注意在闭包中使用self
的方式,这可能会导致内存泄漏,尤其是在UITableViewCell
s 或UICollectionViewCell
s 中时。它可以防止对象被丢弃。见***.com/questions/40583685/…
@iwillnot 我不会在每次点击时更新 productionSize,以便在 web 服务调用时,我将获取最后一个 productionSize 值并将其发送到 updateOnWS 中。如果点击 5 次,它只会在网络上更新 1 次。我不等待响应,为了更好的用户体验,我会显示 ui 更新它们,如果请求成功完成,我会更新它
向下滚动并处理单元格时的行为应该是什么;网络请求应该被处理还是应该仍然执行?
@iwillnot 该请求应该被执行
【参考方案1】:
由于prepareButton()
总是在集合视图的 (cellForItemAt indexPath: IndexPath) 中被调用,你可以试试这个:
func prepareButton()
self.rxBag = nil
let rxBag = DisposeBag()
// This for subscribing for every click for displaying purpose
btn_increase.rx.tap
.subscribe(onNext: [weak self] _ in
self?.increaseClicked()
)
.addDisposableTo(rxBag)
// this for subscribing for sending webservice request after 1 second of clicking the button (so that if user click it quickly i send only last request)
btn_increase.rx.tap
.debounce(1.0, scheduler: MainScheduler.instance)
.subscribe(onNext: [weak self] _ in
self?.updateOnWS()
)
.addDisposableTo(rxBag)
self.rxBag = rxBag
删除prepareForReuse()
实现。
【讨论】:
感谢您的回答。看起来您的代码与 prepareForReuse() 完全相同,因为每次滚动重用单元格时都会调用 prepareButton,因此它将在完成之前处理请求。无论如何我会测试它并再次分享结果 再次,如果我单击按钮并立即滚动而无需等待去抖动时间(1 秒),请求就会被取消。【参考方案2】:在父 ViewController DisposableBag 中添加了
addDisposableTo(vc?.rx_disposableBag)
。问题是,订阅的累积和每次点击都会调用 updateWS() 多次,每次滚动都会订阅并且从未处理过。
由于您订阅按钮的点击方式,您的self.updateOnWS()
可能会被多次调用。
btn_increase.rx.tap
.debounce(1.0, scheduler: MainScheduler.instance)
.subscribe() event in self.updateOnWS()
.addDisposableTo(rxBag)
如您所见,您使用subscribe()
方法订阅所有事件。这意味着所有 Rx 事件(onNext
、onError
、onCompleted
、onSubscribed
和 onDisposed
)都会触发 self.updateOnWS()
。您可以通过打印出event
对象来检查是否是这种情况,以查看触发了什么事件。
仅订阅onNext
一个可能的解决方法是只订阅onNext
操作。
btn_increase.rx.tap
.debounce(1.0, scheduler: MainScheduler.instance)
.subscribe(onNext: [weak self] (_ : Void) in
self?.updateOnWS()
)
.addDisposableTo(vc?.rxdisposableBag)
通过使用视图控制器的DisposeBag
,即使单元格被释放(向下滚动时),您也可以确保操作仍然继续。但是,如果您需要在处置单元格时处置订阅,请使用单元格的DisposeBag
,而不是视图控制器。
旁注 - 内存泄漏
注意self
的引用被指定为弱引用,这样可以防止发生内存泄漏。通过将其指定为弱,它将为您提供对 self 的引用,该引用是可选的。
如果不这样做,您为 onNext
块创建的闭包将保留对 self
的强引用,即您的 UICollectionViewCell
,而后者又拥有我们正在讨论的闭包。
这最终会导致内存不足崩溃。请参阅我在您的问题的 cmets 上发布的参考资料,了解更多关于错误引用 self 导致的内存泄漏的信息。
【讨论】:
非常感谢您的回答。很高兴知道内存泄漏,但是当我订阅.addDisposableTo(vc?.rxdisposableBag)
时出现了大问题。旧订阅不会在滚动上处理它会累积许多订阅和许多对 web 服务实施的调用。
我们可以在不取消旧调用的情况下进行处理吗?
您可以通过在订阅对象上调用dispose
来手动处理您的订阅。或者有一个DisposeBag
,您可以重新实例化以模拟处置。
这个问题的另一种解决方案是什么?
我会考虑使用debounce
、throttle
运算符,甚至可能查看buffer
和filter
条件count > 0
是否得到我想要的行为。任何可能减少订阅代码多次运行的东西。对于旧订阅的处置,我会使用一个单独的处置袋,我手动清理或考虑使用 `flatMapLatest
运算符。如果答案有帮助,请将其标记为已接受。 :)以上是关于在 UICollectionViewCell 中为 UIButton 设置 rx.tap 的问题 - RxSwift 3的主要内容,如果未能解决你的问题,请参考以下文章
我如何在 UITableViewCell 或 UICollectionViewCell 中为 UIButton 编写一个扩展,它只影响它所在的单元格?
在 UICollectionViewCell 的情况下无法同时满足约束
如何在 CollectionViewController 中为 gif 设置动画.. (Objective-C)