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

如何在已呈现的控制器上呈现新控制器?(自定义相机场景)

在已呈现控制器上叠加居中相机控制器的解决方案

嘿,我明白你现在的需求:在自定义相机捕获图片时,弹出一个居中的控制器来模拟拍摄状态,而且这个控制器要在已经呈现的上层控制器上面显示对吧?你的现有代码有几个潜在问题,我一步步给你修正和优化。

先说说你现有代码的问题

  • 直接依赖AppDelegate的rootViewController去获取presentedViewController,耦合性太高,而且如果当前没有任何弹出的控制器,这行代码直接就失效了。
  • 设置了.custom的模态样式,但没指定对应的transitioningDelegate,导致自定义居中的效果根本不会生效。
  • 尽量不要在非必要时用asyncAfter,UI操作要确保在主线程同步执行(除非你真的需要延迟显示)。

第一步:让相机控制器实现居中显示

这里给你两种方案,分别适配不同iOS版本:

方案1:用iOS 13+自带的居中模态(最简单)

不用写自定义转场,直接用系统的.pageSheet样式,还能自定义大小和背景:

let layerVC = CameraLayerViewController(nibName: "CameraLayerViewController", bundle: nil)
// iOS13+ 自带的居中模态样式
layerVC.modalPresentationStyle = .pageSheet
// 设置控制器的宽高,让它居中显示(比如和屏幕等宽,减去左右边距)
let cameraViewWidth = UIScreen.main.bounds.width - 80
layerVC.preferredContentSize = CGSize(width: cameraViewWidth, height: cameraViewWidth)
// 自定义sheet的样式,比如背景半透明、隐藏拖拽条
if let sheet = layerVC.sheetPresentationController {
    sheet.detents = [.custom(resolver: { _ in cameraViewWidth })]
    sheet.backgroundColor = .black.withAlphaComponent(0.6)
    sheet.prefersGrabberVisible = false
    sheet.prefersEdgeAttachedInCompactHeight = true
    sheet.widthFollowsPreferredContentSizeWhenEdgeAttached = true
}

方案2:自定义转场(兼容iOS 12及以下)

如果需要适配更低版本,就写个自定义的转场代理和PresentationController:
首先创建转场相关的类:

// 自定义转场代理
class CenteredTransitionDelegate: NSObject, UIViewControllerTransitioningDelegate {
    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        return CenteredPresentationController(presentedViewController: presented, presenting: presenting)
    }
}

// 控制控制器的居中位置
class CenteredPresentationController: UIPresentationController {
    override var frameOfPresentedViewInContainerView: CGRect {
        guard let container = containerView else { return .zero }
        let size = CGSize(width: container.bounds.width - 80, height: container.bounds.width - 80)
        return CGRect(
            x: (container.bounds.width - size.width)/2,
            y: (container.bounds.height - size.height)/2,
            width: size.width,
            height: size.height
        )
    }
}

然后在呈现控制器时设置:

let layerVC = CameraLayerViewController(nibName: "CameraLayerViewController", bundle: nil)
layerVC.modalPresentationStyle = .custom
layerVC.transitioningDelegate = CenteredTransitionDelegate()
// 给控制器背景加半透明效果
layerVC.view.backgroundColor = .black.withAlphaComponent(0.6)

第二步:正确获取当前最上层的控制器

不要依赖AppDelegate,写个UIViewController的扩展来获取当前活跃的顶层控制器:

extension UIViewController {
    static func topMost() -> UIViewController? {
        var topVC = UIApplication.shared.keyWindow?.rootViewController
        // 一直找到最上层的presented控制器
        while let presented = topVC?.presentedViewController {
            topVC = presented
        }
        return topVC
    }
}

最终的完整呈现代码

把上面的部分组合起来,确保在主线程执行UI操作:

DispatchQueue.main.async {
    guard let topVC = UIViewController.topMost() else { return }
    
    let layerVC = CameraLayerViewController(nibName: "CameraLayerViewController", bundle: nil)
    // 这里选你需要的模态样式,比如iOS13+的.pageSheet
    layerVC.modalPresentationStyle = .pageSheet
    let cameraWidth = UIScreen.main.bounds.width - 80
    layerVC.preferredContentSize = CGSize(width: cameraWidth, height: cameraWidth)
    
    if let sheet = layerVC.sheetPresentationController {
        sheet.detents = [.custom(resolver: { _ in cameraWidth })]
        sheet.backgroundColor = .black.withAlphaComponent(0.6)
        sheet.prefersGrabberVisible = false
    }
    
    // 呈现控制器,完成后可以启动相机预览
    topVC.present(layerVC, animated: true) {
        layerVC.startCameraCapture() // 假设你有这个方法来启动相机
    }
}

额外注意事项

  • 确保已经申请了相机权限,不然控制器显示了也没法捕获图片。
  • 拍摄完成或取消时,记得调用layerVC.dismiss(animated: true)关闭这个控制器。
  • 所有UI相关的代码一定要在主线程执行,避免出现奇怪的卡顿或崩溃。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 06:39:12