在 Swift 中从 nib 加载 UIView

下面是我的 Objective-C 代码,我用它来加载定制 UIView的笔尖:

-(id)init{


NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"myXib" owner:self options:nil];
return [subviewArray objectAtIndex:0];


}

Swift 中的等效代码是什么?

245120 次浏览
let subviewArray = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)
return subviewArray[0]

试试下面的代码。

var uiview :UIView?


self.uiview = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)[0] as? UIView

编辑:

import UIKit


class TestObject: NSObject {


var uiview:UIView?


init()  {
super.init()
self.uiview = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)[0] as? UIView
}




}

原始解决方案

  1. 我创建了一个 XIB 和一个名为 SomView 的类(对于 方便性和可读性)。
  2. 在 XIB 中,我将“ File’s Owner”类更改为 SomView (在标识检查器中)。
  3. 我在 SomView.swift 中创建了一个 UIView 出口,将其链接到 XIB 文件中的顶级视图(为方便起名为“ view”)。然后根据需要将其他出口添加到 XIB 文件中的其他控件中。
  4. 在 SomView.swift 中,我在“ init with code”初始化器中加载了 XIB。没有必要为“自我”分配任何东西。一旦加载了 XIB,所有的出口都会连接起来,包括顶级视图。唯一缺少的是将顶部视图添加到视图层次结构中:

.

class SomeView: UIView {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
NSBundle.mainBundle().loadNibNamed("SomeView", owner: self, options: nil)
self.addSubview(self.view);    // adding the top level view to the view hierarchy
}
...
}

注意,通过这种方式,我得到了一个从 nib 加载自身的类。然后,只要 UiView 可以在项目中使用(无论是 Interface Builder 的还是编程的) ,我就可以使用 somview 作为一个类。

更新-使用 Swift 3语法

在下面的扩展中加载 xib 是作为一个实例方法编写的,然后可以由像上面这样的初始化程序使用:

extension UIView {


@discardableResult   // 1
func fromNib<T : UIView>() -> T? {   // 2
guard let contentView = Bundle(for: type(of: self)).loadNibNamed(String(describing: type(of: self)), owner: self, options: nil)?.first as? T else {    // 3
// xib not loaded, or its top view is of the wrong type
return nil
}
self.addSubview(contentView)     // 4
contentView.translatesAutoresizingMaskIntoConstraints = false   // 5
contentView.layoutAttachAll(to: self)   // 6
return contentView   // 7
}
}
  1. 使用可丢弃的返回值,因为当所有出口都已连接时,调用方基本上不会感兴趣。
  2. 这是一个泛型方法,返回 UIView 类型的可选对象。如果加载视图失败,则返回空。
  3. 尝试加载与当前类实例同名的 XIB 文件。如果失败,则返回 nil。
  4. 将顶级视图添加到视图层次结构中。
  5. 这一行假设我们使用约束来布局视图。
  6. 这种方法添加了顶部,底部,前导和尾随约束-附加视图的“自我”在所有方面(见: https://stackoverflow.com/a/46279424/2274829的详细信息)
  7. 返回顶层视图

调用方法可能是这样的:

final class SomeView: UIView {   // 1.
required init?(coder aDecoder: NSCoder) {   // 2 - storyboard initializer
super.init(coder: aDecoder)
fromNib()   // 5.
}
init() {   // 3 - programmatic initializer
super.init(frame: CGRect.zero)  // 4.
fromNib()  // 6.
}
// other methods ...
}
  1. SomClass 是一个 UIView 子类,它从一个 somClass.xib 文件加载其内容。
  2. 在故事板中使用视图时的初始化器(请记住使用 SomClass 作为故事板视图的自定义类)。
  3. 用于以编程方式创建视图的初始化程序(例如: “ let myView = Some View ()”)。
  4. 使用全零框架,因为这个视图是使用自动布局布局的。 注意,“ init (frame: CGRect){ . . }”方法不是独立创建的,因为自动布局只在我们的项目中使用。
  5. 使用扩展名加载 xib 文件。

Credit: 在这个解决方案中使用通用扩展是受到 Robert 下面的回答的启发。

剪辑 将“ view”更改为“ contentView”以避免混淆。还将数组下标更改为“ . first”。

现在能够迅速返回 -> Self有助于简化这一点。

extension UIView {
class func fromNib(named: String? = nil) -> Self {
let name = named ?? "\(Self.self)"
guard
let nib = Bundle.main.loadNibNamed(name, owner: nil, options: nil)
else { fatalError("missing expected nib named: \(name)") }
guard
/// we're using `first` here because compact map chokes compiler on
/// optimized release, so you can't use two views in one nib if you wanted to
/// and are now looking at this
let view = nib.first as? Self
else { fatalError("view of type \(Self.self) not found in \(nib)") }
return view
}
}

如果您的 .xib文件和子类共享相同的名称,您可以使用:

let view = CustomView.fromNib()

如果您有自定义名称,请使用:

let view = CustomView.fromNib(named: "special-case")

注意:

如果您得到错误“ YourType 类型的视图未在。."那么您还没有在 .xib文件中设置视图的类

.xib文件中选择您的视图,然后按 cmd + opt + 4,在 class输入中输入您的类

我更喜欢这个解决方案(基于这个答案,如果@GK100) :

  1. 我创建了一个 XIB 和一个名为 SomView 的类(为方便起见和易读性使用了相同的名称)。我都是基于 UIView。
  2. 在 XIB 中,我将“ File’s Owner”类更改为 SomView (在标识检查器中)。
  3. 我在 SomView.swift 中创建了一个 UIView 出口,将其链接到 XIB 文件中的顶级视图(为方便起名为“ view”)。然后根据需要将其他出口添加到 XIB 文件中的其他控件中。
  4. 在 somView.swift 中,我将 XIB 加载到 initinit:frame: CGRect初始值设定项中。没有必要为“自我”分配任何东西。一旦加载了 XIB,所有的出口都会连接起来,包括顶级视图。唯一缺少的是将顶部视图添加到视图层次结构中:

    class SomeView: UIView {
    override init(frame: CGRect) {
    super.init(frame: frame)
    NSBundle.mainBundle().loadNibNamed("SomeObject", owner: self, options: nil)
    self.addSubview(self.view);    // adding the top level view to the view hierarchy
    }
    
    
    required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    NSBundle.mainBundle().loadNibNamed("SomeObject", owner: self, options: nil)
    self.addSubview(self.view);    // adding the top level view to the view hierarchy
    }
    
    
    
    
    ...
    }
    

