如何用 Swift 中的 XIB 文件初始化/实例化自定义 UIView 类

我有一个名为 MyClass的类,它是 UIView的一个子类,我想用一个 XIB文件对它进行初始化。我不确定如何用名为 View.xib的 xib 文件初始化这个类

class MyClass: UIView {


// what should I do here?
//init(coder aDecoder: NSCoder) {} ??
}
206003 次浏览

我测试了这个代码,它工作得很好:

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

初始化视图并像下面这样使用它:

var view = MyClass.instanceFromNib()
self.view.addSubview(view)

OR

var view = MyClass.instanceFromNib
self.view.addSubview(view())

更新 Swift > = 3.x & Swift > = 4. x

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

从 Swift 2.0开始,您可以添加一个协议扩展。在我看来,这是一种更好的方法,因为返回类型是 Self而不是 UIView,所以调用者不需要强制转换到视图类。

import UIKit


protocol UIViewLoading {}
extension UIView : UIViewLoading {}


extension UIViewLoading where Self : UIView {


// note that this method returns an instance of type `Self`, rather than UIView
static func loadFromNib() -> Self {
let nibName = "\(self)".characters.split{$0 == "."}.map(String.init).last!
let nib = UINib(nibName: nibName, bundle: nil)
return nib.instantiateWithOwner(self, options: nil).first as! Self
}


}

Sam 的解决方案已经很棒了,尽管它没有考虑不同的捆绑包(NSBundle: forClass 解决了这个问题) ,并且需要手动加载,也就是输入代码。

如果希望对 Xib Outlet 提供完全支持,请使用不同的 bundle (在框架中使用!)在情节串连板中获得一个不错的预览试试这个:

// NibLoadingView.swift
import UIKit


/* Usage:
- Subclass your UIView from NibLoadView to automatically load an Xib with the same name as your class
- Set the class name to File's Owner in the Xib file
*/


@IBDesignable
class NibLoadingView: UIView {


@IBOutlet weak var view: UIView!


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


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


private func nibSetup() {
backgroundColor = .clearColor()


view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
view.translatesAutoresizingMaskIntoConstraints = true


addSubview(view)
}


private func loadViewFromNib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: String(self.dynamicType), bundle: bundle)
let nibView = nib.instantiateWithOwner(self, options: nil).first as! UIView


return nibView
}


}

像往常一样使用 xib,即连接 Outlets 到 File Owner,并将 File Owner 类设置为您自己的类。

用法: 从 NibLoadingView 中继承自己的 View 类,并在 Xib 文件中将类名设置为 文件的所有者

不再需要额外的代码。

信用在哪里到期: 叉这与小的变化,从丹无头在 GH。我的要点: https://gist.github.com/winkelsdorf/16c481f274134718946328b6e2c9a4d8

这是 Frederik 在 Swift 3.0上的回答

/*
Usage:
- make your CustomeView class and inherit from this one
- in your Xib file make the file owner is your CustomeView class
- *Important* the root view in your Xib file must be of type UIView
- link all outlets to the file owner
*/
@IBDesignable
class NibLoadingView: UIView {


@IBOutlet weak var view: UIView!


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


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


private func nibSetup() {
backgroundColor = .clear


view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.translatesAutoresizingMaskIntoConstraints = true


addSubview(view)
}


private func loadViewFromNib() -> UIView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
let nibView = nib.instantiate(withOwner: self, options: nil).first as! UIView


return nibView
}
}

从 xib 载入视图的通用方法:

例如:

let myView = Bundle.loadView(fromNib: "MyView", withType: MyView.self)

实施方法:

extension Bundle {


static func loadView<T>(fromNib name: String, withType type: T.Type) -> T {
if let view = Bundle.main.loadNibNamed(name, owner: nil, options: nil)?.first as? T {
return view
}


fatalError("Could not load view with type " + String(describing: type))
}
}

Swift 3回答: 在我的例子中,我想在自定义类中有一个可以修改的出口:

class MyClassView: UIView {
@IBOutlet weak var myLabel: UILabel!
    

class func createMyClassView() -> MyClassView {
let myClassNib = UINib(nibName: "MyClass", bundle: nil)
return myClassNib.instantiate(withOwner: nil, options: nil)[0] as! MyClassView
}
}

在.xib 文件中,确保 Custom Class 字段是 MyClassView。不要打扰文件的所有者。

Make sure Custom Class is MyClassView

另外,确保将 MyClassView 中的插座连接到标签: Outlet for myLabel

举例来说:

let myClassView = MyClassView.createMyClassView()
myClassView.myLabel.text = "Hello World!"
override func draw(_ rect: CGRect)
{
AlertView.layer.cornerRadius = 4
AlertView.clipsToBounds = true


btnOk.layer.cornerRadius = 4
btnOk.clipsToBounds = true
}


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


@IBAction func okBtnDidClicked(_ sender: Any) {


removeAlertViewFromWindow()


UIView.animate(withDuration: 0.4, delay: 0.0, options: .allowAnimatedContent, animations: {() -> Void in
self.AlertView.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)


}, completion: {(finished: Bool) -> Void in
self.AlertView.transform = CGAffineTransform.identity
self.AlertView.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
self.AlertView.isHidden = true
self.AlertView.alpha = 0.0


self.alpha = 0.5
})
}




