未从 Superview 中删除 UIView 警报

UIView alerts not being removed from Superview

我使用了 YouTube 视频中的一些代码(自定义提醒 Swift 5 (Xcode 11) - 2020 iOS App IOS Academy) 在 swift 中创建自定义警报,从 iPhone 屏幕顶部进入场景,停在中间屏幕上,然后当用户按下 OK 按钮时,警报通过向下滑动到场景底部退出屏幕。

问题: 问题是,当代码显示第一个警报时,一切似乎都很好,但是如果显示第二个或第三个不同的警报,似乎第一个警报子视图(或第二个/第三个等)没有从超级视图中正确删除,这意味着当显示第二个/第三个警报时,它显示在前一个警报的顶部。

我正在使用 Xcode v13.0。我怀疑它发生的原因是因为 removeFromSuperview 发生在一个 ObjC 函数中,该函数在创建子视图的地方之外。但是,我不知道如何解决这个问题。我尝试了很多不同的方法,并在高处和低处搜索了类似的例子。

查看控制器代码: 这是我的视图控制器的代码:

import UIKit

class ViewController: UIViewController {
    
    var myCounter : Int = 0
    
    @IBOutlet weak var myButton: UIButton!
    
    let customAlert = MyAlert()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        myButton.backgroundColor = .link
        myButton.setTitleColor(.white, for: .normal)
        myButton.setTitle("Show Alert", for: .normal)
        
    }

    @IBAction func didTapButton(_ sender: Any) {
        
        if myCounter > 3 {
            myCounter = 1
        }
        
        if myCounter == 1 {
            customAlert.showMySomethingAlert(on: self)
        }
        
        if myCounter == 2 {
            customAlert.showMySomethingAlert2(on: self)
        }
        
        if myCounter == 3 {
            customAlert.showMySomethingAlert3(on: self)
            
        }
        
        myCounter = myCounter + 1
        
    }
    
    @objc func dismissAlert() {
        customAlert.dismissAlert()
    }

}

CustomAlert Swift 文件:

import Foundation
import UIKit


class MyAlert {
    
    struct Constants {
        static let backgroundAlphaTo: CGFloat = 0.6
    }
    
