使用 RxSwift 从 UITableViewCell 向 UIViewController 发送回调

Ani*_*ale 5 ios swift rx-swift

我有一个 UITableViewCell,其中我有 customContentView,我想向 viewController 发送回调。为此,我使用 RxSwift。

class GISThemeFormTableCell: UITableViewCell {

    @IBOutlet weak var customContentView: UIView!

    var index = -1

    var cellSelected: Observable<(Int, Bool)>?
    private var observer: AnyObserver<(Int, Bool)>?

    override func awakeFromNib() {
        super.awakeFromNib()

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.didSelectCell))
        customContentView.addGestureRecognizer(tapGesture)

        cellSelected = Observable<(Int, Bool)>.create { (observer) -> Disposable in
            self.observer = observer
            return Disposables.create()
        }
    }

    @objc private func didSelectCell() {
        let newImage = selectionImageView.image! == Images.uncheckedRound ? Images.checkedRound : Images.uncheckedRound
        selectionImageView.image = newImage
        observer?.onNext((index, selectionImageView.image! == Images.checkedRound))
    }
}

class GISFormListView: UIView {

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        switch indexPath.section {
        case 0:
            let cell = tableView.dequeueReusableCell(withIdentifier: "GISThemeFormTableCell", for: indexPath) as! GISThemeFormTableCell
            cell.index = indexPath.row
            cell.formTitle.text = presenter.getFormName(indexPath.row)

            cell.cellSelected?.subscribe { (event) in
                let index = event.element!.0
                let isSelected = event.element!.1

                print(index, isSelected)

            }.disposed(by: disposeBag)

            return cell

        case 1:
            let cell = tableView.dequeueReusableCell(withIdentifier: "LoadingTableViewCell", for: indexPath) as! LoadingTableViewCell
            return cell

        default:
            return UITableViewCell()
        }
    }
}

Run Code Online (Sandbox Code Playgroud)

在上面的代码中,我在awakeFromNib 方法中创建了Observable,并在那里初始化了观察者。一旦调用 didSelectCell 方法,我就会在观察者中传递单元格和 bool 的索引。

我只是想知道如果我不想使用闭包和委托,这是否是实现这一目标的正确方法。

Dan*_* T. 4

如果您有兴趣,这里有一个更完整的答案。我看到 @Erumaru 警告您有关单元格被多次打印的问题。如果您有足够的表单项,某些单元格被重复使用,并且您没有正确处理,就会发生这种情况,因此请记住这一点。

class GISThemeFormTableCell: UITableViewCell {

    @IBOutlet weak var formTitle: UILabel!
    @IBOutlet weak var selectionImageView: UIImageView!
    @IBOutlet weak var customContentView: UIView!

    var disposeBag = DisposeBag()

    private var tapGesture: UITapGestureRecognizer = UITapGestureRecognizer(target: nil, action: nil)

    override func awakeFromNib() {
        super.awakeFromNib()
        customContentView.addGestureRecognizer(tapGesture)
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        disposeBag = DisposeBag()
    }

    func configure(title: String, initial: Bool = false) -> Observable<Bool> {
        // you shouldn't be inspecting your views to figure out the state of your model. The code below avoids that by making the state its own thing.
        let state = tapGesture.rx.event
            .filter { $0.state == .ended }
            .scan(false) { current, _ in !current }
            .startWith(initial)

        state
            .map { $0 ? Images.checkedRound : Images.uncheckedRound }
            .bind(to: selectionImageView.rx.image)
            .disposed(by: disposeBag)

        return state
    }
}

class GISFormListView: UIView {

    var presenter: Presenter!

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        switch indexPath.section {
        case 0:
            let cell = tableView.dequeueReusableCell(withIdentifier: "GISThemeFormTableCell", for: indexPath) as! GISThemeFormTableCell
            // the cell doesn't need to know its row, because that information is needed here, and it's already here.
            cell.configure(title: presenter.getFormName(indexPath.row))
                .map { (indexPath.row, $0) }
                .subscribe(onNext: { index, isSelected in
                    print(index, isSelected)
                })
                .disposed(by: cell.disposeBag)

            return cell

        case 1:
            let cell = tableView.dequeueReusableCell(withIdentifier: "LoadingTableViewCell", for: indexPath) as! LoadingTableViewCell
            return cell

        default:
            return UITableViewCell()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)