TableView下拉加载:保持下拉状态并显示加载指示器实现方案咨询
TableView下拉刷新保持状态并显示加载Spinner的最优实现
针对你说的需求——下拉TableView后保持展开的空白区域并显示加载Spinner,这里有两种实用的实现方案,根据你的场景选择即可:
方案一:定制系统UIRefreshControl(iOS原生场景首选)
系统自带的UIRefreshControl默认会在刷新开始后自动收起,我们只要修改它的行为,再替换指示器就能搞定:
- 写个
UIRefreshControl的子类,重写endRefreshing方法,不让它自动收起,留到数据加载完再手动触发。 - 把系统默认的指示器换成你需要的Spinner,直接用系统的
UIActivityIndicatorView也能满足需求。 - 开始刷新时手动固定TableView的
contentOffset,保持下拉状态,等数据加载完再恢复。
代码示例(Swift):
class CustomRefreshControl: UIRefreshControl { // 重写方法,默认不自动收起 override func endRefreshing() {} // 提供外部调用的结束方法,手动收起 func finishRefreshing() { super.endRefreshing() } } // 在ViewController里的用法 let refreshControl = CustomRefreshControl() refreshControl.addTarget(self, action: #selector(refreshData), for: .valueChanged) tableView.refreshControl = refreshControl @objc private func refreshData() { // 记录当前下拉的位置,固定住 let currentOffset = tableView.contentOffset tableView.contentOffset = currentOffset // 添加自定义Spinner到刷新控件里 let spinner = UIActivityIndicatorView(style: .large) spinner.center = refreshControl.center spinner.startAnimating() refreshControl.addSubview(spinner) // 模拟数据请求 DispatchQueue.global().async { // 这里写你的数据获取逻辑 DispatchQueue.main.async { // 更新TableView数据 self.tableView.reloadData() // 完成刷新,恢复状态 self.refreshControl.finishRefreshing() spinner.removeFromSuperview() } } }
方案二:自定义TableView Header(高度自定义场景)
如果系统控件满足不了你的UI需求(比如要做特殊的下拉动画),直接自定义一个HeaderView更灵活:
- 做个包含Spinner的HeaderView,初始高度设为0,作为TableView的
tableHeaderView。 - 监听TableView的滚动代理,根据下拉的偏移量调整Header的高度,下拉到指定阈值时触发刷新。
- 刷新期间固定Header的高度,保持空白区域显示Spinner,加载完再把Header缩回去。
代码示例(Swift):
class CustomRefreshHeader: UIView { let spinner = UIActivityIndicatorView(style: .large) override init(frame: CGRect) { super.init(frame: frame) addSubview(spinner) spinner.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ spinner.centerXAnchor.constraint(equalTo: centerXAnchor), spinner.centerYAnchor.constraint(equalTo: centerYAnchor) ]) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } // ViewController里的实现 private let refreshThreshold: CGFloat = 60 // 触发刷新的下拉距离 private var refreshHeader = CustomRefreshHeader(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 0)) private var isRefreshing = false override func viewDidLoad() { super.viewDidLoad() tableView.tableHeaderView = refreshHeader tableView.delegate = self } func scrollViewDidScroll(_ scrollView: UIScrollView) { guard !isRefreshing else { return } let pullDistance = -scrollView.contentOffset.y // 跟着下拉距离调整Header高度 refreshHeader.frame.size.height = max(0, pullDistance) // 下拉到阈值就触发刷新 if pullDistance >= refreshThreshold { startRefreshing() } } private func startRefreshing() { isRefreshing = true // 固定Header高度,保持下拉状态 refreshHeader.frame.size.height = refreshThreshold refreshHeader.spinner.startAnimating() // 数据请求逻辑 DispatchQueue.global().async { // 获取数据 DispatchQueue.main.async { self.tableView.reloadData() self.endRefreshing() } } } private func endRefreshing() { isRefreshing = false refreshHeader.spinner.stopAnimating() // 动画缩回头部,恢复初始状态 UIView.animate(withDuration: 0.3) { self.refreshHeader.frame.size.height = 0 self.tableView.contentOffset = .zero } }
最优选择建议
- 如果是常规iOS项目,优先用定制系统UIRefreshControl,原生集成的交互逻辑更稳定,不用自己处理复杂的滚动计算,兼容性也更好。
- 如果需要高度自定义的UI或者特殊的下拉过渡效果,就选自定义HeaderView的方式,灵活性拉满。
内容的提问来源于stack exchange,提问作者Rik
相关产品推荐
相关产品推荐

