刷微博、看新闻、逛购物App,手指一拉就刷新新内容——这已经成了大家的肌肉记忆。在 SwiftUI 里想给 List 或 ScrollView 加个下拉刷新,其实不难,但得用对方法。iOS 15 开始,SwiftUI 原生支持 .refreshable 修饰符,不用再折腾第三方库或者硬套 UIKit 的 UIRefreshControl。
最简写法:一行搞定
假设你有个展示文章标题的列表:
struct ArticleView: View {
@State private var articles = ["iOS 18 新特性","SwiftUI 动画技巧","Xcode 16 使用避坑"]
var body: some View {
List(articles, id: \.self) { title in
Text(title)
}
.refreshable {
// 模拟网络请求
await Task.sleep(nanoseconds: 1_500_000_000)
articles.insert("刚刷新的第" + String(articles.count + 1), at: 0)
}
}
}下拉时会自动出现那个熟悉的旋转圆圈,松手后执行闭包里的逻辑,刷新完成后动画收起。注意:.refreshable 要求闭包是 async 的,所以里面可以直接用 await 调用异步函数。
配合真实网络请求
实际项目中,你大概率要调接口。比如从一个假 API 拉最新 3 条新闻:
func fetchLatestNews() async -> [String] {
guard let url = URL(string: "https://api.example.com/news?limit=3") else { return [] }
do {
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode([String].self, from: data)
} catch {
print("加载失败:\($0)")
return []
}
}然后在 View 里这样用:
.refreshable {
let newItems = await fetchLatestNews()
articles = newItems + articles
}旧系统兼容(iOS 14 及以下)
如果你还得支持 iOS 14,.refreshable 不可用,就得桥接 UIKit。新建一个 RefreshableScrollView 封装 UIRefreshControl,再用 UIViewRepresentable 包一层。不过现在绝大多数用户都升到 iOS 15+ 了,除非你做企业内网 App 或明确要求兼容老系统,否则真没必要绕这么大弯。
小提醒几个坑
• 列表为空时下拉也会触发刷新,这是正常行为,别误以为没生效;
• 如果用的是自定义 ScrollView + LazyVStack,记得加上 .refreshable 在 ScrollView 外层,不是 Stack 里面;
• 刷新过程中用户又猛拉一次?不会重复触发,SwiftUI 会自动节流。
顺手试一试,把上面那段代码粘进 Xcode 的预览里,下拉看看效果——比想象中快多了。