如何使用Xib创建自定义UIView
新建一个空白View xib文件
关联xib文件和code 文件
有两种方式去关联xib文件和代码文件,第一种就是设置file's owner 的 custom class,另外一种就是在view那里去设置custom class
File's owner和View's custom class的区别
File's owner custom class | View's custom class |
---|---|
NSObject | UIView |
根据上图也可以看到,File's owner 的custom class对应是NSObject类型,而View‘s custom class是UIView类型。
使用View's custom class创建关联
XibView.swift 里面的代码
import UIKit
@IBDesignable
class XibView: UIView {
let nibName = "XibView"
override init(frame: CGRect) {
super.init(frame: frame)
print(#function)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
print(#function)
}
func commonInit() {
guard let view = loadViewFromNib() else { return }
view.frame = self.bounds
addSubview(view)
}
func loadViewFromNib() -> UIView? {
print(#function)
guard let view = Bundle.main.loadNibNamed("XibView", owner: nil, options: nil)?.first as? UIView else { return nil }
return view
}
}
在ViewController中的使用
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let xibView = XibView(frame: view.bounds)
view.addSubview(xibView)
}
}
运行✅
可以看到先调用 init
去初始化,init
方法里面去调用loadNib
方法,而loadNib
会触发required init?(coder aDecoder: NSCoder)
这个时候得说下required init?(coder aDecoder: NSCoder)
的调用,一般代码去初始化一个view的时候,会走init(frame: CGRect)
,而不会走required init?(coder aDecoder: NSCoder)
. 需要去load Nib的时候才会调用required init?(coder aDecoder: NSCoder)
,也就是说用StoryBoard拉控件的形式只会调用required init?(coder aDecoder: NSCoder)
在StoryBoard中使用
1⃣️ XibView.swift 里面需要修改部分代码,即是在required init?(coder aDecoder: NSCoder)
里面调用私有方法commonInit()去loadNib
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
print(#function)
commonInit()
}
2⃣️ 在StoryBoard拉一个UIView,将class设置成自定义view的类就可以引用了
Run -> ….. 运行❌
❌ 所以!如果使用View's custom class去关联xib和代码文件,并且要在storyboard使用这个自定义的xib view的时候,我们必须在required init?(coder aDecoder: NSCoder)
里面去调用commonInit()
去load xib里面添加的控件(用storyboard拉控件的形式,不会走init(frame: CGRect)
),我们将会陷入初始化的死循环。
使用File's owner
Apple官方文档给出关于File's owner的说明:
About the File’s Owner
One of the most important objects in a nib file is the File’s Owner object. Unlike interface objects, the File’s Owner object is a placeholder object that is not created when the nib file is loaded. Instead, you create this object in your code and pass it to the nib-loading code. The reason this object is so important is that it is the main link between your application code and the contents of the nib file. More specifically, it is the controller object that is responsible for the contents of the nib file.
In Xcode, you can create connections between the File’s Owner and the other interface objects in your nib file. When you load the nib file, the nib-loading code recreates these connections using the replacement object you specify. This allows your object to reference objects in the nib file and receive messages from the interface objects automatically.
文件中最重要的对象之一是File的Owner对象。与接口对象不同,File的Owner对象是一个占位符对象,在加载nib文件时不会创建占位符对象。相反,您在代码中创建此对象并将其传递给nib加载代码。这个对象如此重要的原因是它是应用程序代码和nib文件内容之间的主要链接。更具体地说,它是控制器对象,负责nib文件的内容。
在Xcode中,您可以在文件所有者和nib文件中的其他接口对象之间创建连接。加载nib文件时,nib加载代码使用您指定的替换对象重新创建这些连接。这允许您的对象引用nib文件中的对象并自动从接口对象接收消息.
Bundle.main.loadNibNamed("XibView", owner: self, options: nil)?.first as? UIView
可以看到此时我们把初始化好的owner对象作为参数传递进去了。
在Demo中的使用
XibView.swift 文件只需要修改loadNib部分代码
func loadViewFromNib() -> UIView? {
print(#function)
guard let view = Bundle.main.loadNibNamed("XibView", owner: self, options: nil)?.first as? UIView else { return nil }
return view
}
StoryBoard中还是拉一个UIView控件,className改为自定义类型。
运行✅
可以在StoryBoard里面使用Xib自定义的View了 🎉🎉🎉
总结
File’s Owner 和 View custom class都是用于xib跟代码文件建立关联的。
在StoryBoard里面使用自定义xib时候,只会走required init?(coder aDecoder: NSCoder)
方法,所以需要在里面调用loadNib加载添加在xib上的控件。
使用File’s Owner,用代码去loadNib的时候,不会走required init?(coder aDecoder: NSCoder)
方法。但是使用View custom class时候会,这也就是为什么使用View custom class时候造成死循环的原因。
所以,如果需要在StoryBoard里面使用Xib自定义的View,需要设置File’s Owner,而不是View custom class.