将 XIB 文件加载到 UIView Swift

Loading a XIB file to a UIView Swift

我正在尝试将我的 XIB 文件加载到 UIView 中,但我遇到了一些问题。我有所需的覆盖功能,但它们似乎崩溃了。说这个错误,警告:

could not load any Objective-C class information. This will significantly reduce the quality of type information available.

我想知道是否有人可以告诉我如何正确地将 XIB 文件加载到 UIView

import UIKit

class Widget: UIView {

    let view = UIView()

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

        //call function

        loadNib()

    }

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

        loadNib()

        //fatalError("init(coder:) has not been implemented")
    }

    func loadNib() {
        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: "nib", bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
        view.frame = bounds
        view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        self.addSubview(view);  
    }
}

通常我使用下面的方式来加载自定义的xib文件UIView:

NSBundle.mainBundle().loadNibNamed(nibName, owner: self, options: nil)[0];
let xibView = NSBundle.mainBundle().loadNibNamed("NameXibView", owner: nil, options: nil)[0] as! UIView

我在我们的一个项目中使用了它,可能对你有用

import UIKit

class RegisterPageView: UIView {
    
        class func instanceFromNib() -> RegisterPageView {
            return UINib(nibName: "RegisterPageView", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as! RegisterPageView
        }
}
 

使用 Swift 3.0

let viewFromNib: UIView? = Bundle.main.loadNibNamed("NibName", 
    owner: nil, 
    options: nil)?.first

对于swift 3

class YourClass:  UIView {
    class func instanceFromNib() -> YourClass {
        return UINib(nibName: "YourClassNibName", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! YourClass
    }
}

在我的项目中,我实现了以下(与 Peter 的解决方案非常相似)

import UIKit

// MARK: - Protocol Declaration

public protocol InterfaceBuilderInstantiable
{
    /// The UINib that contains the view
    ///
    /// Defaults to the swift class name if not implemented
    static var associatedNib : UINib { get }
}

// MARK: - Default Implementation

extension InterfaceBuilderInstantiable
{
    /// Creates a new instance from the associated Xib
    ///
    /// - Returns: A new instance of this object loaded from xib
    static func instantiateFromInterfaceBuilder() -> Self
    {
        return associatedNib.instantiate(withOwner:nil, options: nil)[0] as! Self
    }

    static var associatedNib : UINib
    {
        let name = String(describing: self)
        return UINib(nibName: name, bundle: Bundle.main)
    }
}

要使用,您只需简单地实现协议:

class MyView: UIView, InterfaceBuilderInstantiable
{
    // The rest

并且如果您的笔尖与您的 class (MyView.xib) 同名,则您已设置:协议的默认实现查找与 class 在主包中。

当然,如果您的笔尖在另一个包中或具有不同的名称,您可以覆盖 associatedNib 和 return 您自己的笔尖。

这是我的方法(写在Swift 3.1):

protocol XibDesignable : class {}

extension XibDesignable where Self : UIView {

