Swift 4中实现导航栏刷新按钮及5分钟自动刷新功能
嘿,我来帮你实现这个导航栏刷新的需求!既要支持手动点击刷新,又要每5分钟自动更新,还要在加载时显示Activity Indicator,咱们一步步来搞定:
第一步:定义所需的变量
首先在你的ViewController里声明几个核心变量,用来管理刷新按钮、加载指示器和自动刷新定时器:
private var refreshButton: UIBarButtonItem! private var activityIndicator: UIActivityIndicatorView! private var refreshTimer: Timer?
第二步:初始化UI组件和启动自动刷新
在viewDidLoad里完成控件初始化、设置导航栏按钮,同时启动自动刷新和初始数据加载:
override func viewDidLoad() { super.viewDidLoad() // 初始化Activity Indicator,适配导航栏风格 activityIndicator = UIActivityIndicatorView(style: .medium) activityIndicator.tintColor = .white // 根据你的导航栏背景色调整颜色 // 创建刷新按钮并绑定点击事件 refreshButton = UIBarButtonItem(barButtonSystemItem: .refresh, target: self, action: #selector(refreshButtonTapped)) navigationItem.rightBarButtonItem = refreshButton // 启动每5分钟一次的自动刷新 startAutoRefresh() // 首次加载数据 getCoinData { [weak self] in self?.coinTableView.reloadData() } }
第三步:改造数据获取方法,添加完成回调
你的getCoinData是异步网络请求,必须添加闭包回调,这样才能在数据加载完成后更新UI并恢复按钮状态:
func getCoinData(completion: @escaping () -> Void) { // 替换成你的JSON请求URL guard let url = URL(string: "https://your-api-url.com/coins") else { print("无效的请求URL") DispatchQueue.main.async { completion() } return } URLSession.shared.dataTask(with: url) { [weak self] data, response, error in guard let self = self else { DispatchQueue.main.async { completion() } return } if let error = error { print("数据请求失败: \(error.localizedDescription)") DispatchQueue.main.async { completion() } return } guard let data = data else { print("未获取到有效数据") DispatchQueue.main.async { completion() } return } // 解析JSON到你的数据源数组(这里假设你的数据模型是Coin) do { self.coinDataArray = try JSONDecoder().decode([Coin].self, from: data) } catch { print("JSON解析失败: \(error.localizedDescription)") } // 回到主线程执行回调,更新UI DispatchQueue.main.async { completion() } }.resume() }
第四步:实现手动刷新逻辑和状态切换
编写点击事件方法,切换按钮和加载指示器的显示状态,同时处理数据刷新:
@objc private func refreshButtonTapped() { // 用户手动刷新后,重置自动刷新定时器(重新计算5分钟的自动刷新时间) refreshTimer?.invalidate() // 切换到加载状态,隐藏刷新按钮显示指示器 toggleRefreshLoadingState(isLoading: true) // 获取数据并更新表格 getCoinData { [weak self] in guard let self = self else { return } self.coinTableView.reloadData() self.toggleRefreshLoadingState(isLoading: false) // 重新启动自动刷新 self.startAutoRefresh() } } // 封装状态切换逻辑,方便复用 private func toggleRefreshLoadingState(isLoading: Bool) { if isLoading { navigationItem.rightBarButtonItem = UIBarButtonItem(customView: activityIndicator) activityIndicator.startAnimating() } else { navigationItem.rightBarButtonItem = refreshButton activityIndicator.stopAnimating() } }
第五步:实现自动刷新和内存清理
编写自动刷新的定时器逻辑,同时在页面消失时销毁定时器,避免内存泄漏:
private func startAutoRefresh() { // 5分钟 = 300秒,设置定时器并加入RunLoop,避免后台暂停 refreshTimer = Timer.scheduledTimer(timeInterval: 300, target: self, selector: #selector(autoRefresh), userInfo: nil, repeats: true) RunLoop.current.add(refreshTimer!, forMode: .common) } @objc private func autoRefresh() { // 自动刷新的逻辑和手动刷新完全一致 toggleRefreshLoadingState(isLoading: true) getCoinData { [weak self] in guard let self = self else { return } self.coinTableView.reloadData() self.toggleRefreshLoadingState(isLoading: false) } } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // 页面消失时销毁定时器,释放资源 refreshTimer?.invalidate() refreshTimer = nil }
一些细节提醒
- 全程用
[weak self]避免循环引用,尤其是在闭包和定时器中,防止内存泄漏 - 网络请求失败时一定要执行completion回调,确保加载状态能恢复,不会一直转圈
- 如果你的导航栏是深色背景,记得调整Activity Indicator的
tintColor,保证可见性
内容的提问来源于stack exchange,提问作者cs4alhaider
相关产品推荐
相关产品推荐