我用 Swift 通过以下代码实现了这一点:

class Dialog: UIView {
@IBOutlet var view:UIView!


override init(frame: CGRect) {
super.init(frame: frame)
self.frame = UIScreen.mainScreen().bounds
NSBundle.mainBundle().loadNibNamed("Dialog", owner: self, options: nil)
self.view.frame = UIScreen.mainScreen().bounds
self.addSubview(self.view)
}


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

不要忘记连接您的 XIB 风景插座到 风景插座快速定义。您还可以将 First Responder 设置为自定义类名,以开始连接任何其他插座。

希望这个能帮上忙!

对 Swift 进行此操作的一个好方法是使用枚举。

enum Views: String {
case view1 = "View1" // Change View1 to be the name of your nib
case view2 = "View2" // Change View2 to be the name of another nib


func getView() -> UIView? {
return NSBundle.mainBundle().loadNibNamed(self.rawValue, owner: nil, options: nil).first as? UIView
}
}

然后在代码中,您可以简单地使用:

let view = Views.view1.getView()

在 Xcode 7 beta 4、 Swift 2.0和 iOS9 SDK 中测试。 下面的代码将把 xib 分配给 uiview。 您可以在故事板中使用这个自定义 xib 视图并访问 IBOutlet 对象。

import UIKit


@IBDesignable class SimpleCustomView:UIView
{
var view:UIView!;


@IBOutlet weak var lblTitle: UILabel!


@IBInspectable var lblTitleText : String?
{
get{
return lblTitle.text;
}
set(lblTitleText)
{
lblTitle.text = lblTitleText!;
}
}


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


required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
loadViewFromNib ()
}
func loadViewFromNib() {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "SimpleCustomView", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
view.frame = bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
self.addSubview(view);






}




}

以编程方式访问自定义视图

self.customView =  SimpleCustomView(frame: CGRectMake(100, 100, 200, 200))
self.view.addSubview(self.customView!);

源代码 -https://github.com/karthikprabhuA/CustomXIBSwift

我的贡献是:

