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

SwiftUI:解决无List时使用searchable的动画跳变问题

问题根源

ScrollView本身没有像List那样内置适配搜索栏显示/隐藏时的布局动画逻辑,导致导航栏和搜索栏执行上下动画时,ScrollView内容直接跳变。全局添加废弃的.animation会污染所有视图的动画,引发列表项加载、筛选时的异常。

解决方案

方案1:iOS16+ 最优解(切换到NavigationStack)

NavigationView在iOS16已被废弃,NavigationStack对.searchable的布局动画做了原生适配,替换后ScrollView会自动跟随动画,无需额外处理:

import SwiftUI

struct ContentView: View {
    @State var searchText: String = ""
    
    var body: some View {
        NavigationStack {
            ScrollView(.vertical) {
                CustomListView()
            }
            .navigationTitle("Fixed ScrollView")
            .searchable(text: $searchText, placement: .automatic)
        }
    }
}

// 其余CustomListView、CustomListItemView代码保持不变

方案2:iOS15+ 兼容方案

通过监听搜索状态,仅给ScrollView的布局变化添加动画,避免全局动画污染列表项:

import SwiftUI

struct ContentView: View {
    @State var searchText: String = ""
    @Environment(\.isSearching) private var isSearching
    
    var body: some View {
        NavigationView {
            ScrollView(.vertical) {
                CustomListView()
            }
            // 仅针对搜索状态变化绑定动画,不影响列表项的正常加载/筛选
            .animation(.easeInOut, value: isSearching)
            .navigationTitle("Fixed ScrollView")
            .searchable(text: $searchText, placement: .automatic)
        }
    }
}

// 其余代码保持不变

补充调整(若存在布局偏移)

如果方案2中仍有微小布局偏移,可通过GeometryReader获取导航栏高度,动态调整ScrollView的内容内边距:

struct ContentView: View {
    @State var searchText: String = ""
    @Environment(\.isSearching) private var isSearching
    @State private var navigationBarHeight: CGFloat = 0
    
    var body: some View {
        NavigationView {
            ScrollView(.vertical) {
                CustomListView()
            }
            .contentInsets(.init(top: isSearching ? 0 : navigationBarHeight, leading: 0, bottom: 0, trailing: 0))
            .animation(.easeInOut, value: isSearching)
            .navigationTitle("Fixed ScrollView")
            .searchable(text: $searchText, placement: .automatic)
            // 监听导航栏高度
            .background(
                GeometryReader { proxy in
                    Color.clear
                        .onAppear {
                            navigationBarHeight = proxy.safeAreaInsets.top
                        }
                }
            )
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 17:39:39