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

iOS Swift:半屏弹出页跳转带导航栏编辑页的实现问题

问题描述

现有个人资料页面,导航栏rightBarButtonItem为菜单入口,点击按钮后通过以下代码弹出半屏样式的MenuSheetController:

let nextVc = storyboard.instantiateViewController(withIdentifier: "MenuSheetStoryboard") as! MenuSheetController
      
if let sheet =  nextVc.sheetPresentationController {
    sheet.detents = [.medium()]
}

self.present(nextVc, animated: true)

半屏菜单页内嵌展示包含多个菜单项的tableView,其中一个菜单项为「编辑资料」。点击该选项时需要跳转至EditProfileViewController,要求该页面和常规NavigationController栈内管理的页面一致,显示原生返回按钮,支持类似popViewController的栈式回退能力。

当前通过以下代码实现菜单页到编辑页的跳转:

let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "EditProfileStoryboard") as! EditProfileViewController
self.present(nextViewController, animated: true)

该实现存在两个明确问题:

  • 若以sheet形式弹出编辑页,用户可通过下拉手势直接关闭页面,不符合需求;同时编辑页的保存按钮需要实现点击后自动回退上一页的效果,和popViewController交互逻辑一致,当前模态弹出的逻辑无法对齐该体验。
  • 若修改为全屏样式弹出编辑页,页面没有默认配置返回入口,用户无法手动回到上一级页面。
解决方案

核心思路是不要直接在半屏菜单控制器上模态弹出编辑页,而是将「编辑资料」的点击事件回调给弹出菜单的个人资料页(该页面本身处于应用主导航栈中),先关闭半屏菜单,再通过导航栈push编辑页,完全对齐常规栈页面的交互逻辑。

实现步骤

  1. 给半屏菜单控制器添加点击事件回调
    在MenuSheetController中添加闭包属性,用于向外传递菜单项点击事件:
class MenuSheetController: UIViewController {
    // 新增编辑资料点击回调
    var onEditProfileSelected: (() -> Void)?

    // 其余原有代码...
}

在tableView的didSelectRowAt代理方法中,匹配到「编辑资料」项时触发回调:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    tableView.deselectRow(at: indexPath, animated: true)
    // 按实际业务的菜单位置判断索引
    if indexPath.row == 0 {
        onEditProfileSelected?()
    }
}
  1. 在个人资料页弹出菜单时配置回调逻辑
    在个人资料页弹出MenuSheetController的位置,实现回调逻辑:先dismiss半屏菜单,动画结束后push编辑页:
let nextVc = storyboard.instantiateViewController(withIdentifier: "MenuSheetStoryboard") as! MenuSheetController
if let sheet = nextVc.sheetPresentationController {
    sheet.detents = [.medium()]
}
// 配置编辑项点击回调
nextVc.onEditProfileSelected = { [weak self] in
    guard let self = self else { return }
    // 先关闭半屏菜单
    self.dismiss(animated: true) {
        // 菜单关闭完成后,从当前个人资料页push编辑页
        let storyBoard = UIStoryboard(name: "Main", bundle: nil)
        guard let editProfileVc = storyBoard.instantiateViewController(withIdentifier: "EditProfileStoryboard") as? EditProfileViewController else { return }
        self.navigationController?.pushViewController(editProfileVc, animated: true)
    }
}
self.present(nextVc, animated: true)
  1. 编辑页实现常规栈页面的回退逻辑
    编辑页不需要做特殊模态适配,保存按钮的回退逻辑和普通导航栈页面完全一致,直接调用pop方法即可:
@objc func handleSaveTap() {
    // 执行资料保存逻辑:接口请求、本地存储等
    
    // 直接调用pop返回个人资料页,和常规栈页面交互完全一致
    navigationController?.popViewController(animated: true)
}

方案优势

  • 编辑页完全处于应用主导航栈中,自动显示系统原生返回按钮,支持侧滑返回手势,不需要手动添加返回按钮、自定义导航栏
  • 禁止下拉关闭的需求天然满足,push出来的页面不支持下拉手势dismiss
  • 保存回退逻辑和普通栈页面完全统一,不需要区分present/dismiss和push/pop的逻辑差异
  • 转场动画流畅自然,半屏菜单先下落,编辑页从右侧滑入,符合iOS系统交互规范

不推荐的备选方案

如果因特殊业务场景必须使用模态弹出,可以将编辑页嵌套在UINavigationController中全屏present,手动添加导航栏返回按钮,回退时调用dismiss方法。但该方案需要单独适配导航栏样式、返回按钮逻辑、侧滑返回手势,和应用内其他栈内页面的交互逻辑不统一,后续维护成本高,非必要不使用。

// 不推荐的实现示例
let storyBoard = UIStoryboard(name: "Main", bundle:nil)
guard let nextViewController = storyBoard.instantiateViewController(withIdentifier: "EditProfileStoryboard") as? EditProfileViewController else { return }
let nav = UINavigationController(rootViewController: nextViewController)
nav.modalPresentationStyle = .fullScreen
// 手动添加返回按钮
nextViewController.navigationItem.leftBarButtonItem = UIBarButtonItem(
    image: UIImage(systemName: "chevron.left"),
    style: .plain,
    target: self,
    action: #selector(handleBackTap)
)
// 禁止下拉关闭
nav.isModalInPresentation = true
self.present(nav, animated: true)

@objc func handleBackTap() {
    dismiss(animated: true)
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 19:36:17