3

I am building an iOS app with RxSwift and RxDataSource using VIPER architecture. I want to change the content of the UICollectionView as the value of the presenter changes (as the user typed in some letters in the searchBar, the collectionView should show all users' profile that starts with the letters), but it doesn't work as I wanted it to.

By trying debug() function, the value of presenter.section in ViewController (which holds the content of the CollectionView) is changed after I typed in some letters in the searchBar. However, the collectionView is not reflecting the change. Here are the main parts of the code.

code of ViewController

override func viewDidLoad() {

    super.viewDidLoad()
    self.view.backgroundColor = .white

    self.presenter.section
        .drive(self.searchedFriendsCollectionView.rx.items(dataSource: self.dataSource))
        .disposed(by: self.disposeBag)

    self.searchedFriendsCollectoinView.rx
        .setDelegate(self)
        .disposed(by: self.disposeBag)
}

code of Presenter

init(view: SearchFriendsViewInterface, interactor: 
SearchFriendsInteractorInterface, wireframe: 
SearchFriendsWireframeInterface) {

    self.view = view
    self.interactor = interactor
    self.wireframe = wireframe

    let allUsers = self.interactor.allUsers().asObservable()

    self.view.searchText
        .debounce(0.5)
        .filterNil()
        .distinctUntilChanged()
        .filterEmpty()
        .asObservable()
        .flatMap { text -> Observable<[SearchFriendsSection]> in
            // get all users starting with "text"
            allUsers.mapSections(query: text)
        }
        .bind(to: self.section)
        .disposed(by: self.disposeBag)
} 

extension Observable where E == [User] {

fileprivate func mapSections(query: String) -> Observable<[SearchFriendsSection]> {

    return self.map {
            $0.filter { $0.userDisplayName.hasPrefix(query) || $0.username.hasPrefix(query) }
        }
        .map { users -> [SearchFriendsSection] in

            let items = users.map { user in
                SearchFriendsItem(icon: user.profileImageURL, displayName: user.userDisplayName)
            }
            return [SearchFriendsSection(items: items)]
        }
    }
}

How I defined dataSource

override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {

    self.searchedFriendsCollectoinView = UICollectionView(frame: .init(), collectionViewLayout: self.searchedFriendsCollectionViewFlowLayout)
    let dataSource = RxCollectionViewSectionedReloadDataSource<SearchFriendsSection> (configureCell: {(_, collectionView, indexPath, item) -> UICollectionViewCell in
        collectionView.register(SearchFriendsCell.self, forCellWithReuseIdentifier: "SearchFriendsCell")
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SearchFriendsCell", for: indexPath) as! SearchFriendsCell
        cell.item = item
        return cell
    })
    self.dataSource = dataSource
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
  • ViewController has the instance of Presenter, and the presenter has an instance called section. This section holds the list of the users whose username is starting with specific letters.

Could you please help me? If I have left anything unclear, please let me know in the comment.

Update: output of the debug()

2019-02-01 10:22:27.610: values -> subscribed
2019-02-01 10:22:27.611: values -> Event next([])
--after typed in some letters in the searchBar--
2019-02-01 10:22:41.494: values -> Event 
next([AppScreen.SearchFriendsSection(items: 
[AppScreen.SearchFriendsItem(....), 
AppScreen.SearchFriendsItem(....), 
AppScreen.SearchFriendsItem(....)])])
4

3 回答 3

2

我发现了你的问题。您从未将集合视图添加到主视图中。如果集合视图没有超级视图,或者它的大小为零,则它不会自行加载。

将这两行放在您的 viewDidLoad 中:

searchedFriendsCollectionView.frame = view.bounds
view.addSubview(searchedFriendsCollectionView)

当你在它的时候,你应该把这条线从你的配置闭包移到 viewDidLoad 中:

searchedFriendsCollectionView.register(SearchFriendsCell.self, forCellWithReuseIdentifier: "SearchFriendsCell")

每次系统请求一个单元格时都没有理由调用它。

于 2019-02-01T02:40:03.133 回答
1

只是想知道你如何定义self.interactor.allUsers()allUsers.mapSections(query: text)

你打电话onNext()通知观察者序列中有一个新元素吗?在您映射部分或获取所有用户之后?

如果你真的想使用flatMap. 我认为您应该改用 flatMapLatest

参考: RxSwift map 和 flatMap 的区别

更新:在这种情况下,您需要创建一个新的可观察对象,mapSections其中始终生成[SearchFriendsSection]并将其发送给观察者

 return Observable.create { observer in
        let filteredSections = ...
        observer.on(.next(filteredSections))
        observer.on(.completed)
        return Disposables.create()
    }
于 2019-01-29T19:19:17.793 回答
0

好的,最后我解决了这个问题,这与 Rxswift 没有直接关系。我是使用 FlexLayout 来布局所有内容,而在 CollectionViewCell 中,我没有将代码放在下面。

override func layoutSubviews() {

    super.layoutSubviews()
    self.contentView.flex.layout()
}

没有此代码就没有理由更新视图,因为 FlexLayout 没有此代码就不会执行布局。

于 2019-02-01T03:29:23.477 回答