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

Swift自定义弹窗UIView未从Superview移除导致新弹窗重叠问题求解

问题根源
  1. 你的MyAlert类全局持有唯一的alertView、backgroundView实例,每次调用showAlert方法都会向alertView新增标题、文本、按钮等子视图,但旧子视图从未被清理,多次调用后子视图会层层叠加。
  2. 没有做弹窗存在性校验:如果未点击Dismiss就快速触发新弹窗,旧弹窗还没执行移除逻辑,新的视图会直接被添加到父视图上,导致多层弹窗重叠。
解决方案

方案1:复用现有单实例弹窗(适合同一时间仅展示一个弹窗的场景)

在showAlert方法的最开头加入清理逻辑,先清空旧视图再渲染新弹窗:

// MARK: Show the alert on the scene
func showAlert(with title: String,
               message: String,
               on viewController: UIViewController) {
    // 新增以下清理逻辑
    // 清空alertView上所有旧子视图
    alertView.subviews.forEach { $0.removeFromSuperview() }
    // 如果弹窗已在显示,先移除旧的弹窗视图、中断未完成的动画
    if alertView.superview != nil || backgroundView.superview != nil {
        alertView.layer.removeAllAnimations()
        backgroundView.layer.removeAllAnimations()
        alertView.removeFromSuperview()
        backgroundView.removeFromSuperview()
    }
    
    // 原有逻辑保持不变
    guard let targetView = viewController.view else {
        return
    }
    // ... 剩下的原有代码
}

同时可以在dismissAlert的完成回调里新增清空逻辑,进一步避免残留:

// 在dismissAlert的最终完成回调里补充
if done {
    self.alertView.removeFromSuperview()
    self.backgroundView.removeFromSuperview()
    // 新增:清空子视图、置空targetView
    self.alertView.subviews.forEach { $0.removeFromSuperview() }
    self.myTargetView = nil
}

方案2:每次弹窗创建独立实例(适合需要同时展示多个弹窗的场景)

直接将alertView、backgroundView的定义从类的全局属性移到showAlert方法内部,每次调用都创建新的视图实例,从根源避免复用冲突:

class MyAlert {
    struct Constants {
        static let backgroundAlphaTo: CGFloat = 0.6
    }
    
    // 移除原有全局的backgroundView、alertView定义
    
    private var myTargetView: UIView?
    // 新增属性持有当前弹窗的视图,用于dismiss操作
    private var currentAlertView: UIView?
    private var currentBackgroundView: UIView?
    
    // MARK: Show the alert on the scene
    func showAlert(with title: String,
                   message: String,
                   on viewController: UIViewController) {
        guard let targetView = viewController.view else {
            return
        }
        // 每次创建新的视图实例
        let backgroundView = UIView()
        backgroundView.backgroundColor = .black
        backgroundView.alpha = 0
        self.currentBackgroundView = backgroundView
        
        let alertView = UIView()
        alertView.backgroundColor = .white
        alertView.layer.masksToBounds = true
        alertView.layer.cornerRadius = 4
        self.currentAlertView = alertView
        
        // 剩下的原有逻辑中,把所有用到self.alertView、self.backgroundView的地方替换成当前的局部变量即可
        // ...
    }
    
    @objc func dismissAlert() {
        guard let targetView = myTargetView,
              let alertView = currentAlertView,
              let backgroundView = currentBackgroundView else {
            return
        }
        // 原有动画逻辑中替换对应的视图变量即可
        // ...
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 23:36:06