Swift自定义弹窗UIView未从Superview移除导致新弹窗重叠问题求解
问题根源
- 你的
MyAlert类全局持有唯一的alertView、backgroundView实例,每次调用showAlert方法都会向alertView新增标题、文本、按钮等子视图,但旧子视图从未被清理,多次调用后子视图会层层叠加。 - 没有做弹窗存在性校验:如果未点击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
相关产品推荐
相关产品推荐

