SwiftUI横竖屏切换LazyVGrid列数时如何避免fullScreenIsPresented自动消失
问题根因
当设备旋转触发sizeClass变化时,你直接在ScrollView内容块内生成GridItem数组,会导致LazyVGrid的布局配置每次重绘都生成新的实例,SwiftUI会将其判定为全新的视图,既会丢失原有滚动位置的上下文,也会因为父视图层级的强制重绘,导致子视图弹出的全屏模态被意外销毁。
修复方案
- 把GridItem布局配置挪到计算属性中,避免不必要的实例刷新
- 给LazyVGrid添加固定的唯一标识,告知SwiftUI重绘时复用原有视图状态
- 绑定滚动位置,横竖屏切换时系统会自动适配新的布局尺寸恢复滚动偏移
- 全屏弹出的状态
fullScreenIsPresented要声明在Feed_View层级,不要放在FeedCell等子视图中,避免子视图重制导致状态丢失
修正后代码示例
struct Feed_View: View { @EnvironmentObject var viewModel : Post_View_Model @Environment(\.verticalSizeClass) var sizeClass @FocusState private var isFocused: Bool // iOS17+ 滚动位置绑定,低版本可以用ScrollViewReader实现 @State private var scrollPosition = ScrollPosition() // 全屏弹窗状态移到当前层声明 @State private var fullScreenIsPresented = false @State private var selectedPost: PostModel? // 可选,用来传选中的单元格数据 // 布局改成计算属性,不要在body的视图构建逻辑里生成数组 private var layout: [GridItem] { Array(repeating: GridItem(.flexible(), spacing: 10), count: sizeClass == .compact ? 3 : 2) } var body: some View { Color("BGColor").ignoresSafeArea() ZStack { VStack (alignment: .center, spacing: 0) { NavBar_View() .frame(maxHeight: 40, alignment: .center) // 绑定滚动位置 ScrollView (.vertical, showsIndicators: false) { LazyVGrid(columns: layout, spacing: 10) { ForEach (viewModel.posts, id: \.self) { post in Feed_Cell(postModel: post) .onTapGesture { selectedPost = post fullScreenIsPresented = true } } } .padding(.horizontal, 10).padding(.vertical, 10) // 给LazyVGrid加固定id,避免布局变化被判定为新视图 .id("FeedFixedLazyVGrid") } .scrollPosition($scrollPosition) } } // 全屏弹窗写在最外层,不受内部滚动视图重绘影响 .fullScreenCover(isPresented: $fullScreenIsPresented) { if let post = selectedPost { // 你的全屏页实现 PostFullScreenView(post: post) } } } }
如果是iOS16及更低版本,不需要ScrollPosition的相关代码,只保留给LazyVGrid加固定id、布局挪到计算属性、全屏弹窗移到外层三个修改即可修复。
内容的提问来源于stack exchange,提问作者TheManOfSteell
相关产品推荐
相关产品推荐