func removeAlertViewFromWindow()
{
for subview  in (appDel.window?.subviews)! {
if subview.tag == 500500{
subview.removeFromSuperview()
}
}
}




public func openAlertView(title:String , string : String ){


lblTital.text  = title
txtView.text  = string


self.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
appDel.window!.addSubview(self)




AlertView.alpha = 1.0
AlertView.isHidden = false


UIView.animate(withDuration: 0.2, animations: {() -> Void in
self.alpha = 1.0
})
AlertView.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)


UIView.animate(withDuration: 0.3, delay: 0.2, options: .allowAnimatedContent, animations: {() -> Void in
self.AlertView.transform = CGAffineTransform(scaleX: 1.1, y: 1.1)


}, completion: {(finished: Bool) -> Void in
UIView.animate(withDuration: 0.2, animations: {() -> Void in
self.AlertView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)


})
})




}

Swift 4

在我的例子中,我必须将数据传递到自定义视图中,因此我创建静态函数来实例化视图。

  1. 创建 UIView 扩展

    extension UIView {
    class func initFromNib<T: UIView>() -> T {
    return Bundle.main.loadNibNamed(String(describing: self), owner: nil, options: nil)?[0] as! T
    }
    }
    
  2. Create MyCustomView

    class MyCustomView: UIView {
    
    
    @IBOutlet weak var messageLabel: UILabel!
    
    
    static func instantiate(message: String) -> MyCustomView {
    let view: MyCustomView = initFromNib()
    view.messageLabel.text = message
    return view
    }
    }
    
  3. Set custom class to MyCustomView in .xib file. Connect outlet if necessary as usual. enter image description here

  4. Instantiate view

    let view = MyCustomView.instantiate(message: "Hello World.")
    

如果有人想用 XIB 以编程方式加载自定义视图,下面的代码将完成这项工作。

let customView = UINib(nibName:"CustomView",bundle:.main).instantiate(withOwner: nil, options: nil).first as! UIView
customView.frame = self.view.bounds
self.view.addSubview(customView)

斯威夫特5.3

创建一个名为 NibLoadingView的类,其内容如下:

import UIKit


/* Usage:
- Subclass your UIView from NibLoadView to automatically load an Xib with the same name as your class
- Set the class name to File's Owner in the Xib file
*/


@IBDesignable
class NibLoadingView: UIView {


@IBOutlet public weak var view: UIView!
    

private var didLoad: Bool = false


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

self.nibSetup()
}


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

self.nibSetup()
}
    

open override func layoutSubviews() {
super.layoutSubviews()
        

if !self.didLoad {
self.didLoad = true
self.viewDidLoad()
}
}
    

open func viewDidLoad() {
self.setupUI()
}


private func nibSetup() {
self.view = self.loadViewFromNib()
self.view.frame = bounds
self.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.view.translatesAutoresizingMaskIntoConstraints = true


addSubview(self.view)
}


private func loadViewFromNib() -> UIView {
guard let nibName = type(of: self).description().components(separatedBy: ".").last else {
fatalError("Bad nib name")
}
        

if let defaultBundleView = UINib(nibName: nibName, bundle: Bundle(for: type(of: self))).instantiate(withOwner: self, options: nil).first as? UIView {
return defaultBundleView
} else {
fatalError("Cannot load view from bundle")
}
}
}

现在创建一个 XIB 和 UIView 类对,将 XIB 的所有者设置为 UIView 类和子类 NibLoadingView

你现在可以像 ExampleView()ExampleView(frame: CGRect)等等一样初始化这个类,或者直接从故事板中进行初始化。

你也可以像在 UIViewController中一样使用 viewDidLoad。你所有的插座和布局都是在那一刻呈现的。

基于 弗雷德里克的回答

从.xib 创建视图

let nib = UINib(nibName: "View1", bundle: nil) //View1 is a file name(View1.swift)
if let view = nib.instantiate(withOwner: self, options: nil).first as? UIView {
// logic
}


//or


if let view = Bundle.main.loadNibNamed("View1", owner: self, options: nil)?.first as? UIView {
// logic
}

因为. xib 可以包含多个视图,这就是为什么要在这里使用 array (.first)

比如说

  1. 创建 View1.xib
  2. 在代码中创建 View1.ift,其中 set owner (loadNibNamed())用于创建 class ("View1")的实例
  3. View1.xib中的 File's Owner设置为 View1。允许连接插座和操作
import UIKit


class View1: UIView {
@IBOutlet var contentView: UIView!
    

override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}


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

private func commonInit() {
if let view = Bundle.main.loadNibNamed("View1", owner: self, options: nil)?.first as? UIView {
addSubview(view)
view.frame = self.bounds
}
}
}


笔记 如果我们把 Custom ClassFile's owner移动到 Container View,我们会得到错误(循环) ,这是因为:

Container View初始化系统实例,在 commonInit()中再次初始化

.loadNibNamed

Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ff7bf6fbfc8)