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

Swift中如何独立显示与隐藏多个Overlay遮罩视图

实现多条件独立控制的全屏遮罩Overlay(基于UINavigationController)

针对你需要多个独立遮罩根据条件动态显示/隐藏、且不影响原有导航栈的需求,我们可以通过给UINavigationController扩展子控制器管理能力来实现,核心是将遮罩ViewController作为导航控制器的子控制器添加到视图层级,而非使用present或push,这样每个遮罩可以独立管控,互不干扰。

核心实现思路

  • 给UINavigationController维护一个字典,存储所有遮罩VC及其唯一标识(用于区分不同条件的遮罩)
  • 遮罩VC作为导航控制器的子控制器添加,视图层级置于最上层,实现全屏遮罩效果
  • 提供添加、移除、更新遮罩的方法,支持根据条件独立控制每个遮罩的显示/隐藏

代码实现

UINavigationController扩展

import UIKit

extension UINavigationController {
    // 存储遮罩VC,key为遮罩的唯一标识(比如"locationPermission"、"networkError")
    private var overlayVCs: [String: UIViewController] {
        get {
            objc_getAssociatedObject(self, &AssociatedKeys.overlayVCs) as? [String: UIViewController] ?? [:]
        }
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.overlayVCs, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    private struct AssociatedKeys {
        static var overlayVCs = "overlayVCs"
    }
    
    /// 添加遮罩VC
    /// - Parameters:
    ///   - vc: 遮罩视图控制器
    ///   - identifier: 唯一标识,用于后续移除/更新
    ///   - animated: 是否显示动画
    func addOverlay(_ vc: UIViewController, identifier: String, animated: Bool = true) {
        guard overlayVCs[identifier] == nil else { return }
        
        // 设置遮罩VC的frame为导航控制器的bounds
        vc.view.frame = view.bounds
        vc.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        
        addChild(vc)
        view.addSubview(vc.view)
        vc.didMove(toParent: self)
        
        overlayVCs[identifier] = vc
        
        if animated {
            vc.view.alpha = 0
            UIView.animate(withDuration: 0.3) {
                vc.view.alpha = 1
            }
        }
    }
    
    /// 移除指定标识的遮罩VC
    /// - Parameters:
    ///   - identifier: 遮罩的唯一标识
    ///   - animated: 是否显示动画
    func removeOverlay(with identifier: String, animated: Bool = true) {
        guard let vc = overlayVCs[identifier] else { return }
        
        if animated {
            UIView.animate(withDuration: 0.3, animations: {
                vc.view.alpha = 0
            }) { _ in
                vc.willMove(toParent: nil)
                vc.view.removeFromSuperview()
                vc.removeFromParent()
                self.overlayVCs.removeValue(forKey: identifier)
            }
        } else {
            vc.willMove(toParent: nil)
            vc.view.removeFromSuperview()
            vc.removeFromParent()
            overlayVCs.removeValue(forKey: identifier)
        }
    }
    
    /// 根据条件批量更新遮罩状态
    /// - Parameter conditions: 字典,key为遮罩标识,value为是否需要显示(true显示,false隐藏)
    func updateOverlays(conditions: [String: Bool], animated: Bool = true) {
        for (identifier, shouldShow) in conditions {
            if shouldShow {
                // 这里需要你提前准备好对应标识的遮罩VC,或者在方法里传入创建逻辑
                // 示例:假设你有一个方法可以根据标识创建对应的遮罩VC
                guard let vc = createOverlayVC(for: identifier) else { continue }
                addOverlay(vc, identifier: identifier, animated: animated)
            } else {
                removeOverlay(with: identifier, animated: animated)
            }
        }
    }
    
    /// 示例:根据标识创建对应的遮罩VC(你可以根据自己的需求实现)
    private func createOverlayVC(for identifier: String) -> UIViewController? {
        switch identifier {
        case "locationPermission":
            let vc = LocationPermissionOverlayVC()
            return vc
        case "networkError":
            let vc = NetworkErrorOverlayVC()
            return vc
        default:
            return nil
        }
    }
}

使用示例

// 在需要检查条件的地方(比如AppDelegate、BaseViewController)
let navigationController = self.window?.rootViewController as! UINavigationController

// 模拟条件:定位未开启、网络异常
let conditions: [String: Bool] = [
    "locationPermission": !CLLocationManager.locationServicesEnabled(),
    "networkError": !Reachability.isConnectedToNetwork()
]

// 更新遮罩状态
navigationController.updateOverlays(conditions: conditions)

// 单独移除某一个遮罩(比如用户开启定位后)
navigationController.removeOverlay(with: "locationPermission")

方案优势

  • 独立管控:每个遮罩通过唯一标识管理,可单独显示/隐藏,不会出现present覆盖或push栈混乱的问题
  • 不影响原有导航栈:遮罩作为子控制器添加,完全不干扰原有的present/push操作和导航栈结构
  • 关注点分离:所有遮罩管理逻辑封装在导航控制器扩展中,业务代码只需关注条件判断和调用方法即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 20:11:02