我有一个名为MyClass
的类,它是UIView
的子类,我想用一个XIB
文件来初始化它。我不确定如何使用名为View.xib
的xib文件来初始化这个类。
class MyClass: UIView {
// what should I do here?
//init(coder aDecoder: NSCoder) {} ??
}
我有一个名为MyClass
的类,它是UIView
的子类,我想用一个XIB
文件来初始化它。我不确定如何使用名为View.xib
的xib文件来初始化这个类。
class MyClass: UIView {
// what should I do here?
//init(coder aDecoder: NSCoder) {} ??
}
我测试了这段代码,它运行得非常好:
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)
或者
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
}
var view = MyClass.instanceFromNib()
和 self.view.addSubview(view)
,而不是 var view = MyClass.instanceFromNib
和 self.view.addSubview(view())
。这只是一个小建议,以改进答案 :) - Logan尽管Sam的解决方案没有考虑不同的bundle(使用NSBundle:forClass可以帮助解决),并且需要手动加载,也就是输入代码,但它已经很出色了。
如果你想要完全支持你的Xib Outlets、不同的Bundle(在框架中使用!)并在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
}
}
按照惯例使用你的xib,即将Outlets连接到文件所有者并将文件所有者类设置为你自己的类。
用法:只需从NibLoadingView子类化自己的视图类,并在Xib文件中将类名设置为File's Owner
不再需要额外的代码。
功劳归于功臣:从DenHeadless的GH上修改了此分支。 我的Gist:https://gist.github.com/winkelsdorf/16c481f274134718946328b6e2c9a4d8
NibLoadingView
嵌入到XIB中并从init?(coder:)
中调用nibSetup
,会导致无限递归。 - redent84.clearColor()
。但如果在实例化后通过代码执行,它应该可以工作。无论如何,正如所说的,更优雅的方法是基于协议的方法。所以,这里有一个链接供您参考:https://github.com/AliSoftware/Reusable。我现在正在使用类似的方法处理 UITableViewCells(在我发现那个非常有用的项目之前实现的)。希望对你有帮助! - Frederik Winkelsdorf从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
}
}
var myView = nib.instantiate... as! myViewType
,每次都可以正常工作。 - Paul Linsaylet nibName = String(describing: Self.self)
(Swift 3)来获取类的名称。 - OliverD这是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
}
}
Swift 4
在我的情况下,我需要将数据传递到自定义视图中,因此我创建了一个静态函数来实例化该视图。
Create UIView extension
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. Connect outlet if necessary as usual.
Instantiate view
let view = MyCustomView.instantiate(message: "Hello World.")
从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 的解答: 在我的情况下,我想在自定义类中拥有一个可以修改的 outlet:
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。不要担心File's Owner。
另外,请确保将MyClassView中的outlet连接到标签:
实例化:
let myClassView = MyClassView.createMyClassView()
myClassView.myLabel.text = "Hello World!"
Swift 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文件创建视图
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文件可以包含多个视图,所以您在这里使用数组(.first
)。
例如
loadNibNamed()
)来创建类("View1"
)的实例File's Owner
在View1.xib
中设置为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 Class
从File's owner
移动到Container View
,则会出现错误(循环)。这是因为:
系统从Container View
初始化实例,在commonInit()
中再次初始化它。
.loadNibNamed
Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ff7bf6fbfc8)
let customView = UINib(nibName:"CustomView",bundle:.main).instantiate(withOwner: nil, options: nil).first as! UIView
customView.frame = self.view.bounds
self.view.addSubview(customView)