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

Swift:登录场景下触发自定义Alert时如何避免视图重叠问题

问题原因

你遇到的弹窗重叠问题来自两个核心逻辑漏洞:

  1. 每次调用showAlert方法时,都会向全局复用的alertView上新增标题、文本、按钮三个子视图,但dismiss时仅移除了alertView本身,没有清理其内部的旧子视图,多次调用后子视图堆叠就会出现文字重影
  2. 没有加弹窗显示状态的判断,前一个弹窗还在展示/动画过程中时,再次触发showAlert会重复向当前页面添加背景层和弹窗层,导致多层视图叠加
修复方案

直接修改你的CustomAlert类即可,修改点如下:

  1. 新增显示状态标记位,避免同时弹出多个弹窗
  2. 每次展示新弹窗前,先清理alertView的旧子视图
  3. 动画结束后同步更新状态标记

修改后的完整CustomAlert代码如下:

class CustomAlert{
    
    struct Constants {
        static let backgroundAlphaTo: CGFloat = 0.6
    }
    
    // 标记当前是否有弹窗正在展示
    private var isShowing = false
    
    private let backgroundView: UIView = {
        let backgroundView = UIView()
        backgroundView.backgroundColor = .black
        backgroundView.alpha = 0
        return backgroundView
    }()
    
    private let alertView: UIView = {
        let alertView = UIView()
        alertView.backgroundColor = UIColor(rgb: 0x363636, alphaVal: 1)
        alertView.layer.masksToBounds = true
        alertView.layer.cornerRadius = 12
        return alertView
    }()
    
    private var myTargetView: UIView?
    
    func showAlert(with title: String, message: String, on ViewController: UIViewController){
        // 如果已有弹窗正在展示,直接返回不重复弹出;如果需要替换旧弹窗可以改为 if isShowing { dismissAlert() }
        guard !isShowing else { return }
        
        guard let targetView = ViewController.view else{
            return
        }
        
        // 清理弹窗上的旧子视图,避免内容重叠
        alertView.subviews.forEach { $0.removeFromSuperview() }
        
        isShowing = true
        myTargetView = targetView
        backgroundView.frame = targetView.bounds
        targetView.addSubview(backgroundView)
        targetView.addSubview(alertView)
        alertView.frame = CGRect(x: 40,
                                 y: -300,
                                 width: targetView.frame.size.width-80,
                                 height: 180)
       
        let titleLabel = UILabel(frame: CGRect(x: 20,
                                               y: 10,
                                               width: alertView.frame.size.width,
                                               height: 40))
        titleLabel.text = title
        titleLabel.textAlignment = .left
        titleLabel.font = UIFont(name: "Roboto-Regular", size: 20)
        titleLabel.textColor = .white
        
        alertView.addSubview(titleLabel)
        
        let messageLabel = UILabel(frame: CGRect(x: 20,
                                          y: 40,
                                          width: alertView.frame.size.width-30,
                                          height: 60))
        
        
        messageLabel.text = message
        messageLabel.font = UIFont(name: "Roboto-Thin", size: 18)
        messageLabel.textColor = .white
        messageLabel.numberOfLines = 0
        messageLabel.textAlignment = .left
        
        
        alertView.addSubview(messageLabel)
        
        let button = UIButton (frame: CGRect(x: alertView.frame.size.width-130,
                                             y: alertView.frame.size.height-70,
                                             width: (alertView.frame.midX)/2,
                                             height: 50))
        
        button.setTitle("Okay", for: .normal)
        button.applyGradient(colors: [Helper.UIColorFromRGB(0x694BCE).cgColor,Helper.UIColorFromRGB(0x7D3297).cgColor])
        button.addTarget(self, action: #selector(dismissAlert), for: .touchUpInside)
        alertView.addSubview(button)
        
        UIView.animate(withDuration: 0.25, animations: {
            self.backgroundView.alpha = Constants.backgroundAlphaTo},
                       completion: {done in
                        if done{
                            UIView.animate(withDuration: 0.25, animations: {
                                self.alertView.center = targetView.center
                            })
                        }
                       })
    }
    
    @objc func dismissAlert(){
        guard let targetView = myTargetView else{
            return
        }
        UIView.animate(withDuration: 0.25, animations: {
            self.alertView.frame = CGRect(x: 40,
                                          y: targetView.frame.size.height,
                                     width: targetView.frame.size.width-80,
                                     height: 180)
        },
                       completion: { [weak self] done in
                        guard let self = self else { return }
                        if done{
                            UIView.animate(withDuration: 0.25, animations: {
                                self.backgroundView.alpha = 0
                            }, completion: { done in
                                if done {
                                    self.alertView.removeFromSuperview()
                                    self.backgroundView.removeFromSuperview()
                                    // 弹窗完全消失后更新状态
                                    self.isShowing = false
                                }
                            })
                        }
                       })
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 18:39:03