【问题标题】:RXSwift with UITableView - How to change swipe action based on observable带有 UITableView 的 RXSwift - 如何根据 observable 更改滑动操作
【发布时间】:2021-08-27 13:34:02
【问题描述】:

我是 RXSwift 的新手,希望根据可观察的值来控制我的表格视图滑动操作。

我有一个变量 - Observable 并基于 if Product.isEnabled 我想显示“已售罄”或“有货”滑动操作。这是当前代码:

func tableView(
    _ tableView: UITableView,
    trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath
) -> UISwipeActionsConfiguration? {

    self.interactionService.prod
    let soldOutAction = UIContextualAction(
        style: .normal,
        title: "Sold Out"
    ) { [weak self] (_, _, completionHandler) in
        guard let self = self else { return }
        self.interactionService.products.map({
            $0[indexPath.row]
        })
        .take(1)
        .do(onNext: { [weak self] in
            guard let self = self else { return }
            self.analytics.userMarkedAsSoldOut(product: $0.productID)
        })
        .flatMapLatest({self.interactionService.disableProduct($0)})
        .subscribe(onCompleted: {
            completionHandler(true)
        })
        .disposed(by: self.disposeBag)
    }
    soldOutAction.backgroundColor = UIColor(red: 0.996,
                                            green: 0.09,
                                            blue: 0.478,
                                            alpha: 1)
    let config = UISwipeActionsConfiguration(actions: [soldOutAction])
    config.performsFirstActionWithFullSwipe = false
    return config
}

上面的代码工作正常。但我需要更新它,以便如果 products[indexPath.row].isEnabled -> 使用 soldOutAction,如果 !isEnabled,则使用新创建的 inStockAction。

如前所述,我对 RXSwift 很陌生,所以我不知道如何更改 RXSwift 语法以使用 soldOutAction 或 inStockAction。非常感谢您对此事的任何帮助。

【问题讨论】:

    标签: swift xcode rx-swift


    【解决方案1】:

    注释在代码中。

    class Example: UIViewController {
        var tableView = UITableView()
        let disposeBag = DisposeBag()
        var interactionService = InteractionService(products: .just([]))
        var analytics = Analytics()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // create a couple of subjects for dealing with the user selections.
            let inStockSubject = PublishSubject<Product>()
            let soldOutSubject = PublishSubject<Product>()
    
            // here we use a higher order function to create two functions that take a Product and return a swipe action configuration.
            let makeSoldOutAction = createAction(title: "Sold Out", color: #colorLiteral(red: 0.996, green: 0.09, blue: 0.478, alpha: 1.0), observer: soldOutSubject.asObserver())
            let makeInStockAction = createAction(title: "In Stock", color: #colorLiteral(red: 0.09, green: 0.996, blue: 0.478, alpha: 1.0), observer: inStockSubject.asObserver())
    
            // every time `products` emits a new value...
            interactionService.products
                // create a [IndexPath: UISwipeActionsConfiguration] dict with the correct swipe action for each product.
                .map { Dictionary(uniqueKeysWithValues: $0.enumerated().map { (IndexPath(row: $0.offset, section: 0), $0.element.isEnabled ? makeSoldOutAction($0.element) : makeInStockAction($0.element)) }) }
                // and bind it to the table view delegate.
                .bind(to: tableView.rx.trailingSwipeActionsConfigurationForRowAt)
                .disposed(by: disposeBag)
    
            // these are so we don't have to worry about capturing self in the closures below.
            let interactionService = self.interactionService
            let analytics = self.analytics
    
            // every time the user taps the sold out action, this will emit the product.
            soldOutSubject
                // disable the product and pass it down.
                .flatMapLatest { interactionService.disableProduct($0).map { [p = $0] in p } }
                // report the event to the analytics system.
                .subscribe(onNext: {
                    analytics.userMarkedAsSoldOut(product: $0.productID)
                })
                .disposed(by: disposeBag)
    
            // setup your inStock chain like the above.
        }
    }
    
    // this is a generic action configuration creator for products.
    func createAction(title: String, color: UIColor, observer: AnyObserver<Product>) -> (Product) -> UISwipeActionsConfiguration {
        { product in
            let action = UIContextualAction(style: .normal, title: title) { _, _, completionHandler in
                observer.onNext(product)
                completionHandler(true)
            }
            action.backgroundColor = color
            let config = UISwipeActionsConfiguration(actions: [action])
            config.performsFirstActionWithFullSwipe = false
            return config
        }
    }
    
    // put the below in a separate file so you can reuse it in other contexts.
    
    // this creates a delegate proxy class for the table view. Learn more in this article: https://danielt1263.medium.com/convert-a-swift-delegate-to-rxswift-observables-f52afe77f8d6
    class UITableViewDelegateProxy: DelegateProxy<UITableView, UITableViewDelegate>, DelegateProxyType, UITableViewDelegate {
    
        static func currentDelegate(for object: UITableView) -> UITableViewDelegate? {
            object.delegate
        }
    
        static func setCurrentDelegate(_ delegate: UITableViewDelegate?, to object: UITableView) {
            object.delegate = delegate
        }
    
        public static func registerKnownImplementations() {
            self.register { UITableViewDelegateProxy(parentObject: $0) }
        }
    
        init(parentObject: UITableView) {
            super.init(
                parentObject: parentObject,
                delegateProxy: UITableViewDelegateProxy.self
            )
        }
    
        func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
            relay.value[indexPath]
        }
    
        fileprivate let relay = BehaviorRelay<[IndexPath: UISwipeActionsConfiguration]>(value: [:])
    }
    
    extension Reactive where Base: UITableView {
        var delegate: UITableViewDelegateProxy {
            return UITableViewDelegateProxy.proxy(for: base)
        }
    
        var trailingSwipeActionsConfigurationForRowAt: Binder<[IndexPath: UISwipeActionsConfiguration]> {
            Binder(delegate) { del, value in
                del.relay.accept(value)
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多