如何将 xib 文件加载为自定义 class?
How to load a xib file as a custom class?
我正在尝试使用 xib 文件创建可重用组件(在同一视图中多次),一切都很好,直到我想从 @IBInspectable
[=52= 更新一些控件].我发现 @IBOutlet
在那一刻没有设置,所以我搜索并找到了一些东西
http://justabeech.com/2014/07/27/xcode-6-live-rendering-from-nib/
他正在将加载的视图保存到 proxyView 中,以便您可以在 @IBInspectable
中使用它。不幸的是,该代码有点旧并且不能按原样工作。但我的问题是,当我尝试将笔尖加载为 class 时,它不起作用。它只有在我将它加载为 UIView
.
时才有效
此行失败,因为
return bundle.loadNibNamed("test", owner: nil, options: nil)?[0] as? ValidationTextField
只有这样才有效
return bundle.loadNibNamed("test", owner: nil, options: nil)?[0] as? UIView
我认为问题在于,xib 文件的所有者被标记为 ValidationTextField
,但主视图是 UIView
。因此,当您加载笔尖时,它会带来 UIView
,显然没有自定义属性或插座。
许多关于加载 xib 文件的例子都说 class 必须在文件所有者中。所以我不知道如何使用它来获得自定义 class。
import UIKit
@IBDesignable class ValidationTextField: UIView {
@IBOutlet var lblError: UILabel!
@IBOutlet var txtField: XTextField!
@IBOutlet var imgWarning: UIImageView!
private var proxyView: ValidationTextField?
override init(frame: CGRect) {
super.init(frame: frame)
}
required init(coder: NSCoder) {
super.init(coder: coder)!
}
override func awakeFromNib() {
super.awakeFromNib()
xibSetup()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
xibSetup()
self.proxyView?.prepareForInterfaceBuilder()
}
func xibSetup() {
guard let view = loadNib() else { return }
view.frame = bounds
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(view)
// Saving the view in a variable
self.proxyView = view
}
func loadNib() -> ValidationTextField? {
let bundle = Bundle(for: type(of: self))
return bundle.loadNibNamed("test", owner: nil, options: nil)?[0] as? ValidationTextField
}
@IBInspectable var fontSize: CGFloat = 14.0 {
didSet {
let font = UIFont(name: "System", size: self.fontSize)
self.proxyView!.txtField.font = font
self.proxyView!.lblError.font = font
}
}
}
我什至不知道剩下的是否可行。如果 link 说的是真的,加载笔尖后获取视图将允许我访问插座。
运行 该行代码失败
guard let view = loadNib() else { return }
我猜它无法将 UIView
转换为 class 所以它 returns nil,然后退出。
我的目标是拥有一个可以在单个控制器中多次放置的可重用组件。并且能够在故事板中看到它的设计。
将 xibSetup()
移至您的初始值设定项。 awakeFromNib
被调用得太晚,如果以编程方式创建视图,则不会调用它。不需要在prepareForInterfaceBuilder
.
中调用
简而言之,这可以归纳为:
open class NibLoadableView: UIView {
public override init(frame: CGRect) {
super.init(frame: frame)
loadNibContentView()
commonInit()
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
loadNibContentView()
commonInit()
}
public func commonInit() {
// to be overriden
}
}
public extension UIView {
// @objc makes it possible to override the property
@objc
var nibBundle: Bundle {
return Bundle(for: type(of: self))
}
// @objc makes it possible to override the property
@objc
var nibName: String {
return String(describing: type(of: self))
}
@discardableResult
func loadNibContentView() -> UIView? {
guard
// note that owner = self !!!
let views = nibBundle.loadNibNamed(nibName, owner: self, options: nil),
let contentView = views.first as? UIView
else {
return nil
}
addSubview(contentView)
contentView.translatesAutoresizingMaskIntoConstraints = true
contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
contentView.frame = self.bounds
return contentView
}
}
注意加载nib的视图必须是视图的所有者。
那么你的class会变成:
@IBDesignable
class ValidationTextField: NibLoadableView {
@IBOutlet var lblError: UILabel!
@IBOutlet var txtField: XTextField!
@IBOutlet var imgWarning: UIImageView!
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
commonInit()
}
override func commonInit() {
super.commonInit()
updateFont()
}
@IBInspectable var fontSize: CGFloat = 14.0 {
didSet {
updateFont()
}
}
private func updateFont() {
let font = UIFont.systemFont(ofSize: fontSize)
txtField.font = font
lblError.font = font
}
}
我想关于代理对象的整个想法来自对 Nib 所有者的误用。使用代理对象,层次结构必须是这样的:
ValidationTextField
-> ValidationTextField (root view of the nib)
-> txtField, lblError, imgWarning
这没有多大意义。我们真正想要的是:
ValidationTextField (nib owner)
-> UIView (root view of the nib)
-> txtField, lblError, imgWarning
我正在尝试使用 xib 文件创建可重用组件(在同一视图中多次),一切都很好,直到我想从 @IBInspectable
[=52= 更新一些控件].我发现 @IBOutlet
在那一刻没有设置,所以我搜索并找到了一些东西
http://justabeech.com/2014/07/27/xcode-6-live-rendering-from-nib/
他正在将加载的视图保存到 proxyView 中,以便您可以在 @IBInspectable
中使用它。不幸的是,该代码有点旧并且不能按原样工作。但我的问题是,当我尝试将笔尖加载为 class 时,它不起作用。它只有在我将它加载为 UIView
.
此行失败,因为
return bundle.loadNibNamed("test", owner: nil, options: nil)?[0] as? ValidationTextField
只有这样才有效
return bundle.loadNibNamed("test", owner: nil, options: nil)?[0] as? UIView
我认为问题在于,xib 文件的所有者被标记为 ValidationTextField
,但主视图是 UIView
。因此,当您加载笔尖时,它会带来 UIView
,显然没有自定义属性或插座。
许多关于加载 xib 文件的例子都说 class 必须在文件所有者中。所以我不知道如何使用它来获得自定义 class。
import UIKit
@IBDesignable class ValidationTextField: UIView {
@IBOutlet var lblError: UILabel!
@IBOutlet var txtField: XTextField!
@IBOutlet var imgWarning: UIImageView!
private var proxyView: ValidationTextField?
override init(frame: CGRect) {
super.init(frame: frame)
}
required init(coder: NSCoder) {
super.init(coder: coder)!
}
override func awakeFromNib() {
super.awakeFromNib()
xibSetup()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
xibSetup()
self.proxyView?.prepareForInterfaceBuilder()
}
func xibSetup() {
guard let view = loadNib() else { return }
view.frame = bounds
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(view)
// Saving the view in a variable
self.proxyView = view
}
func loadNib() -> ValidationTextField? {
let bundle = Bundle(for: type(of: self))
return bundle.loadNibNamed("test", owner: nil, options: nil)?[0] as? ValidationTextField
}
@IBInspectable var fontSize: CGFloat = 14.0 {
didSet {
let font = UIFont(name: "System", size: self.fontSize)
self.proxyView!.txtField.font = font
self.proxyView!.lblError.font = font
}
}
}
我什至不知道剩下的是否可行。如果 link 说的是真的,加载笔尖后获取视图将允许我访问插座。
运行 该行代码失败
guard let view = loadNib() else { return }
我猜它无法将 UIView
转换为 class 所以它 returns nil,然后退出。
我的目标是拥有一个可以在单个控制器中多次放置的可重用组件。并且能够在故事板中看到它的设计。
将 xibSetup()
移至您的初始值设定项。 awakeFromNib
被调用得太晚,如果以编程方式创建视图,则不会调用它。不需要在prepareForInterfaceBuilder
.
简而言之,这可以归纳为:
open class NibLoadableView: UIView {
public override init(frame: CGRect) {
super.init(frame: frame)
loadNibContentView()
commonInit()
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
loadNibContentView()
commonInit()
}
public func commonInit() {
// to be overriden
}
}
public extension UIView {
// @objc makes it possible to override the property
@objc
var nibBundle: Bundle {
return Bundle(for: type(of: self))
}
// @objc makes it possible to override the property
@objc
var nibName: String {
return String(describing: type(of: self))
}
@discardableResult
func loadNibContentView() -> UIView? {
guard
// note that owner = self !!!
let views = nibBundle.loadNibNamed(nibName, owner: self, options: nil),
let contentView = views.first as? UIView
else {
return nil
}
addSubview(contentView)
contentView.translatesAutoresizingMaskIntoConstraints = true
contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
contentView.frame = self.bounds
return contentView
}
}
注意加载nib的视图必须是视图的所有者。
那么你的class会变成:
@IBDesignable
class ValidationTextField: NibLoadableView {
@IBOutlet var lblError: UILabel!
@IBOutlet var txtField: XTextField!
@IBOutlet var imgWarning: UIImageView!
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
commonInit()
}
override func commonInit() {
super.commonInit()
updateFont()
}
@IBInspectable var fontSize: CGFloat = 14.0 {
didSet {
updateFont()
}
}
private func updateFont() {
let font = UIFont.systemFont(ofSize: fontSize)
txtField.font = font
lblError.font = font
}
}
我想关于代理对象的整个想法来自对 Nib 所有者的误用。使用代理对象,层次结构必须是这样的:
ValidationTextField
-> ValidationTextField (root view of the nib)
-> txtField, lblError, imgWarning
这没有多大意义。我们真正想要的是:
ValidationTextField (nib owner)
-> UIView (root view of the nib)
-> txtField, lblError, imgWarning