extension UIView {
class func fromNib<T: UIView>() -> T {
return Bundle(for: T.self).loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
}
}

那就这么说吧:

let myCustomView: CustomView = UIView.fromNib()

甚至:

let myCustomView: CustomView = .fromNib()

如果您的项目中有很多自定义视图,您可以创建类似于 UIViewFromNib的类

Swift 2.3

class UIViewFromNib: UIView {
    

var contentView: UIView!
    

var nibName: String {
return String(self.dynamicType)
}
    

//MARK:
override init(frame: CGRect) {
super.init(frame: frame)
        

loadViewFromNib()
}
    

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

loadViewFromNib()
}
    

//MARK:
private func loadViewFromNib() {
contentView = NSBundle.mainBundle().loadNibNamed(nibName, owner: self, options: nil)[0] as! UIView
contentView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
contentView.frame = bounds
addSubview(contentView)
}
}

Swift 5

class UIViewFromNib: UIView {
    

var contentView: UIView!
    

var nibName: String {
return String(describing: type(of: self))
}
    

//MARK:
override init(frame: CGRect) {
super.init(frame: frame)
        

loadViewFromNib()
}
    

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

loadViewFromNib()
}
    

//MARK:
func loadViewFromNib() {
let bundle = Bundle(for: UIViewFromNib.self)
contentView = UINib(nibName: nibName, bundle: bundle).instantiate(withOwner: self).first as? UIView
contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
contentView.frame = bounds
addSubview(contentView)
}
}

并且在每个类中,如果 .xib文件有不同的名称,也可以覆盖 nibName属性:

class MyCustomClass: UIViewFromNib {
    

}

您所要做的就是在 UIView类中调用 init 方法。

这样做:

class className: UIView {


@IBOutlet var view: UIView!


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


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


func setup() {
UINib(nibName: "nib", bundle: nil).instantiateWithOwner(self, options: nil)
addSubview(view)
view.frame = self.bounds
}
}

现在,如果你想把这个视图作为子视图添加到视图控制器中,那么你可以在 view controller. ift 文件中这样做:

self.view.addSubview(className())

类似于上面的一些答案,但是更加一致的 Swift3 UIView 扩展:

extension UIView {
class func fromNib<A: UIView> (nibName name: String, bundle: Bundle? = nil) -> A? {
let bundle = bundle ?? Bundle.main
let nibViews = bundle.loadNibNamed(name, owner: self, options: nil)
return nibViews?.first as? A
}


class func fromNib<T: UIView>() -> T? {
return fromNib(nibName: String(describing: T.self), bundle: nil)
}
}

它提供了从自己命名的 nib 和从其他 nib/bundle 加载类的便利。

以上述解决方案为基础。

这将跨所有项目包工作,并且在从 Nib ()调用时不需要泛型。

Swift 2

extension UIView {


public class func fromNib() -> Self {
return fromNib(nil)
}


public class func fromNib(nibName: String?) -> Self {


func fromNibHelper<T where T : UIView>(nibName: String?) -> T {
let bundle = NSBundle(forClass: T.self)
let name = nibName ?? String(T.self)
return bundle.loadNibNamed(name, owner: nil, options: nil)?.first as? T ?? T()
}
return fromNibHelper(nibName)
}
}

Swift 3

extension UIView {


public class func fromNib() -> Self {
return fromNib(nibName: nil)
}


public class func fromNib(nibName: String?) -> Self {
func fromNibHelper<T>(nibName: String?) -> T where T : UIView {
let bundle = Bundle(for: T.self)
let name = nibName ?? String(describing: T.self)
return bundle.loadNibNamed(name, owner: nil, options: nil)?.first as? T ?? T()
}
return fromNibHelper(nibName: nibName)
}
}

可以这样使用:

let someView = SomeView.fromNib()

或者像这样:

let someView = SomeView.fromNib("SomeOtherNibFileName")

洛根回答的迅捷版本

extension UIView {
public class func fromNib(nibName: String? = nil) -> Self {
return fromNib(nibName: nibName, type: self)
}


public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T {
return fromNib(nibName: nibName, type: T.self)!
}


public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T? {
var view: T?
let name: String


if let nibName = nibName {
name = nibName
} else {
name = self.nibName
}


if let nibViews = Bundle.main.loadNibNamed(name, owner: nil, options: nil) {
for nibView in nibViews {
if let tog = nibView as? T {
view = tog
}
}
}


return view
}


public class var nibName: String {
return "\(self)".components(separatedBy: ".").first ?? ""
}


public class var nib: UINib? {
if let _ = Bundle.main.path(forResource: nibName, ofType: "nib") {
return UINib(nibName: nibName, bundle: nil)
} else {
return nil
}
}
}

