UITableView分页加载时刷新闪屏问题求助
Hey there, let's tackle that annoying intermittent flicker you're seeing in your UITableView when loading more data or refreshing. I've run into similar head-scratchers before, and looking at your code, there are a few key areas we can adjust to smooth things out.
Key Issues in Your Current Implementation
First, let's break down why the flicker might be happening:
- Using
reloadData()for pagination: This forces the entire table to redraw every time you add new rows, which is inefficient and can cause layout jumps/flicker, especially if the table is scrolled down. - Timing of content offset handling: While you're saving and restoring the offset, wrapping
reloadData()with animation disabling might not be enough to prevent layout shifts, especially if there's any delay in the main queue dispatch. - Table header view changes: Toggling the header view when data loads can also trigger unexpected layout updates that contribute to flicker.
Step-by-Step Fixes
1. Replace Full Reload with Batch Row Insertions (For Pagination)
Instead of reloading the entire table when adding new data, use UITableView's batch update methods to only insert the new rows. This is much smoother and avoids full redraws.
2. Adjust Content Offset Handling
Save the content offset right before modifying your data source, and make sure you're calculating the correct index paths for the new rows. Also, avoid disabling animations globally unless necessary—batch updates handle this more gracefully.
3. Optimize Refresh and Header View Logic
For pull-to-refresh, after replacing your data source, you can scroll the table to the top (if that's the expected behavior) or ensure the reload doesn't cause unexpected shifts. Also, handle header view changes more cleanly to avoid layout jumps.
Modified Code Example
Here's how you can adjust your handleWSSuccessResponse function:
func handleWSSuccessResponse(_ arrRestaurant:[Restaurant], filterBy: filteringByValues) { // Save offset BEFORE modifying data source to get accurate position let offset = self.IBtblUserFeed.contentOffset print(arrRestaurant.count) DispatchQueue.main.async { if !self.arrRestaurants.isEmpty { if self.isFromRefresh { // Refresh: Replace data, reload, and optionally scroll to top self.arrRestaurants = arrRestaurant self.IBtblUserFeed.reloadData() // Optional: Scroll to top on refresh for better user experience self.IBtblUserFeed.setContentOffset(.zero, animated: true) } else { // Pagination: Insert new rows instead of full table reload let currentRowCount = self.arrRestaurants.count self.arrRestaurants.append(contentsOf: arrRestaurant) // Calculate index paths for the new rows let newIndexPaths = (currentRowCount..<self.arrRestaurants.count).map { IndexPath(row: $0, section: 0) } // Use batch updates to avoid full redraw UIView.performWithoutAnimation { self.IBtblUserFeed.beginUpdates() self.IBtblUserFeed.insertRows(at: newIndexPaths, with: .none) self.IBtblUserFeed.endUpdates() // Restore the saved offset to keep user's scroll position self.IBtblUserFeed.setContentOffset(offset, animated: false) } } // Update loading state if (arrRestaurant.count > 0 && arrRestaurant.count <= 10 && arrRestaurant.count % 10 != 0) || arrRestaurant.count == 0 { self.isDataLoading = false self.actInd.stopAnimating() } } else { // Initial load or empty state handling self.arrRestaurants = arrRestaurant // Handle header view with proper frame to avoid layout jumps if self.arrRestaurants.isEmpty { self.IBtblUserFeed.tableHeaderView = nil } else { if !filterBy.isFiltering { let headerView = self.header() // Calculate and set header frame explicitly let headerSize = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) headerView.frame = CGRect(x: 0, y: 0, width: self.IBtblUserFeed.bounds.width, height: headerSize.height) self.IBtblUserFeed.tableHeaderView = headerView } } self.IBtblUserFeed.tableHeaderView?.isHidden = self.arrRestaurants.isEmpty // Update loading state if arrRestaurant.count % 10 != 0 { self.isDataLoading = false self.actInd.stopAnimating() } self.reloadTableView() } } }
Additional Tips
- Ensure consistent row heights: If using dynamic row heights, make sure
estimatedHeightForRowAtis set accurately and cache heights if possible to avoid layout recalculations that cause flicker. - Check your activity indicator: Make sure
actIndis added to the table view or a static container—adding/removing it dynamically can interfere with table layout. - Validate
isFromRefreshstate: Double-check that this flag is set correctly before calling the function, as incorrect state can lead to unexpected refresh/pagination behavior.
内容的提问来源于stack exchange,提问作者Atpl Mohali