    // MARK: Define the greying out of the scene
    private let backgroundView: UIView = {
        let backgroundView = UIView()
        backgroundView.backgroundColor = .black
        backgroundView.alpha = 0
        return backgroundView
    }()
    
    
    // MARK: Define the Alert frame
    private let alertView: UIView = {
        let alert = UIView()
        alert.backgroundColor = .white
        alert.layer.masksToBounds = true
        alert.layer.cornerRadius = 4
        return alert
    }()
    
    
    private let myView: UIView = {
        let myView = UIView()
        myView.translatesAutoresizingMaskIntoConstraints = false
        return myView
    }()
    
    
    private var myTargetView: 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
        }
        
        // MARK: Determine size of frame needed
        var numberOfLines = message.count / 40
        print("Number of lines is \(numberOfLines)")
        
        var heightOfAlert : CGFloat = 100.0
        
        heightOfAlert = heightOfAlert + (CGFloat(numberOfLines * 25))
        
        
        // MARK: Attributes for the frame
        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: heightOfAlert)
        
        
        
        
        
        // ***********************************************
        // MARK: Attributes for image within the alert
        let myImageButton = UIButton(frame: CGRect(x: 20, y: 20, width: 50, height: 50))
        myImageButton.setImage(UIImage(named:"HarbourBridge750x390.png"), for: .normal)
        alertView.addSubview(myImageButton)
        
        
        
        // ***********************************************
        // MARK: Attributes for the title within the alert
        let titleLabel = UILabel(frame: CGRect(x: 0,
                                               y: 0,
                                               width: alertView.frame.size.width,
                                               height: 80))
                                              
        titleLabel.text = title
        titleLabel.textAlignment = .center
        //titleLabel.textColor = .black
        titleLabel.textColor = UIColor(red: 33.00/255, green: 150.00/255, blue: 243.00/255, alpha: 1.00)
        titleLabel.font = UIFont.boldSystemFont(ofSize: 12.00)
        alertView.addSubview(titleLabel)
        
        
        
        // ***********************************************
        // MARK: Attributes for the message label within the alert
        let messageLabel = UILabel(frame: CGRect(x: 15,
                                               y: 80,
                                               width: alertView.frame.size.width-30,
                                               height: (CGFloat(numberOfLines * 25))))
                                              
        messageLabel.numberOfLines = 0
        messageLabel.text = message
        messageLabel.textAlignment = .left
        messageLabel.textColor = .black
        messageLabel.font = UIFont.systemFont(ofSize: 12.00)
        alertView.addSubview(messageLabel)
        
        
        
        
        
        
        // ***********************************************
        // MARK: Attributes for the Dismiss button within the alert
        let button = UIButton(frame: CGRect(x: 0,
                                            y: alertView.frame.size.height-50,
                                            width: alertView.frame.size.width,
                                            height: 50))
        
        button.setTitle("Dismiss", for: .normal)
        button.setTitleColor(.link, for: .normal)
        button.addTarget(self,
                         action: #selector(dismissAlert),
                         for: .touchUpInside)
        alertView.addSubview(button)
        
        
        // MARK: Attributes for the animation
        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
                
                })
            }
        })
    }
    
    
    // MARK: Dismiss the Alert
    @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: 300)
            
        }, completion: { done in
            if done {
                UIView.animate(withDuration: 0.25, animations: {
                    self.backgroundView.alpha = 0
                }, completion: { done in
                    if done {
                        
                        self.alertView.removeFromSuperview()
                        self.backgroundView.removeFromSuperview()
                        
                    }
                })
            }
        })
        
    }
    
    
    // MARK: Setup the alerts to keep the code tidy within the main body files

    func showMySomethingAlert(on vc: UIViewController) {
        showAlert(with: "My Something Alert", message: "Something has gone wrong and your payment was not successful.  This app will continue to operate in trial mode.", on: vc)
    }
    
    func showMySomethingAlert2(on vc: UIViewController) {
        showAlert(with: "My Something Alert", message: "This is a completely different alert and the text should be totally different and not overlap the previous alert whihc appears to be wht is happening", on: vc)
    }
    
    func showMySomethingAlert3(on vc: UIViewController) {
        showAlert(with: "My Something Alert", message: "THIS IS YOUR THIRD ALERT AND is a mixture of lower and UPPER CASE CHARACTERS.", on: vc)
    }
}

使用 UIView 超类对 MyAlert 进行子类化。

class MyAlert: UIView{
//......
}

您的问题出在您的 MyAlert class 中,您正在重用 alertView 的实例,但每次添加新的 myImageButton titleLabel messageLabelbutton

你第一次打电话给 customAlert.showMySomethingAlert(on: self) 你有

alertView1 
    //Childs
    myImageButton1
    titleLabel1
    messageLabel1
    button1

当您拨打 customAlert.showMySomethingAlert2(on: self) 时,您将拥有

alertView1 
        //Childs
        myImageButton1
        titleLabel1
        messageLabel1
        button1
       //Second instances
        myImageButton2
        titleLabel2
        messageLabel2
        button2

而且第一次添加的视图还在,所以你需要删除旧的或者重新使用它们会更好。

删除前。如果警报始终具有相同的视图,则不推荐 从其父视图中删除警报视图后,您需要执行此操作。

self.alertView.removeFromSuperview()
 //Add this to remove child views
 for subview in self.alertView.subviews {
     subview.removeFromSuperview()
 }

如果您的警报总是有保存视图,最好将新文本和图像设置为已创建的视图,而不是创建新的并在每次 showAlert 调用时添加。