我只是这样做:

if let myView = UINib.init(nibName: "MyView", bundle: nil).instantiate(withOwner: self)[0] as? MyView {
// Do something with myView
}

此示例使用主包中的 nib“ MyView.xib”中的第一个视图。但是您可以更改索引、 nib 名称或 bundle (默认情况下为 main)。

我过去常常把视图唤醒到 view init 方法中,或者像上面提到的那样创建泛型方法(顺便说一句,它们很聪明) ,但是我现在不这么做了,因为我注意到用例通常是不同的,而且为了覆盖所有的情况,泛型方法变得和使用 UINib.instance 方法一样复杂。

我更喜欢使用工厂对象,通常是使用视图的 ViewController,或者如果视图需要在多个位置使用,则使用专用的工厂对象或视图扩展。

在本例中,ViewController 从 nib 加载视图。 可以更改 nib 文件,以便对同一视图类使用不同的布局。(这不是一个好的代码,它只是说明了这个想法)

class MyViewController {
// Use "MyView-Compact" for compact version
var myViewNibFileName = "MyView-Standard"


lazy var myView: MyView = {
// Be sure the Nib is correct, or it will crash
// We don't want to continue with a wrong view anyway, so ! is ok
UINib.init(nibName: myViewNibFileName, bundle: nil).instantiate(withOwner: self)[0] as! MyView
}()
}

根据洛根的回答做出了更有力的版本

extension UIView {
public class func fromNib(nibName: String? = nil) -> Self {
return fromNib(nibName: nibName, type: self)
}


public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T {
return fromNib(nibName: nibName, type: T.self)!
}


public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T? {
var view: T?
let name: String


if let nibName = nibName {
name = nibName
} else {
name = self.nibName
}


if let nibViews = nibBundle.loadNibNamed(name, owner: nil, options: nil) {
if nibViews.indices.contains(nibIndex), let tog = nibViews[nibIndex] as? T {
view = tog
}
}


return view
}


public class var nibName: String {
return "\(self)".components(separatedBy: ".").first ?? ""
}


public class var nibIndex: Int {
return 0
}


public class var nibBundle: Bundle {
return Bundle.main
}
}

你可以用

class BaseView: UIView {
override class var nibName: String { return "BaseView" }
weak var delegate: StandardStateViewDelegate?
}


class ChildView: BaseView {
override class var nibIndex: Int { return 1 }
}

如果您希望 Swift UIView 子类完全自包含,并且能够使用 init 或 init (frame:)进行实例化,而不暴露使用 Nib 的实现细节,那么您可以使用协议扩展来实现这一点。该解决方案避免了许多其他解决方案所建议的嵌套 UIView 层次结构。

public class CustomView: UIView {


@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var valueLabel: UILabel!


public convenience init() {
self.init(frame: CGRect.zero)
}


public override convenience init(frame: CGRect) {
self.init(internal: nil)
self.frame = frame
}


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


fileprivate func commonInit() {
}
}


fileprivate protocol _CustomView {
}


extension CustomView: _CustomView {
}


fileprivate extension _CustomView {


// Protocol extension initializer - has the ability to assign to self, unlike
// class initializers. Note that the name of this initializer can be anything
// you like, here we've called it init(internal:)


init(internal: Int?) {
self = Bundle.main.loadNibNamed("CustomView", owner:nil, options:nil)![0] as! Self;
}
}

你可以通过故事板来实现这一点,只需要为视图添加适当的约束即可。你可以通过子类化你自己的任何视图来轻松做到这一点,比如说 BaseView:

目标 C

BaseView.h




/*!
@class BaseView
@discussion Base View for getting view from xibFile
@availability ios7 and later
*/
@interface BaseView : UIView


@end




BaseView.m




#import "BaseView.h"


@implementation BaseView


#pragma mark - Public


- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self prepareView];
}
return self;
}


#pragma mark - LifeCycle


- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self prepareView];
}
return self;
}


