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

iOS SwiftUI中NavigationStack内ScrollView滚动到顶导航标题无法完全展开问题

问题概述

在NavigationStack嵌套的ScrollView中实现回到顶部按钮时,遇到以下问题:

  • 当用withAnimation{}包裹ScrollViewProxy.scrollTo()调用时,滚动到顶部后导航标题无法完全展开,仅显示紧凑样式
  • 不使用withAnimation{}时,滚动到顶功能正常,导航标题能正常展开为大标题样式
  • 测试环境:Xcode 15.2,iOS 16.4、17.0.1、17.2(真机、模拟器、预览均复现)
  • 已尝试的无效方案:指定scrollTo()的锚点、替换LazyVStack为VStack、调整按钮布局

复现代码

import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationStack() {
            LazyVNavView()
                .navigationTitle("THE LAZYVNAV")
        }
    }
}

struct LazyVNavView: View {
    
    var body: some View {
        ScrollViewReader { proxy in
            ScrollView {
                LazyVStack {
                    ForEach(0..<100) { rowIndex in
                        Text("Row " + String(rowIndex))
                            .id(rowIndex)
                    }
                    
                    HStack {
                        Spacer()
                        Button("To Top: Animation") {
                            withAnimation{
                                proxy.scrollTo(0)
                            }
                        }
                        Spacer()
                        Button("To Top: No Animation") {
                            proxy.scrollTo(0)
                        }
                        Spacer()
                    }
                }
            }
        }
    }
}

#Preview {
    ContentView()
}

效果对比

带动画滚动到顶(导航标题未完全展开)

带动画滚动到顶效果

无动画滚动到顶(导航标题正常展开)

无动画滚动到顶效果

解决方案

以下两种方案可解决该问题:

方案1:延迟触发导航栏布局更新

通过短暂延迟后触发UIKit的布局更新,强制导航栏展开:

Button("To Top: Animation Fixed") {
    withAnimation {
        proxy.scrollTo(0, anchor: .top)
    }
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
        UIApplication.shared.sendAction(#selector(UIResponder.becomeFirstResponder), to: nil, from: nil, for: nil)
    }
}

方案2:手动控制导航标题显示模式

通过状态变量控制导航标题的显示模式,在滚动动画前后切换状态:

struct LazyVNavView: View {
    @State private var titleDisplayMode: NavigationBarItem.TitleDisplayMode = .automatic
    
    var body: some View {
        ScrollViewReader { proxy in
            ScrollView {
                LazyVStack {
                    ForEach(0..<100) { rowIndex in
                        Text("Row " + String(rowIndex))
                            .id(rowIndex)
                    }
                    
                    HStack {
                        Spacer()
                        Button("To Top: Animation Fixed") {
                            withAnimation {
                                titleDisplayMode = .large
                                proxy.scrollTo(0, anchor: .top)
                            }
                            DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                                withAnimation {
                                    titleDisplayMode = .automatic
                                }
                            }
                        }
                        Spacer()
                        Button("To Top: No Animation") {
                            proxy.scrollTo(0)
                        }
                        Spacer()
                    }
                }
            }
            .navigationBarTitleDisplayMode(titleDisplayMode)
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 02:35:07