You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过代码触发SwiftUI原生.refreshable加载动画?

问题

我给SwiftUI的List添加了.refreshable修饰符,手动下拉刷新时功能正常。但我需要在特定条件变化时(比如通过按钮点击触发),用代码触发同样的刷新动画。目前调用refresh()方法只能执行刷新逻辑,看不到和手动下拉一致的原生加载动画,想问下能不能通过代码触发原生的.refreshable动画,还是必须自己做个模仿原生样式的自定义刷新指示器?

代码示例:

struct ContentView: View {
    @State private var refreshTimes = 0
    var body: some View {
        List{
            Text("refresh times: \(refreshTimes)")
            Button("click me to refresh") {
                refresh()
            }
        }
        .refreshable {
            refresh()
        }
    }
    
    func refresh() {
        refreshTimes += 1
        print("refresh at :\(Date())")
    }
}
解决方案

目前SwiftUI原生的.refreshable并没有提供直接触发动画的公开API,不过可以通过两种方式实现需求:

方法一:利用ScrollViewProxy模拟下拉刷新(推荐)

通过ScrollViewReader获取ScrollViewProxy,手动将列表滚动到超出顶部的位置,触发原生刷新动画,之后再恢复原位置。这种方式能完全复用原生的刷新指示器和动画逻辑:

struct ContentView: View {
    @State private var refreshTimes = 0
    @State private var isProgrammaticRefresh = false
    
    var body: some View {
        ScrollViewReader { proxy in
            List{
                Text("refresh times: \(refreshTimes)")
                Button("click me to refresh") {
                    isProgrammaticRefresh = true
                    // 滚动到顶部外的位置触发刷新
                    proxy.scrollTo(-1, anchor: .top)
                    
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                        isProgrammaticRefresh = false
                        proxy.scrollTo(0, anchor: .top) // 恢复原位置
                    }
                }
            }
            .refreshable {
                refresh()
            }
            .onAppear {
                // 给列表添加初始锚点
                proxy.scrollTo(0, anchor: .top)
            }
        }
    }
    
    func refresh() {
        refreshTimes += 1
        print("refresh at :\(Date())")
    }
}

注:这里的-1和0是自定义锚点ID,你也可以给列表第一个元素设置明确ID,滚动到该ID上方来触发刷新。

方法二:自定义刷新指示器

如果觉得滚动模拟不够优雅,可以自己实现一个和原生样式一致的刷新指示器,通过状态控制显示时机,灵活性更高:

struct ContentView: View {
    @State private var refreshTimes = 0
    @State private var isRefreshing = false
    
    var body: some View {
        List{
            if isRefreshing {
                ProgressView()
                    .frame(maxWidth: .infinity)
            }
            Text("refresh times: \(refreshTimes)")
            Button("click me to refresh") {
                guard !isRefreshing else { return }
                isRefreshing = true
                refresh()
            }
        }
        .refreshable {
            isRefreshing = true
            refresh()
        }
    }
    
    func refresh() {
        // 模拟异步刷新过程
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            refreshTimes += 1
            isRefreshing = false
            print("refresh at :\(Date())")
        }
    }
}

这个自定义的ProgressView和原生刷新指示器样式基本一致,通过isRefreshing状态控制显示,手动下拉和按钮触发都能同步展示动画。

内容的提问来源于stack exchange,提问作者ximmyxiao

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 11:12:32