#pragma mark - Private


- (void)prepareView
{
NSArray *nibsArray = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
UIView *view = [nibsArray firstObject];


view.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:view];
[self addConstraintsForView:view];
}


#pragma mark - Add constraints


- (void)addConstraintsForView:(UIView *)view
{
[self addConstraints:@[[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0],
[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0],
[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:0],
[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0]
]];
}


@end

Swift 4

import UIKit


class BaseView : UIView {


// MARK: - LifeCycle


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


prepareView()
}


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


prepareView()
}


internal class func xibName() -> String {
return String(describing: self)
}


// MARK: - Private
fileprivate func prepareView() {
let nameForXib = BaseView.xibName()
let nibs = Bundle.main.loadNibNamed(nameForXib, owner: self, options: nil)
if let view = nibs?.first as? UIView {
view.backgroundColor = UIColor.clear
view.translatesAutoresizingMaskIntoConstraints = false
addSubviewWithConstraints(view, offset: false)
}
}
}


UIView+Subview




public extension UIView {
// MARK: - UIView+Extensions


public func addSubviewWithConstraints(_ subview:UIView, offset:Bool = true) {
subview.translatesAutoresizingMaskIntoConstraints = false
let views = [
"subview" : subview
]
addSubview(subview)


var constraints = NSLayoutConstraint.constraints(withVisualFormat: offset ? "H:|-[subview]-|" : "H:|[subview]|", options: [.alignAllLeading, .alignAllTrailing], metrics: nil, views: views)
constraints.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: offset ? "V:|-[subview]-|" : "V:|[subview]|", options: [.alignAllTop, .alignAllBottom], metrics: nil, views: views))
NSLayoutConstraint.activate(constraints)
}
}

我提供了如何添加约束的两种变体-常见的一种和可视化格式语言-选择任何你想要的:)

另外,默认情况下假定 xib名称与实现类名称相同。如果没有-只是更改 xibName参数。

如果你从 BaseView子类化你的视图-你可以很容易地把任何视图和指定类在 IB。

最方便的实现。这里需要两个方法,以便直接返回到类的对象,而不是 UIView。

  1. ViewId 标记为 同学们,允许覆盖
  2. 您的. xib 可以包含顶层的多个视图,这种情况也是 正确处理。

extension UIView {


class var viewId: String {
return String(describing: self)
}


static func instance(from bundle: Bundle? = nil, nibName: String? = nil,
owner: Any? = nil, options: [AnyHashable : Any]? = nil) -> Self? {


return instancePrivate(from: bundle ?? Bundle.main,
nibName: nibName ?? viewId,
owner: owner,
options: options)
}


private static func instancePrivate<T: UIView>(from bundle: Bundle, nibName: String,
owner: Any?, options: [AnyHashable : Any]?) -> T? {


guard
let views = bundle.loadNibNamed(nibName, owner: owner, options: options),
let view = views.first(where: { $0 is T }) as? T else { return nil }


return view
}
}

例如:

guard let customView = CustomView.instance() else { return }


//Here customView has CustomView class type, not UIView.
print(customView is CustomView) // true
class func loadFromNib<T: UIView>() -> T {
let nibName = String(describing: self)
return Bundle.main.loadNibNamed(nibName, owner: nil, options: nil)![0] as! T
}

Swift 4

别忘了写“ . first as? CustomView”。

if let customView = Bundle.main.loadNibNamed("myXib", owner: self, options: nil)?.first as? CustomView {
self.view.addSubview(customView)
}

如果你想在任何地方使用

最佳解决方案是 罗伯特 · 甘梅森的答案。

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

那就这么说吧:

let myCustomView: CustomView = UIView.fromNib()

以下是使用协议和协议扩展(Swift 4.2)以编程方式加载视图的一种简洁且声明性的方法:

protocol XibLoadable {
associatedtype CustomViewType
static func loadFromXib() -> CustomViewType
}


extension XibLoadable where Self: UIView {
static func loadFromXib() -> Self {
let nib = UINib(nibName: "\(self)", bundle: Bundle(for: self))
guard let customView = nib.instantiate(withOwner: self, options: nil).first as? Self else {
// your app should crash if the xib doesn't exist
preconditionFailure("Couldn't load xib for view: \(self)")
}
return customView
}
}

你可以这样使用:

