【问题标题】:UIView alerts not being removed from SuperviewUIView 警报未从 Superview 中删除
【发布时间】:2021-12-31 18:36:33
【问题描述】:

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

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

我正在使用 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)
    }
}

【问题讨论】:

    标签: ios swift uiview alerts removefromsuperview


    【解决方案1】:

    您的问题在您的MyAlert 类中,您正在重用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 时添加。

    【讨论】:

    • 太棒了!!这确实解决了这个问题,也非常感谢你向我解释子实例,我知道我错过了一些“理解”,这是我无法解决这个问题的部分原因。您的回答非常有意义,并且确实有效。我很惊讶没有人(迄今为止)对 YouTube iOS 学院视频发表评论指出给出的代码示例中的地板。再次谢谢谢谢谢谢谢谢-谢谢:)。
    • @JamesLeitch 很高兴我的回答对您有所帮助。我也在向别人学习。
    【解决方案2】:

    使用 UIView 超类继承 MyAlert。

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

    【讨论】:

      猜你喜欢
      • 2020-04-28
      • 1970-01-01
      • 2019-08-09
      • 2019-04-08
      • 2023-03-11
      • 1970-01-01
      • 2011-04-11
      • 1970-01-01
      • 2019-11-22
      相关资源
      最近更新 更多