SwiftUI嵌套ScrollView导航返回后应用冻结问题求助
问题:嵌套滚动视图返回后应用冻结
环境信息:
- SwiftUI: 5.9
- XCode: 15.0.1
- iPhone 11
- iOS: 17.0.3
开发多页面应用时,Home2View通过垂直ScrollView嵌套3个水平ScrollView展示缩略图,点击缩略图可导航至CameraView。该功能首次进入页面时正常,但从CameraView点击返回按钮回到首页后,应用会冻结且无任何报错信息,无法进行操作。已知Swift滚动视图导航存在相关Bug,附上代码寻求解决方案:
struct Home2View: View { @StateObject var accountViewModel: AccountViewModel @StateObject var myPozeGroupViewModel: MyPozeGroupViewModel var body: some View { NavigationView { ScrollView(.vertical) { VStack (spacing: 50) { ScrollView(.horizontal) { horizontalList(data: myPozeGroupViewModel.top10MyPozeGroups, backgroundColor: .green) } ScrollView(.horizontal) { horizontalList(data: myPozeGroupViewModel.freshest10MyPozeGroups) } ScrollView(.horizontal) { horizontalList(data: myPozeGroupViewModel.letters10MyPozeGroups) } } } .onAppear { if myPozeGroupViewModel.top10MyPozeGroups.isEmpty { myPozeGroupViewModel.addListenerForTop10() } if myPozeGroupViewModel.freshest10MyPozeGroups.isEmpty { myPozeGroupViewModel.addListenerForFreshest10() } if myPozeGroupViewModel.letters10MyPozeGroups.isEmpty { myPozeGroupViewModel.addListenerForLetters10() } } } } func horizontalList(data: [MyPozeGroup], backgroundColor: Color = .blue) -> some View { HStack(spacing:5) { ForEach(data) { myPozeGroup in NavigationLink(destination: CameraView( myPozeGroup: myPozeGroup, myPozeGroupViewModel: myPozeGroupViewModel, accountViewModel: accountViewModel) ) { VStack(spacing: 0) { AsyncImage(url: URL(string: myPozeGroup.thumbnail ?? "")) { image in image .resizable() .aspectRatio(contentMode: .fit) .frame(width: 150, height: 150) } placeholder: { ProgressView() } Rectangle() .fill(Color.white) .frame(height: 20) .overlay( HStack { Text(myPozeGroup.name ?? "") .foregroundColor(.black) Spacer() Text("\(myPozeGroup.counter)") .foregroundColor(.gray) } ) } .cornerRadius(20) } } } }
解决方案
针对iOS 17中嵌套ScrollView结合NavigationLink导致的返回冻结问题,可尝试以下几种修复方案:
1. 替换NavigationView为NavigationStack
iOS 16+官方推荐使用NavigationStack替代旧的NavigationView,后者在新版本中存在嵌套滚动视图的兼容问题。修改Home2View的根视图:
NavigationStack { // 原垂直ScrollView内容 }
2. 使用navigationDestination替代嵌套的NavigationLink
嵌套ScrollView中直接使用NavigationLink易引发视图层级冲突,改用navigationDestination统一管理导航目标:
- 首先在Home2View中添加选中对象状态:
@State private var selectedPozeGroup: MyPozeGroup?
- 在NavigationStack上添加导航目标绑定:
.navigationDestination(item: $selectedPozeGroup) { group in CameraView( myPozeGroup: group, myPozeGroupViewModel: myPozeGroupViewModel, accountViewModel: accountViewModel ) }
- 修改horizontalList的点击逻辑,替换NavigationLink为TapGesture:
func horizontalList(data: [MyPozeGroup], backgroundColor: Color = .blue) -> some View { HStack(spacing:5) { ForEach(data) { myPozeGroup in VStack(spacing: 0) { AsyncImage(url: URL(string: myPozeGroup.thumbnail ?? "")) { image in image .resizable() .aspectRatio(contentMode: .fit) .frame(width: 150, height: 150) } placeholder: { ProgressView() } Rectangle() .fill(Color.white) .frame(height: 20) .overlay( HStack { Text(myPozeGroup.name ?? "") .foregroundColor(.black) Spacer() Text("\(myPozeGroup.counter)") .foregroundColor(.gray) } ) } .cornerRadius(20) .onTapGesture { selectedPozeGroup = myPozeGroup } } } }
3. 给水平ScrollView设置滚动优先级
明确垂直与水平ScrollView的滚动优先级,避免交互冲突:
ScrollView(.horizontal) { horizontalList(data: myPozeGroupViewModel.top10MyPozeGroups, backgroundColor: .green) } .scrollPriority(1) // 垂直ScrollView默认优先级为0,设置更高优先级确保水平滚动响应正常
4. 优化ViewModel监听逻辑
避免返回页面后重复创建监听,在MyPozeGroupViewModel中添加初始化标记:
class MyPozeGroupViewModel: ObservableObject { @Published var top10MyPozeGroups: [MyPozeGroup] = [] private var hasInitializedTop10 = false // 其他属性... func addListenerForTop10() { guard !hasInitializedTop10 else { return } hasInitializedTop10 = true // 原监听逻辑 } // 同理处理addListenerForFreshest10和addListenerForLetters10 }
内容的提问来源于stack exchange,提问作者Gregor Sotošek
相关产品推荐
相关产品推荐

