SwiftUI打开EditViewScreen加载首条帖子及弹窗报错问题
问题根因
两个异常由同一处错误写法导致:
- 你将
.sheet(isPresented:)修饰符写在了ForEach的列表单元格内部:- 所有单元格共用同一个
showingEdit布尔状态,长按触发状态变为true时,所有单元格挂载的sheet都会尝试执行弹出逻辑。系统检测到当前控制器已经在展示弹窗、仍收到新的弹出请求时,就会抛出你看到的Presentation报错,含义为目标控制器已在展示其他内容,无法重复弹出视图。 - sheet闭包捕获的
post值是列表首次加载时第一个单元格的对象,不会随长按选中的单元格更新,因此编辑页永远加载第一条帖子的内容。
- 所有单元格共用同一个
- 额外隐患:
EditViewScreen中通过.onAppear给@State绑定的输入框赋值的写法存在时序风险,同时代码里大量用强制解包(!)容易触发崩溃。
修复方案
1. 调整HomeViewScreen的状态与sheet挂载位置
不要在ForEach内部挂载sheet,将sheet移到列表外层,使用SwiftUI提供的sheet(item:)初始化方法,该方法会自动绑定当前选中的内容,不需要手动控制布尔开关:
- 将原有状态
@State var showingEdit = false替换为@State var selectedPost: Post? = nil - 长按手势逻辑改为给
selectedPost赋值为当前长按的post对象 - 将
.sheet修饰符移到List(或外层ZStack)上,闭包内直接获取选中的post传入编辑页
修改后的核心代码如下:
// 替换原有showingEdit状态 @State var selectedPost: Post? = nil ZStack { List { ForEach(viewModel.posts, id: \.self) { post in PostCell(post: post) .onLongPressGesture { // 长按直接赋值选中的帖子 selectedPost = post } } .onDelete(perform: delete) } .listStyle(.plain) // sheet移到列表外层,不要放在ForEach循环内 .sheet(item: $selectedPost) { currentPost in EditViewScreen(post: currentPost) } if viewModel.isLoading { ProgressView() } }
注意:使用sheet(item:)需要Post模型遵守Identifiable协议,如果Post本身已经有id属性,只需要加一行扩展即可:extension Post: Identifiable {}
2. 优化EditViewScreen的状态初始化
删除.onAppear中给输入框赋值的逻辑,直接在视图初始化时给@State变量赋初始值,避免时序问题和强制解包风险:
将原有@State定义:
@State var p_title = "" @State var p_body = ""
替换为带初始化方法的写法:
@State var p_title: String @State var p_body: String var post: Post init(post: Post) { self.post = post self._p_title = State(initialValue: post.title ?? "") self._p_body = State(initialValue: post.body ?? "") }
同时删除body中.onAppear { p_title = post.title!; p_body = post.body! }这段代码即可。
修改完成后两个异常都会消失:长按单元格只会弹出一个编辑弹窗,不会再出现Presentation报错,编辑页会准确加载当前选中帖子的标题和正文。
内容的提问来源于stack exchange,提问作者user15549045
相关产品推荐
相关产品推荐

