[英]How to initialize/instantiate a custom UIView class with a XIB file in Swift
I have a class called MyClass
which is a subclass of UIView
, that I want to initialize with a XIB
file.我有一个名为
MyClass
的UIView
,它是 UIView 的子类,我想用XIB
文件对其进行初始化。 I am not sure how to initialize this class with the xib file called View.xib
我不确定如何使用名为
View.xib
class MyClass: UIView {
// what should I do here?
//init(coder aDecoder: NSCoder) {} ??
}
I tested this code and it works great:我测试了这段代码,效果很好:
class MyClass: UIView {
class func instanceFromNib() -> UIView {
return UINib(nibName: "nib file name", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as UIView
}
}
Initialise the view and use it like below:初始化视图并使用它,如下所示:
var view = MyClass.instanceFromNib()
self.view.addSubview(view)
OR或者
var view = MyClass.instanceFromNib
self.view.addSubview(view())
UPDATE Swift >=3.x & Swift >=4.x更新 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
}
Sam's solution is already great, despite it doesn't take different bundles into account (NSBundle:forClass comes to the rescue) and requires manual loading, aka typing code. Sam 的解决方案已经很棒,尽管它没有考虑不同的包(NSBundle:forClass 来救援)并且需要手动加载,也就是输入代码。
If you want full support for your Xib Outlets, different Bundles (use in frameworks!) and get a nice preview in Storyboard try this:如果你想完全支持你的 Xib Outlets、不同的 Bundles(在框架中使用!)并在 Storyboard 中获得一个很好的预览,试试这个:
// 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
}
}
Use your xib as usual, ie connect Outlets to File Owner and set File Owner class to your own class.像往常一样使用您的 xib,即将 Outlets 连接到 File Owner 并将 File Owner 类设置为您自己的类。
Usage: Just subclass your own View class from NibLoadingView & Set the class name to File's Owner in the Xib file用法:只需从 NibLoadingView 子类化您自己的 View 类并在 Xib文件中将类名设置为File's Owner
No additional code required anymore.不再需要额外的代码。
Credits where credit's due: Forked this with minor changes from DenHeadless on GH.信用到期的信用:从 GH 上的 DenHeadless 进行了微小的更改,将其分叉。 My Gist: https://gist.github.com/winkelsdorf/16c481f274134718946328b6e2c9a4d8
我的要点: https : //gist.github.com/winkelsdorf/16c481f274134718946328b6e2c9a4d8
As of Swift 2.0, you can add a protocol extension.从 Swift 2.0 开始,您可以添加协议扩展。 In my opinion, this is a better approach because the return type is
Self
rather than UIView
, so the caller doesn't need to cast to the view class.在我看来,这是一个更好的方法,因为返回类型是
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
}
}
And this is the answer of Frederik on Swift 3.0这是 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
}
}
Universal way of loading view from xib:从xib加载视图的通用方式:
Example:例子:
let myView = Bundle.loadView(fromNib: "MyView", withType: MyView.self)
Implementation:执行:
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 Answer: In my case, I wanted to have an outlet in my custom class that I could modify: Swift 3 答案:就我而言,我想在我的自定义类中有一个可以修改的插座:
class MyClassView: UIView {
@IBOutlet weak var myLabel: UILabel!
class func createMyClassView() -> MyClass {
let myClassNib = UINib(nibName: "MyClass", bundle: nil)
return myClassNib.instantiate(withOwner: nil, options: nil)[0] as! MyClassView
}
}
When in the .xib, make sure that the Custom Class field is MyClassView.在 .xib 中时,确保自定义类字段是 MyClassView。 Don't bother with the File's Owner.
不要打扰文件的所有者。
Also, make sure that you connect the outlet in MyClassView to the label:另外,请确保将 MyClassView 中的插座连接到标签:
To instantiate it:要实例化它:
let myClassView = MyClassView.createMyClassView()
myClassView.myLabel.text = "Hello World!"
Swift 4斯威夫特 4
Here in my case I have to pass data into that custom view, so I create static function to instantiate the view.在我的情况下,我必须将数据传递到该自定义视图中,因此我创建了静态函数来实例化该视图。
Create UIView extension创建 UIView 扩展
extension UIView { class func initFromNib<T: UIView>() -> T { return Bundle.main.loadNibNamed(String(describing: self), owner: nil, options: nil)?[0] as! T } }
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 } }
Set custom class to MyCustomView in .xib file.在 .xib 文件中将自定义类设置为 MyCustomView。 Connect outlet if necessary as usual.
如有必要,照常连接插座。
Instantiate view实例化视图
let view = MyCustomView.instantiate(message: "Hello World.")
Below code will do the job if anyone wants to load a custom View with XIB Programmatically.如果有人想以编程方式使用 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)
Swift 5.2斯威夫特 5.2
Create a class named NibLoadingView
with the following contents:创建一个名为
NibLoadingView
的类,内容如下:
import Foundation
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()
}
override func awakeFromNib() {
super.awakeFromNib()
nibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
nibSetup()
}
private func nibSetup() {
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: type(of: self).description().replacingOccurrences(of: "REPLACEME.", with: ""), bundle: bundle)
let nibView = nib.instantiate(withOwner: self, options: nil).first as! UIView
return nibView
}
}
Find REPLACEME
in the above code and replace it with your project's name.在上面的代码中找到
REPLACEME
并将其替换为您的项目名称。
Now create a XIB & UIView class pair, set XIB's owner to UIView class and subclass NibLoadingView
.现在创建一个 XIB 和 UIView 类对,将 XIB 的所有者设置为 UIView 类和子类
NibLoadingView
。
You can now init the class just like ExampleView()
, ExampleView(frame: CGRect)
, etc.您现在可以像
ExampleView()
、 ExampleView(frame: CGRect)
等一样初始化该类。
Based on Frederik's answer基于弗雷德里克的回答
create view from.xib从.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
}
Since.xib can contains several view, that is why you are working with array here( .first
)由于.xib 可以包含多个视图,这就是您在此处使用数组的原因(
.first
)
For example例如
File's Owner
in View1.xib
as View1
.View1.xib
中File's Owner
设置为View1
。 Allows to connect outlets and actionsimport 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
}
}
}
Notes if we move Custom Class
from File's owner
to Container View we get error(loop).注意如果我们将
Custom Class
从File's owner
移动到容器视图,我们会得到错误(循环)。 It is because of:这是因为:
System init instance from Container View
where we init it again in commonInit()来自
Container View
的系统初始化实例,我们在 commonInit() 中再次对其进行初始化
.loadNibNamed
Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ff7bf6fbfc8)
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)
})
})
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.