如何正确子类化从 Swift 中的 xib 加载的视图类?

Posted

技术标签:

【中文标题】如何正确子类化从 Swift 中的 xib 加载的视图类?【英文标题】:How to properly subclass a view class which is loaded from xib in Swift? 【发布时间】:2015-01-25 12:41:22 【问题描述】:

我试图了解如何正确子类化从 Swift 中的 xib 加载的视图。

我有TitleDetailLabel 类,它是UIControl 的子类。这个类有titleLabeldetailLabel 出口,它们是UILabels。

class TitleDetailLabel: UIControl 
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var detailLabel: UILabel!

    override func awakeAfterUsingCoder(aDecoder: NSCoder) -> AnyObject? 
        return NTHAwakeAfterUsingCoder(aDecoder, nibName: "TitleDetailLabel")
    

    func setTitle(text: String) 
        self.titleLabel.text = text
    

    func setDetailText(text: String) 
        self.detailLabel.text = text
    

XIB结构:

占位符 文件所有者:NSObject(未更改) 第一响应者 标题详细标签 - UIView - TitleDetailLabel 类 标签 - UILabel - 标题标签 标签 - UILabel - 详细标签

在 Storyboard 中,我有视图控制器和占位符 - 带有约束的简单 UIView 对象。

我创建了UIView 类的扩展,以简化与我感兴趣的对象交换占位符。它适用于这个TitleDetailLabel 类。这是它的外观:

extension UIView 
    public func NTHAwakeAfterUsingCoder(aDecoder: NSCoder, nibName: String) -> AnyObject? 
        if (self.subviews.count == 0) 
            let nib = UINib(nibName: nibName, bundle: nil)
            let loadedView = nib.instantiateWithOwner(nil, options: nil).first as UIView

            /// set view as placeholder is set
            loadedView.frame = self.frame
            loadedView.autoresizingMask = self.autoresizingMask
            loadedView.setTranslatesAutoresizingMaskIntoConstraints(self.translatesAutoresizingMaskIntoConstraints())

            for constraint in self.constraints() as [NSLayoutConstraint] 
                var firstItem = constraint.firstItem as UIView
                if firstItem == self 
                    firstItem = loadedView
                

                var secondItem = constraint.secondItem as UIView?
                if secondItem != nil 
                    if secondItem! == self 
                        secondItem = loadedView
                    
                

                loadedView.addConstraint(NSLayoutConstraint(item: firstItem, attribute: constraint.firstAttribute, relatedBy: constraint.relation, toItem: secondItem, attribute: constraint.secondAttribute, multiplier: constraint.multiplier, constant: constraint.constant))
            

            return loadedView
        

        return self
    

我决定创建TitleDetailLabel 类的BasicTitleDetailLabel 子类,以保留一些配置代码和其他内容。

class BasicTitleDetailLabel: TitleDetailLabel 

    override func awakeFromNib() 
        super.awakeFromNib()
        self.setup()
    

    override init(coder aDecoder: NSCoder) 
        super.init(coder: aDecoder)
        self.setup()
    

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

    private func setup() 
        self.titleLabel.textColor = UIColor.NTHCadetGrayColor()
        self.detailLabel.textColor = UIColor.NTHLinkWaterColor()
    

但每次我将此占位符的类从 TitleDetailLabel 更改为 BasicTitleDetailLabel 后,应用程序都会崩溃。

应用程序崩溃,因为 titleLabeldetailLabel 为 nil。

如何正确使用这个 TitleDetailLabel 类和 xib 以及如何正确子类化它?我不想创建另一个看起来像第一个使用子类的 xib。

提前致谢。

【问题讨论】:

你能发布崩溃返回的错误吗? 应用程序崩溃是因为在super.init(coder: aDecoder) 之后我调用了self.setup(),而此时的出口是nil (Unexpectedly found nil...)。 awakeAfterUsingCoder: 返回 TitleDetailLabel 类型的对象。我读到一些东西,这是不可能使用 xibs 创建不同类的对象的。 你为什么假设在初始化之后出口就在附近?在加载 xib 之后,它们才会出现。如果您只是在尝试访问它们之前检查标签不为零,它是否有效?为了更清楚地说明这一点,您可以将 outlet 设置为可选项,并在访问它们之前使用 if let 嗨@TomaszSzulc,你有没有找到任何解决方案。我也面临同样的问题。 【参考方案1】:

确保将 .xib 文件的文件所有者设置为 .swift 文件。还要为根视图添加一个出口,然后从代码中加载 xib。这就是我为类似项目所做的:

import UIKit
class ResuableCustomView: UIView 

    @IBOutlet var view: UIView!
    @IBOutlet weak var label: UILabel!
    @IBAction func buttonTap(sender: UIButton) 
        label.text = "Hi"
    

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

        NSBundle.mainBundle().loadNibNamed("ReusableCustomView", owner: self, options: nil)[0] as! UIView
        self.addSubview(view)
        view.frame = self.bounds
    

它与您的略有不同,但您可以添加 init:frame 方法。如果您有 awakeFromNib 方法,则不要在 awakeFromNibinit:coder 中加载 setup 方法。

我对上述代码项目的完整答案是here 或观看this video。

【讨论】:

我见过的最佳答案

以上是关于如何正确子类化从 Swift 中的 xib 加载的视图类?的主要内容,如果未能解决你的问题,请参考以下文章

Swift 3 - 从 xib 加载的 UITableViewCell - 自动布局和高度不正确

如何使用Swift 3从Xib加载NSView

从 XIB 加载自定义视图,如何使其响应自定义子类?

如何在 swift 中使用 .xib 创建 UIViewController 子类?

如何在不使用 Nib 的情况下实例化和调用 UIView 子类

从 xib 加载子类时如何设置子类 UIView 属性的值