UIView 子类中的 self.removeFromSuperview()

Posted

技术标签:

【中文标题】UIView 子类中的 self.removeFromSuperview()【英文标题】:self.removeFromSuperview() within UIView subclass 【发布时间】:2016-08-03 22:04:12 【问题描述】:

背景: 我创建了一个 UIView (helloView) 的子类来在我的 MainViewController 视图上显示一些文本和一个按钮。 这个子类在MainVC中初始化viewDidLoad并添加到视图中:view.addSubview(helloView)

目标:helloView 有一个 UIButton (continueButton),目标为 dimissHelloView。这个方法简单地调用self.removeFromSuperView(),以便简单地在屏幕上获取helloView。

问题:用户与按钮的交互会破坏代码并返回 nil 指针异常(在展开 Optional 值时意外发现 nil)。尽管在 SO 和 Google 上搜索了几个小时,但我还是找不到通过其自己的按钮子视图关闭视图的正确方法。

以前的故障排除:调用removeFromSuperview时superview似乎是nil,但如果我使用断点并在控制台中调用它不是。

代码:忽略属性和约束部分,按钮创建和目标动作在底部。

import UIKit

class HelloView: UIView 

    var helloText: String = ""
    var continueButton: UIButton!

    override init(frame: CGRect) 
        super.init(frame: frame)

        didLoad()
    

    required init?(coder aDecoder: NSCoder) 
        super.init(coder: aDecoder)

        didLoad()
    

    func didLoad()

        self.translatesAutoresizingMaskIntoConstraints = false
        setContinueButton()
        self.addSubview(continueButton)
    

    override func didMoveToSuperview() 
        setConstraints()
    

    func setConstraints()

        //remove any previous constraints
        for constraint in superview!.constraints 
            if constraint.firstItem as! UIView == self && constraint.secondItem as? UIView == superview 
                superview!.removeConstraint(constraint)
            
        

        // Hello View basic constraints
        let xAxisConstraint = NSLayoutConstraint(item: self, attribute: .CenterX, relatedBy: .Equal, toItem: superview, attribute: .CenterX, multiplier: 1, constant: 0)
        let yAxisConstraint = NSLayoutConstraint(item: self, attribute: .CenterY, relatedBy: .Equal, toItem: superview, attribute: .CenterY, multiplier: 1, constant: 0)

        // check device orientation and add constraints consequently
        if UIDeviceOrientationIsLandscape(UIDevice.currentDevice().orientation) 
            let widthConstraint = NSLayoutConstraint(item: self, attribute: .Width, relatedBy: .Equal, toItem: superview, attribute: .Width, multiplier: 1, constant: -100)
            let heightConstraint = NSLayoutConstraint(item: self, attribute: .Height, relatedBy: .Equal, toItem: superview, attribute: .Height, multiplier: 1, constant: -150)
            superview!.addConstraints([xAxisConstraint, yAxisConstraint, widthConstraint, heightConstraint])
        
        if UIDeviceOrientationIsPortrait(UIDevice.currentDevice().orientation)
            let widthConstraint = NSLayoutConstraint(item: self, attribute: .Width, relatedBy: .Equal, toItem: superview, attribute: .Width, multiplier: 1, constant: -100)
            let heightConstraint = NSLayoutConstraint(item: self, attribute: .Height, relatedBy: .Equal, toItem: superview, attribute: .Height, multiplier: 1, constant: -300)
            superview!.addConstraints([xAxisConstraint, yAxisConstraint, widthConstraint, heightConstraint])
        

        // Continue Button constraints
        let centerXConstraint = NSLayoutConstraint(item: continueButton, attribute: .CenterX, relatedBy: .Equal, toItem: self, attribute: .CenterX, multiplier: 1, constant: 0)
        let bottomConstraint = NSLayoutConstraint(item: continueButton, attribute: .Bottom, relatedBy: .Equal, toItem: self, attribute: .Bottom, multiplier: 1, constant: -8)
        self.addConstraints([centerXConstraint, bottomConstraint])
    

    func setContinueButton()
        continueButton = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 20))
        continueButton.setTitle("Continue", forState: .Normal)
        continueButton.setTitleColor(UIColor.whiteColor(), forState: .Normal)
        continueButton.addTarget(self, action: #selector(HelloView.dismissHelloView), forControlEvents: .TouchUpInside)
        continueButton.translatesAutoresizingMaskIntoConstraints = false
    

    func dismissHelloView()
        print(superview!)
        self.removeFromSuperview()
    


【问题讨论】:

【参考方案1】:

当您从其父视图中删除视图时,didMoveToSuperview 将被调用(父视图将为 nil)。你的setConstraints 函数是从didMoveToSuperview 调用的,它强制解开superview,它为nil,导致崩溃。可选项的全部意义在于它迫使您考虑如果某些东西为零会发生什么。

无论如何,在您的情况下,您不希望在 superview 变为 nil 时发生任何事情,所以我会在 didMoveToSuperview 的顶部放置这样的内容:

guard superview != nil else  return 

【讨论】:

非常感谢@zpasternack,我没有意识到在 removeFromSuperview 上调用了 didMoveToSuperview!添加提前返回的保护语句解决了这个问题。

以上是关于UIView 子类中的 self.removeFromSuperview()的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Swift 中的 ViewController 获取 UIView 子类实例

XIB 中的 UIView 子类不显示(drawRect:从未调用)

如何覆盖子类的 swift 协议函数(例如 UIView 中的 UILabel)

UIScrollView 中的 UIView 子类如何处理内存

Swift 中的 UIView 子类:IBOutlet 在展开时发现为零

UIView 子类在 Swift 中设置自己的高度 + UIViewController 中的约束 + 情节提要