Post

Replies

Boosts

Views

Activity

I have a question regarding UI hang issues when using CompositionalLayout. As the number of cells displayed on the screen increases, the UI becomes less responsive. Could you provide advice or solutions for optimizing performance in such cases?
We are currently combining AutoLayout and StackView to structure our cells, and we’re using UICollectionViewCompositionalLayout to set up sections. Additionally, we’re utilizing UICollectionViewDiffableDataSource to manage the data source. On the home screen, we display cells in a multi-section layout. In the main screen, we support infinite scrolling and use a paging method that fetches 10 items at a time from the API. As we fetch and render more data, the number of displayed cells increases. However, we’ve noticed that as the number of displayed cells grows, the UI freezes during the process of fetching and rendering more data. We suspect that the issue lies in the configuration of UICollectionViewCompositionalLayout. When data is fetched through paging, the data is applied to the CollectionViewDiffableDataSource, and during the process of displaying it on the screen, the method that configures the UICollectionViewCompositionalLayout layout is called. The problem here is that when 10 cells are displayed on the screen and we fetch more data through paging and add 10 more cells, the layout calculation is redone for all 20 cells. In other words, the UICollectionViewCompositionalLayout layout configuration occurs a total of 20 times. Because of this, it seems that the UI freezing issue occurs as the number of cells increases. What steps can we take to resolve this problem? We have attached the relevant code for your reference. private lazy var collectionView = UICollectionView( frame: .zero, collectionViewLayout: self.createCollectionViewLayout() ) private func createCollectionViewLayout() -> UICollectionViewLayout { let layout = UICollectionViewCompositionalLayout { [weak self] sectionIndex, _ in guard let self, self.dataSource.snapshot().sectionIdentifiers.isEmpty == false else { return LayoutProvider.emptySection() } private func applyRefreshSnapshot(with sections: [PlateViewSection]) { var snapshot = self.dataSource.snapshot() snapshot.deleteAllItems() snapshot.appendSections(sections) sections.forEach { section in snapshot.appendItems(section.items, toSection: section) } self.dataSource.apply(snapshot, animatingDifferences: false) } let sectionIdentifier = self.dataSource.snapshot().sectionIdentifiers[safe: sectionIndex] let analyticsScreen = self.payload.analyticsScreen switch sectionIdentifier?.module { case let .tabOutlined(_, reactor): if self.tabOutlinedView == nil { let tabOutlinedView = self.dependency.tabOutlinedViewFactory.create( payload: .init( reactor: reactor, collectionView: self.collectionView, analyticsScreen: analyticsScreen, currentDisplayDepthObserver: .init { event in guard let depth = event.element else { return } self.currentLayoutHeaderTabDepth = depth }, selectedTabItemObserver: .init { [weak self] event in guard let (tab, isPrimaryTabClick) = event.element else { return } var queryParams: [String: String]? if isPrimaryTabClick == false { guard let currentState = self?.reactor?.currentState else { return } let currentSelectedQueryParams = self?.dependency.userDefaults .dictionary(forKey: Keys.PlateParamsKeys.brandQueryParams) as? [String: String] queryParams = currentSelectedQueryParams ?? currentState.defaultQueryParams } self?.scrollCollectionViewToTop() self?.collectionView.viewWithTag( QueryToggleModuleCell.Metric.optionsMenuViewTag )?.removeFromSuperview() self?.reactor?.action.onNext(.refreshWithParams( tabParams: tab?.params, queryParams: queryParams )) } ) ) self.view.addSubview(tabOutlinedView) tabOutlinedView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() } self.view.layoutIfNeeded() self.tabOutlinedView = tabOutlinedView self.tabOutlinedView?.emitInitializeAction() } return LayoutProvider.emptySection() case let .carouselOneRowBrand(module, _): let section = self.dependency.layoutProvider.createLayoutSection(module: module) section.visibleItemsInvalidationHandler = { [weak self] _, _, _ in guard let self else { return } self.emitFetchLikedAction(module: module, sectionIndex: sectionIndex) } return section case let .queryToggle(module): return self.dependency.layoutProvider.createLayoutSection(module: module) case .loadingIndicator: return LayoutProvider.loadingIndicatorSection() case let .space(module): return self.dependency.layoutProvider.createLayoutSection(module: module) case let .noResult(module): return self.dependency.layoutProvider.createLayoutSection(module: module) case let .buttonViewAll(module): return self.dependency.layoutProvider.createLayoutSection(module: module) case .footer: return LayoutProvider.footerSection() default: return LayoutProvider.emptySection() } } return layout } private func applyAppendSnapshot(with sections: [PlateViewSection]) { var snapshot = self.dataSource.snapshot() // loadingIndicator section delete let sectionsToDelete = snapshot.sectionIdentifiers.filter { section in section.module == .loadingIndicator } snapshot.deleteSections(sectionsToDelete) snapshot.appendSections(sections) sections.forEach { section in snapshot.appendItems(section.items, toSection: section) } self.dataSource.apply(snapshot, animatingDifferences: false) }
3
0
191
Sep ’24