// don't forget you need a xib file too
final class MyView: UIView, XibLoadable { ... }


// and when you want to use it
let viewInstance = MyView.loadFromXib()

一些额外的考虑 :

  1. 确保自定义视图的 xib 文件设置了视图的 Custom Class集(以及从那里设置的插座/操作) ,而不是 File Owner 的。
  2. 您可以在自定义视图外部或内部使用此协议/扩展。如果在初始化视图时还有其他安装工作,则可能需要在内部使用它。
  3. 您的自定义视图类和 xib 文件需要具有相同的名称。

Swift 4-5.1协议扩展

public protocol NibInstantiatable {
    

static func nibName() -> String
    

}


extension NibInstantiatable {
    

static func nibName() -> String {
return String(describing: self)
}
    

}


extension NibInstantiatable where Self: UIView {
    

static func fromNib() -> Self {
        

let bundle = Bundle(for: self)
let nib = bundle.loadNibNamed(nibName(), owner: self, options: nil)
        

return nib!.first as! Self
        

}
    

}

领养

class MyView: UIView, NibInstantiatable {


}

此实现假定 Nib 具有与 UIView 类相同的名称。前女友。MyView.xib.您可以通过在 MyView 实现 nibName ()来修改这种行为,以返回与默认协议扩展实现不同的名称。

在 xib 中,文件所有者是 MyView,根视图类是 MyView。

用法

let view = MyView.fromNib()
  let bundle = Bundle(for: type(of: self))
let views = bundle.loadNibNamed("template", owner: self, options: nil)
self.view.addSubview(views?[0] as! UIView)

我更喜欢下面的分机

extension UIView {
class var instanceFromNib: Self {
return Bundle(for: Self.self)
.loadNibNamed(String(describing: Self.self), owner: nil, options: nil)?.first as! Self
}
}

这个扩展名和顶部应答扩展名的区别在于,你不需要将它存储为常量或变量。

class TitleView: UIView { }


extension UIView {
class var instanceFromNib: Self {
return Bundle(for: Self.self)
.loadNibNamed(String(describing: Self.self), owner: nil, options: nil)?.first as! Self
}
}


self.navigationItem.titleView = TitleView.instanceFromNib
    let nibs = Bundle.main.loadNibNamed("YourView", owner: nil, options: nil)
let shareView = nibs![0] as! ShareView
self.view.addSubview(shareView)

快速5-清洁和易于使用的扩展

[从生产项目复制粘贴]

//
//  Refactored by Essam Mohamed Fahmi.
//


import UIKit


extension UIView
{
static var nib: UINib
{
return UINib(nibName: "\(self)", bundle: nil)
}


static func instantiateFromNib() -> Self?
{
return nib.instantiate() as? Self
}
}


extension UINib
{
func instantiate() -> Any?
{
return instantiate(withOwner: nil, options: nil).first
}
}

用法

let myCustomView: CustomView = .instantiateFromNib()

//使用此类作为超视图

import UIKit


class ViewWithXib: UIView {


func initUI() {}


private func xibSetup() {
let view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
addSubview(view)
initUI()
}


private func loadViewFromNib() -> UIView {
let thisName = String(describing: type(of: self))
let view = Bundle(for: self.classForCoder).loadNibNamed(thisName, owner: self, options: nil)?.first as! UIView
return view
}




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


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


}

//用途:

class HeaderView: ViewWithXib {
}




let header = HeaderView() // No need to load the view from nib, It will work

更新为 Swift 5

在下面的某个地方定义:

extension UIView {
public class func fromNib<T: UIView>() -> T {
let name = String(describing: Self.self);
guard let nib = Bundle(for: Self.self).loadNibNamed(
name, owner: nil, options: nil)
else {
fatalError("Missing nib-file named: \(name)")
}
return nib.first as! T
}
}

使用如下:

let view: MyCustomView = .fromNib();

它将在与 MyCustomView相同的绑定包中搜索,然后加载 MyCustomView.nib文件(如果文件存在,并将其添加到 project)。

RobertGummeson 的 回答我是完美的,但是当我们尝试在 SPM 或者框架中使用它时,它不起作用。
我已经修改如下,使其工作。

internal class func fromNib<T: UIView>() -> T {
return Bundle.module.loadNibNamed(String(describing: T.self), owner: self, options: nil)![0] as! T
}