如何实现显示数字的无限滚动UITableView?
解决方案
核心问题分析
你的代码主要存在两个关键问题:
APICaller中使用无限while循环会导致线程卡死,永远无法执行到completion回调,因此界面无内容;同时分页请求错误返回固定的[0],而非新生成的数字。ViewController中初始数据请求放在viewDidLayoutSubviews中,会被多次触发导致重复加载;且分页时全量刷新表格,性能较差。
修改后的APICaller代码
class APICaller { private var counter = 0 // 定义每页加载的条目数量,可按需调整 private let pageSize = 10 var isPaginating = false func fetchData(pagination: Bool = false, completion: @escaping (Result<[Int], Error>) -> Void) { if pagination { isPaginating = true } // 模拟网络请求延迟,可按需调整时长 DispatchQueue.global().asyncAfter(deadline: .now() + (pagination ? 0.5 : 1), execute: { var newData = [Int]() // 生成固定数量的新数字,避免无限循环 for _ in 0..<self.pageSize { self.counter += 1 newData.append(self.counter) } completion(.success(newData)) if pagination { self.isPaginating = false } }) } }
修改后的ViewController代码
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UIScrollViewDelegate { private let apiCaller = APICaller() private let tableView: UITableView = { let tableView = UITableView(frame: .zero, style: .grouped) tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") return tableView }() private var data = [Int]() // 标记初始数据是否已加载,避免重复请求 private var hasLoadedInitialData = false override func viewDidLoad() { super.viewDidLoad() view.addSubview(tableView) tableView.dataSource = self tableView.delegate = self tableView.frame = view.bounds // 加载初始数据 loadInitialData() } private func loadInitialData() { guard !hasLoadedInitialData else { return } hasLoadedInitialData = true apiCaller.fetchData(pagination: false) { [weak self] result in switch result { case .success(let initialData): self?.data.append(contentsOf: initialData) DispatchQueue.main.async { self?.tableView.reloadData() } case .failure(_): break } } } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return data.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) cell.textLabel?.text = String(data[indexPath.row]) return cell } func scrollViewDidScroll(_ scrollView: UIScrollView) { let position = scrollView.contentOffset.y // 滚动到距离底部100px时触发分页加载 if position > (tableView.contentSize.height - 100 - scrollView.frame.size.height) { guard !apiCaller.isPaginating else { return } apiCaller.fetchData(pagination: true) { [weak self] result in switch result { case .success(let moreData): let startIndex = self?.data.count ?? 0 self?.data.append(contentsOf: moreData) // 仅插入新行,而非全量刷新,提升性能 DispatchQueue.main.async { let indexPaths = (startIndex..<(startIndex + moreData.count)).map { IndexPath(row: $0, section: 0) } self?.tableView.insertRows(at: indexPaths, with: .automatic) } case .failure(_): break } } } } }
关键修改说明
- APICaller:
- 移除无限
while循环,改用for循环生成固定数量的新数字,避免线程卡死。 - 统一初始加载和分页加载的返回逻辑,直接返回本次生成的连续数字。
- 移除无限
- ViewController:
- 将初始数据请求移至
viewDidLoad,并添加标记避免重复加载。 - 分页时使用
insertRows而非全量reloadData,提升滚动流畅度。
- 将初始数据请求移至
内容的提问来源于stack exchange,提问作者Mikl.hart
相关产品推荐
相关产品推荐

