如何将Xib文件作为自定义类加载?

时间:2019-06-20 11:10:13

标签: ios swift xib

我正在尝试使用xib文件创建可重复使用的组件(在同一View中多次),一切都很好,直到我想从@IBInspectable属性更新某些控件。我发现当时没有设置@IBOutlet,所以我进行了搜索并发现了一些东西

http://justabeech.com/2014/07/27/xcode-6-live-rendering-from-nib/

他正在将已加载的视图保存到proxyView中,因此您可以在@IBInspectable中使用它。不幸的是,该代码有点旧,无法按原样工作。但是我的问题是,当我尝试将笔尖作为一个类加载时,它不起作用。仅当我将其加载为UIView时,它才有效。

此行失败

return bundle.loadNibNamed("test", owner: nil, options: nil)?[0] as? ValidationTextField

仅在这样的情况下有效

return bundle.loadNibNamed("test", owner: nil, options: nil)?[0] as? UIView

我认为问题是,xib文件的所有者标记为ValidationTextField,但主视图是UIView。因此,当您加载笔尖时,它会带来UIView,显然没有自定义属性或出口。

许多有关加载xib文件的示例都说该类必须位于文件所有者中。所以我不知道如何使用该类。

import UIKit

@IBDesignable class ValidationTextField: UIView {
    @IBOutlet var lblError: UILabel!
    @IBOutlet var txtField: XTextField!
    @IBOutlet var imgWarning: UIImageView!
    private var proxyView: ValidationTextField?

    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    required init(coder: NSCoder) {
        super.init(coder: coder)!
    }
    override func awakeFromNib() {
        super.awakeFromNib()
        xibSetup()
    }
    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        xibSetup()
        self.proxyView?.prepareForInterfaceBuilder()
    }
    func xibSetup() {
        guard let view = loadNib() else { return }
        view.frame = bounds
        view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        addSubview(view)
        // Saving the view in a variable
        self.proxyView = view
    }
    func loadNib() -> ValidationTextField? {
        let bundle = Bundle(for: type(of: self))
        return bundle.loadNibNamed("test", owner: nil, options: nil)?[0] as? ValidationTextField
    }
    @IBInspectable var fontSize: CGFloat = 14.0 {
        didSet {
            let font = UIFont(name: "System", size: self.fontSize)
            self.proxyView!.txtField.font = font
            self.proxyView!.lblError.font = font
        }
    }
}

我什至不知道其余的是否有效。如果该链接说的是真的,那么在装入笔尖后获取视图将使我能够访问商店。

在此行运行该代码失败

guard let view = loadNib() else { return }

我猜想它无法将UIView转换为该类,因此它返回nil,然后退出。

我的目标是拥有一个可重用的组件,该组件可以在单个控制器中放置多次。并能够在情节提要中查看其设计。

1 个答案:

答案 0 :(得分:1)

xibSetup()移至初始化程序。 awakeFromNib的调用为时已晚,如果以编程方式创建视图,则不会调用它。无需在prepareForInterfaceBuilder中调用它。

简而言之,可以概括为:

open class NibLoadableView: UIView {
    public override init(frame: CGRect) {
        super.init(frame: frame)
        loadNibContentView()
        commonInit()
    }

    public required init?(coder: NSCoder) {
        super.init(coder: coder)
        loadNibContentView()
        commonInit()
    }

    public func commonInit() {
       // to be overriden
    }
}

public extension UIView {
    // @objc makes it possible to override the property
    @objc
    var nibBundle: Bundle {
        return Bundle(for: type(of: self))
    }

    // @objc makes it possible to override the property
    @objc
    var nibName: String {
        return String(describing: type(of: self))
    }

    @discardableResult
    func loadNibContentView() -> UIView? {
        guard
            // note that owner = self !!!
            let views = nibBundle.loadNibNamed(nibName, owner: self, options: nil),
            let contentView = views.first as? UIView
        else {
            return nil
        }

        addSubview(contentView)
        contentView.translatesAutoresizingMaskIntoConstraints = true
        contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        contentView.frame = self.bounds

        return contentView
    }
}

请注意,加载笔尖的视图必须是该视图的 owner

然后您的班级将变为:

@IBDesignable
class ValidationTextField: NibLoadableView {
    @IBOutlet var lblError: UILabel!
    @IBOutlet var txtField: XTextField!
    @IBOutlet var imgWarning: UIImageView!

    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        commonInit()
    }

    override func commonInit() {
        super.commonInit()
        updateFont()
    }

    @IBInspectable var fontSize: CGFloat = 14.0 {
        didSet {
            updateFont()
        }
    }

    private func updateFont() {
       let font = UIFont.systemFont(ofSize: fontSize)
       txtField.font = font
       lblError.font = font
    }
}

我想关于代理对象的整个想法来自滥用Nib所有者。对于代理对象,层次结构必须是这样的:

ValidationTextField
    -> ValidationTextField (root view of the nib)
          -> txtField, lblError, imgWarning

这没有多大意义。我们真正想要的是:

ValidationTextField (nib owner)
    -> UIView (root view of the nib)
          -> txtField, lblError, imgWarning