    static func instantiateFromXib() -> Self {

        let dynamicMetatype = Self.self
        let bundle = Bundle(for: dynamicMetatype)
        let nib = UINib(nibName: "\(dynamicMetatype)", bundle: bundle)

        guard let view = nib.instantiate(withOwner: nil, options: nil).first as? Self else {

            fatalError("Could not load view from nib file.")
        }
        return view
    }
}

extension UIView : XibDesignable {}

现在我可以像这样 MyCustomView.instantiateFromXib() 从 Xib(假设有一个)创建任何 UIView 子类。请记住将您的 Xib 文件命名为您的 UIView 子类并正确设置该 Xib 文件中主视图的类型。


一旦 SE-0068 实施,就可以删除协议并将功能直接移动到 UIView 扩展中。


请注意:原始 post 使用带有嵌套视图的常用模式。恕我直言,这是一个糟糕的模式,它不利用资源,只会创建不必要的视图层次结构。

改进了 UIView 扩展

public extension UIView
{
    static func loadFromXib<T>(withOwner: Any? = nil, options: [UINib.OptionsKey : Any]? = nil) -> T where T: UIView
    {
        let bundle = Bundle(for: self)
        let nib = UINib(nibName: "\(self)", bundle: bundle)

        guard let view = nib.instantiate(withOwner: withOwner, options: options).first as? T else {
            fatalError("Could not load view from nib file.")
        }
        return view
    }
}

用法

let view = CustomView.loadFromXib()
let view = CustomView.loadFromXib(withOwner: self)
let view = CustomView.loadFromXib(withOwner: self, options: [UINibExternalObjects: objects])

External Objects discussion

Swift 4.x

let myView = Bundle.main.loadNibNamed("yourXibView", owner: nil, options: nil)![0] as! UIView
  func configureNib() -> UIView {
    let bundle = Bundle(for: type(of: self))
    let nib = UINib(nibName: "CustomUIView", bundle: bundle)
    let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
    return view
}

并将本教程用于 xib 的自定义视图... https://developerfly.com/custom-view-use-xib-swift/

Swift 4.x

我终于做到了 这不在 customView 本身中。我把代码放在 ViewController 加载 customView 的地方。

import UIKit

class StartMenuViewController: UIViewController {

    @IBOutlet weak var customView: CustomView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let myView = Bundle.main.loadNibNamed("CustomView", owner: self, options: nil)![0] as! UIView
        customView .addSubview(myView)
    }

我想分享这段代码,它需要我付出一些努力才能使其具有弹性。

import Foundation

protocol Nib {

    func registerNib()

}

extension Nib where Self : UIView {

    func registerNib() {
        guard let nibName = type(of: self).description().components(separatedBy: ".").last else { return }
        // ** Check if resource is used in Interface Builder first to avoid crash during compile
        #if !TARGET_INTERFACE_BUILDER
        let bundle = Bundle(for: type(of: self))
        guard let _ = bundle.path(forResource: nibName, ofType: "nib")
            else { fatalError("can't find \(nibName) xib resource in current bundle") }
        #endif
        guard let view = Bundle(for: type(of: self)).loadNibNamed(nibName, owner: self, options: nil)?.first as? UIView
            else { return }
        // ** Another way to write it but do not work if xib is bundled with framework
        //guard let view = UINib(nibName: nibName, bundle: nil).instantiate(withOwner: self, options: nil).first as? UIView
        //    else { return }
        view.frame = bounds
        addSubview(view)
    }

}

您可以使用它创建一个名为 class 名称(又名 CustomView.xib)

的 xib 资源文件
import UIKit

class CustomView: UIView, Nib {

    override init(frame: CGRect) {
        super.init(frame: frame)
        postInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        postInit()
    }

    func postInit() {
        registerNib()
    }

}

不要忘记将 xib 资源文件的所有者 class 设置为 CustomView 并将自定义 class class 字段留空。

Swift 5.x

let loadMusicView = Bundle.main.loadNibNamed("MusicView", owner: nil, options: nil)![0] as? MusicView
loadMusicView?.frame = controlsMainView.bounds
loadMusicView?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
controlsMainView.addSubview(loadMusicView!)

//if you have variables in your .xib file access those variables like this
loadMusicView.yourVariableName = .....

//**只需将此 class 用作视图的超级 class **

import UIKit

class ViewWithXib: UIView {

func initUI() {}

private func xibSetup() {
    let view = loadViewFromNib()
    view.frame = bounds
    view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
    addSubview(view)
    initUI()
}

private func loadViewFromNib() -> UIView {
    let thisName = String(describing: type(of: self))
    let view = Bundle(for: self.classForCoder).loadNibNamed(thisName, owner: self, options: nil)?.first as! UIView
    return view
}


override init(frame: CGRect) {
    super.init(frame: frame)
    xibSetup()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    xibSetup()
}

}

// 用法

class HeaderView: ViewWithXib {
}


let header = HeaderView() // No need to load the view from nib, It will work