如何在 RxDataSource 中将新部分反映到 UICollectionView?
Posted
技术标签:
【中文标题】如何在 RxDataSource 中将新部分反映到 UICollectionView?【英文标题】:How to reflect the new section to UICollectionView in RxDataSource? 【发布时间】:2019-01-29 15:14:57 【问题描述】:我正在使用 VIPER 架构构建一个带有 RxSwift 和 RxDataSource 的 ios 应用程序。我想更改 UICollectionView 的内容,因为演示者的值发生变化(当用户在 searchBar 中输入一些字母时,collectionView 应该显示所有以字母开头的用户的个人资料),但它不能作为我想要它。
通过尝试debug()
函数,在我在searchBar 中输入一些字母后,ViewController 中presenter.section
的值(它保存了CollectionView 的内容)发生了变化。但是,collectionView 并未反映更改。
以下是代码的主要部分。
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)
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)]
我如何定义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 有 Presenter 的实例,而 Presenter 有一个名为 section
的实例。这个section
包含用户名以特定字母开头的用户列表。
你能帮帮我吗?如果我有任何不清楚的地方,请在评论中告诉我。
更新: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(....)])])
【问题讨论】:
如果您将debug()
调用放在绑定/驱动/订阅表视图本身的数据源之前。它的输出是什么?
Daniel T. 看起来self.dataSource
在我输入了一些字母后也没有改变,它被处理掉了。但是self.dataSource
跟UICollectionView的内容有关系吗?
如果驱动/绑定到你的数据源的链被释放,那么它就不能再发出新的值了。您的链中的 Observable 正在发送停止事件(完成/错误),或者正在从绑定/订阅/驱动器调用一次性的 dispose()。
抱歉self.dataSource
没有改变(因为它是let
)但是当我在ViewController 中将debug()
放在drive()
之前,新元素被发射了。你知道为什么 CollectionView 的内容没有改变吗?
我想我不是很清楚...在视图控制器中的self.presenter.section
行和.drive(self.searchedFriendsCollectionView.rx.items(dataSource: self.dataSource))
行之间放置一个.debug("values")
,并将该调试的输出发布到您的问题。
【参考方案1】:
我发现了你的问题。您从未将集合视图添加到主视图中。如果集合视图没有超级视图,或者它的大小为零,则它不会自行加载。
将这两行放在你的 viewDidLoad 中:
searchedFriendsCollectionView.frame = view.bounds
view.addSubview(searchedFriendsCollectionView)
当你在它的时候,你应该把这条线从你的配置闭包移到 viewDidLoad:
searchedFriendsCollectionView.register(SearchFriendsCell.self, forCellWithReuseIdentifier: "SearchFriendsCell")
没有理由每次系统请求一个单元格时都调用它。
【讨论】:
对不起,我忘了把代码放在问题中,但我使用 FlexLayout 设置了 CollectionView。无论如何,当我用你的代码替换 FlexLayout 代码时,情况看起来并没有改变。当我在init(nibname)
中放置断点时,每次我在搜索栏中输入字母时都会调用它。另外,cell.item
的值似乎已更新......也许问题出在SearchFriendsCell
所以我会寻找它。
是的,问题就在那里。对不起。你的回答对我有帮助,因为我对 rxswift 很着迷,直到我看到你的帖子才认为原因出在其他地方。非常感谢。【参考方案2】:
只是想知道您如何定义self.interactor.allUsers()
和allUsers.mapSections(query: text)
您是否致电onNext()
通知观察者序列中有一个新元素。在您映射部分或获取所有用户之后?
如果你真的想使用flatMap
。我认为您应该改用 flatMapLatest
参考: RxSwift map and flatMap difference
更新:在这种情况下,您需要在 mapSections
中创建一个新的 observable,它始终会生成 [SearchFriendsSection]
并将其发送给观察者
return Observable.create observer in
let filteredSections = ...
observer.on(.next(filteredSections))
observer.on(.completed)
return Disposables.create()
【讨论】:
感谢您的评论。我已经把mapSection()
的代码放在问题里了,请看一下!我认为元素是按顺序通知的。当我使用debug()
检查时,当我输入不同的字母时,ViewController 中的self.presenter.section
每次都会收到不同的元素。
在 mapSections 中,您只需使用 map 来转换 allUsers 发出的用户。您返回的可观察对象不会发出任何项目。您需要创建一个始终生成 [SearchFriendsSection] 的新可观察对象并将其发送给观察者。你可以在我更新的答案中看到这个例子,如果你打算这样做,你最好在 Interactor 中定义函数,比如findUsersBy()
是的,像你说的那样使用findUsersBy()
这样的方法肯定会更好。谢谢。但是,我认为它正在发布新项目。例如,如果我在 searchBar 上没有输入任何内容,元素为空,然后我在 searchBar 中输入“A”,则返回用户名为 Alex
的元素,self.presenter.section
根据debug()
接收该用户.这不意味着新元素被发射了吗?老实说,我对 RxSwift 有点陌生,所以也许这是一个简单的问题
有趣的是,如果你能收到这些物品。你怎么把你的debug()
,你是这样使用self.presenter.section.debug().bind _ in .disposed(by: disposeBag)
吗?但是,如果您的数据流没有任何问题,则根本原因应该是dataSource
。你如何定义数据源?
是的,我仍然使用该流程。我刚刚将定义数据源的代码放在问题中,所以请看一下!【参考方案3】:
好的,最后我解决了这个问题,这与 Rxswift 没有直接关系。我是用FlexLayout来布局一切的,在CollectionViewCell中,我没有把代码放在下面。
override func layoutSubviews()
super.layoutSubviews()
self.contentView.flex.layout()
没有这个代码就没有理由更新视图,因为没有这个代码 FlexLayout 不会执行布局。
【讨论】:
以上是关于如何在 RxDataSource 中将新部分反映到 UICollectionView?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 RxDatasource 在 UICollectionView 中创建具有多个标题的多个部分
如何解决气旋复杂性? - RxDataSource RxSwift - SwiftLint