RxSwift - 如何将项目的数量反映到 TableView

RxSwift - How to reflect the number of item's count to TableView

我是 RxSwift 的新手。这很棘手。

我创建的 ToDoList 视图是 tableView 和 add-item 视图,由 TabBarController 分隔。 我已成功显示列表数组并在 tableView 中添加了一个新项目。

我还想在具有 tableView 的视图中显示数组的计数和收藏计数,以便我通过 .just.

抛出一个值来显示它

但是根据SearchBar显示的数组结果显示一个值,这个值并没有像我预期的那样反映出来。

在 MainViewModel 中,我确定我是否可以通过 print 正确获取数组的计数,但显然该值很好。 它只是没有反映在视图中。

//型号

struct Item: Codable {
    var name = String()
    var detail = String()
    var tag = String()
    var memo = String()
    var fav = Bool()
    var cellNo = Int()

    init(name: String, detail: String, tag: String, memo: String, fav: Bool, celllNo: Int) {
        self.name = name
        self.detail = detail
        self.tag = tag
        self.memo = memo
        self.fav = fav
        self.cellNo = celllNo
    }

    init() {
        self.init(
            name: "Apple",
            detail: "ringo",
            tag: "noun",
            memo: "",
            fav: false,
            celllNo: 0
        )
    }
}




struct SectionModel: Codable {
    var list: [Item]
}
extension SectionModel: SectionModelType {
    var items: [Item] {
        return list
    }

    init(original: SectionModel, items: [Item]) {
        self = original
        self.list = items
    }

}

单例份额class

final class Sharing {

    static let shared = Sharing()

    var items: [Item] = [Item()]

    var list: [SectionModel] = [SectionModel(list: [Item()])] {
        didSet {
            UserDefault.shared.saveList(list: list)
        }
    }

    let listItems = BehaviorRelay<[SectionModel]>(value: [])

}

extension Sharing {
    func calcFavCount(array: [Item]) -> Int {
        var count = 0
        if array.count > 0 {
            for i in 0...array.count - 1 {
                if array[i].fav {
                    count += 1
                }
            }
        }
        return count
    }
}

// MainTabViewController

class MainTabViewController: UIViewController {

    @IBOutlet weak var listTextField: UITextField!
    @IBOutlet weak var tagTextField: UITextField!
    @IBOutlet weak var itemCountLabel: UILabel!
    @IBOutlet weak var favCountLabel: UILabel!
    @IBOutlet weak var favIcon: UIImageView!
    @IBOutlet weak var infoButton: UIButton!
    @IBOutlet weak var searchBar: UISearchBar!
    @IBOutlet weak var tableView: UITableView!

    private lazy var viewModel = MainTabViewModel(
        searchTextObservable: searchTextObservable
    )

    private let disposeBag = DisposeBag()
    private var dataSource: RxTableViewSectionedReloadDataSource<SectionModel>!

    override func viewDidLoad() {
        super.viewDidLoad()
        setupTableViewDataSource()
        tableViewSetup()
        listDetailSetup()
    }

    // create Observable searchBar.text to pass to ViewModel
    var searchTextObservable: Observable<String> {

        let debounceValue = 200

        // observable to get the incremental search text
        let incrementalTextObservable = rx
            .methodInvoked(#selector(UISearchBarDelegate.searchBar(_:shouldChangeTextIn:replacementText:)))
            .debounce(.milliseconds(debounceValue), scheduler: MainScheduler.instance)
            .flatMap { [unowned self] _ in Observable.just(self.searchBar.text ?? "") }

        // observable to get the text when the clear button or enter are tapped
        let textObservable = searchBar.rx.text.orEmpty.asObservable()

        // merge these two above
        let searchTextObservable = Observable.merge(incrementalTextObservable, textObservable)
            .skip(1)
            .debounce(.milliseconds(debounceValue), scheduler: MainScheduler.instance)
            .distinctUntilChanged()

        return searchTextObservable
    }

    func setupTableViewDataSource() {
        dataSource = RxTableViewSectionedReloadDataSource<SectionModel>(configureCell: {(_, tableView, indexPath, item) in
            let cell = tableView.dequeueReusableCell(withIdentifier: "ListCell") as! ListCell
            cell.selectionStyle = .none
            cell.backgroundColor = .clear
            cell.configure(item: item)
            return cell
        })
    }

    func tableViewSetup() {

        tableView.rx.itemDeleted
            .subscribe {
                print("delete")
            }
            .disposed(by: disposeBag)

        viewModel.dispItems.asObservable()
            .bind(to: tableView.rx.items(dataSource: dataSource))
            .disposed(by: disposeBag)
    }

    func listDetailSetup() {

        viewModel.itemCountObservable
            .bind(to: itemCountLabel.rx.text)
            .disposed(by: disposeBag)

        viewModel.favCountObservable
            .bind(to: favCountLabel.rx.text)
            .disposed(by: disposeBag)
    }
}

MainTabViewModel

final class MainTabViewModel {

    private let disposeBag = DisposeBag()
    private let userDefault: UserDefaultManager
    var dispItems = BehaviorRelay<[SectionModel]>(value: [])
    private let shared = Sharing.shared

//    lazy var itemCount = shared.list[0].list.count
//    lazy var favCount = shared.calcFavCount

    var itemCountObservable: Observable<String>
    var favCountObservable: Observable<String>

    init(searchTextObservable: Observable<String>,
         userDefault: UserDefaultManager = UserDefault()) {

        self.userDefault = userDefault

        let initialValue = shared.list
        shared.listItems.accept(initialValue)
        dispItems = shared.listItems

       // this part is to display the initil number -> success
        var itemCount = shared.list[0].list.count
        itemCountObservable = .just(itemCount.description + " items")

        var favCount = shared.calcFavCount(array: shared.list[0].list)
        favCountObservable = .just(favCount.description)


        // this part is based on the searching result -> failure
        searchTextObservable.subscribe(onNext: { text in
            if text.isEmpty {
                let initialValue = self.shared.list
                self.shared.listItems.accept(initialValue)
                self.dispItems = self.shared.listItems
            }else{
                let filteredItems: [Item] = self.shared.list[0].list.filter {
                    [=13=].name.contains(text)
                }
                let filteredList = [SectionModel(list: filteredItems)]
                self.shared.listItems.accept(filteredList)
                self.dispItems = self.shared.listItems

                itemCount = filteredItems.count
                self.itemCountObservable = .just(itemCount.description + " items")
                favCount = self.shared.calcFavCount(array: filteredItems)
                self.favCountObservable = .just(favCount.description)

                print("\(itemCount) items") // the ideal number is in but not shown in the view
            }
        })
        .disposed(by: disposeBag)

    }

}

我删除了不必要的代码,但我主要粘贴了整个代码以供您理解。

希望你能帮助我。

谢谢。

我还是解决了这个问题;价值体现出来了。

问题是 itemCountObservable 被声明为 observable 并且使用了 .just

.just的工作原理是抛出onNext一次就完成了,这意味着我在searchTextObservable.subscribe(onNext~中所做的更改是不可接受的。

所以我把 itemCountObservable: Observable<String> 改成 BehaviorRelay<String> 只有 onNext 被抛出并且没有完成,然后就可以了。

我对这个问题的理解是 itemCountObservable: Observable<String> 由于 .just 而停止抛出一个值,正如我在上面写的那样。

我说的对吗??

如果您熟悉 ObservableBehaviorRelay 之间的区别,请告诉我。

谢谢。