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
相关产品推荐
相关产品推荐

