Загрузить UIView из пера в Swift

Вот мой код Objective C, который я использую, чтобы загрузить перо для моего настроенного UIView:

-(id)init{

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

}

Каков эквивалентный код в Swift?

30 ответов

Решение

Оригинальное решение

  1. Я создал XIB и класс с именем SomeView (использовал одно и то же имя для удобства и читабельности). Я основал оба на UIView.
  2. В XIB я изменил класс "Владелец файла" на SomeView (в инспекторе удостоверений).
  3. Я создал выход UIView в SomeView.swift, связав его с представлением верхнего уровня в файле XIB (для удобства его назвали "представлением"). Затем я добавил другие выходы к другим элементам управления в файле XIB по мере необходимости.
  4. в SomeView.swift я загрузил XIB внутри инициализатора "init with code". Нет необходимости присваивать что-либо "себе". Как только 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
   }
   ...
}

Обратите внимание, что таким образом я получаю класс, который загружает себя из пера. Затем я мог бы использовать SomeView в качестве класса всякий раз, когда UIView можно использовать в проекте (в конструкторе интерфейса или программно).

Обновление - с использованием синтаксиса 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 с тем же именем, что и текущий экземпляр класса. Если это не удается, возвращается ноль.
  4. Добавление представления верхнего уровня в иерархию представлений.
  5. В этой строке предполагается, что мы используем ограничения для компоновки представления.
  6. Этот метод добавляет верхние, нижние, ведущие и конечные ограничения - прикрепляя представление к "себе" со всех сторон (подробности см.: /questions/27167536/nib-fajl-zagruzhennyij-uiview-v-uitableviewcell-ne-rastyagivaetsya/27167560#27167560)
  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. SomeClass - это подкласс UIView, который загружает свое содержимое из файла SomeClass.xib. Ключевое слово final не обязательно.
  2. Инициализатор, когда представление используется в раскадровке (не забудьте использовать SomeClass в качестве пользовательского класса представления раскадровки).
  3. Инициализатор, когда представление создается программно (т. Е. "Let myView = SomeView()").
  4. Использование фрейма со всеми нулями, поскольку это представление выложено с помощью автоматического макета. Обратите внимание, что метод init(frame: CGRect) {..}"не создается независимо, так как автоматическая разметка используется исключительно в нашем проекте.
  5. & 6. Загрузка файла XIB с использованием расширения.

Кредит: Использование общего расширения в этом решении было вдохновлено ответом Роберта ниже.

Редактирование Изменение "view" на "contentView", чтобы избежать путаницы. Также изменил индекс массива на ".first".

Мой вклад:

Свифт 3 / Свифт 4

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()

..или даже:

let myCustomView: CustomView = .fromNib()

Вот расширение, которое использует дженерики для загрузки UIView из кончика

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

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

    public class func fromNib<T : UIView>(nibNameOrNil: String? = nil, type: T.Type) -> T? {
        var view: T?
        let name: String
        if let nibName = nibNameOrNil {
            name = nibName
        } else {
            // Most nibs are demangled by practice, if not, just declare string explicitly
            name = nibName
        }
        let nibViews = NSBundle.mainBundle().loadNibNamed(name, owner: nil, options: nil)
        for v in nibViews {
            if let tog = v as? T {
                view = tog
            }
        }
        return view
    }

    public class var nibName: String {
        let name = "\(self)".componentsSeparatedByString(".").first ?? ""
        return name
    }
    public class var nib: UINib? {
        if let _ = NSBundle.mainBundle().pathForResource(nibName, ofType: "nib") {
            return UINib(nibName: nibName, bundle: nil)
        } else {
            return nil
        }
    }
}

Я предпочитаю это, так как это не требует никакой дополнительной настройки в кончике. Он основан на общих соглашениях об именах, поэтому, если ваш класс CustomView, и это соответствует перо с именем: CustomViewВы могли бы просто сделать это:

let myCustomView = CustomView.fromNib()
// or if you're unsure whether or not the nib exists
let myCustomView: CustomView? = CustomView.fromNib()

Если по какой-либо причине вам нужно быть точным в отношении имени пера, передайте строку arg:

let myCustomView = MyCustomView.fromNib("non-conventional-name")

Известные вопросы

Использование этого с закрытым классом представления, кажется, вызывает проблемы. Похоже, это проблема всей системы.

Swift 4 расширения протокола

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. Ex. MyView.xib. Вы можете изменить это поведение, реализовав nibName() в MyView, чтобы возвращать имя, отличное от реализации расширения протокола по умолчанию.

В xib владельцем файла является MyView, а корневым классом представления является MyView.

использование

let view = MyView.fromNib()

Попробуйте следующий код.

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
    }


}

Если у вас много пользовательских представлений в вашем проекте, вы можете создать класс как 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)
    }
}

Свифт 3

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() {
        contentView = Bundle.main.loadNibNamed(nibName, owner: self, options: nil)?[0] as! UIView
        contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        contentView.frame = bounds
        addSubview(contentView)
    }
}

И в каждом классе просто наследовать от UIViewFromNibТакже вы можете переопределить nibName свойство если .xib файл имеет другое имя:

class MyCustomClass: UIViewFromNib {

}

Я добился этого с помощью 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 для просмотра розетки, определенной в swift. Вы также можете установить First Responder на свое имя класса, чтобы начать подключать любые дополнительные розетки.

Надеюсь это поможет!

Протестировано в 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);



    }


}

Доступ к customview программно

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

Исходный код - https://github.com/karthikprabhuA/CustomXIBSwift

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()

Опираясь на вышеуказанные решения.

Это будет работать во всех пакетах проектов и не требует обобщений при вызове fromNib ().

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)
    }
}

Свифт 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")

Я предпочитаю это решение (на основе ответа, если @GK100):

  1. Я создал XIB и класс с именем SomeView (использовал одно и то же имя для удобства и читабельности). Я основал оба на UIView.
  2. В XIB я изменил класс "Владелец файла" на SomeView (в инспекторе удостоверений).
  3. Я создал выход UIView в SomeView.swift, связав его с представлением верхнего уровня в файле XIB (для удобства его назвали "представлением"). Затем я добавил другие выходы к другим элементам управления в файле XIB по мере необходимости.
  4. В SomeView.swift я загрузил XIB внутри init или же init: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 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();

Хороший способ сделать это с помощью Swift - использовать enum.

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)[0] as! UIView
    }
}

Тогда в вашем коде вы можете просто использовать:

let view = Views.view1.getView()
let subviewArray = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)
return subviewArray[0]

Я просто делаю так:

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

В этом примере используется первое представление в наконечнике "MyView.xib" в основном комплекте. Но вы можете изменить либо индекс, имя пера, либо комплект (по умолчанию main).

Раньше я пробуждал представления в методе view init или создавал общие методы, как в приведенных выше решениях (которые, кстати, умны), но я больше этим не занимаюсь.

Таким образом, я могу использовать разные макеты или характеристики, сохраняя логику и код одного и того же представления.

Мне проще позволить фабричному объекту (обычно viewController, который будет использовать представление) создавать его так, как ему нужно. Иногда вам нужен владелец (обычно, когда созданный вид получает розетку, связанную с создателем), иногда нет..

Возможно, поэтому Apple не включила initFromNib метод в своем классе UIView...

Чтобы взять пример с уровня земли, вы не знаете, как вы родились. Ты просто родился. Так же как и мнения;)

Swift 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()

Swift 3 версия ответа Логана

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
        }
    }
}

Вот простой и декларативный способ программной загрузки представления с использованием протокола и расширения протокола (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 набор (и выходы / действия, установленные оттуда), а не владельца файла.
  2. Вы можете использовать этот протокол / расширение вне вашего собственного представления или внутри. Возможно, вы захотите использовать его внутри, если у вас есть другие настройки при инициализации вашего представления.
  3. Ваш пользовательский класс представления и файл xib должны иметь одно и то же имя.

Все, что вам нужно сделать, это вызвать метод init в вашем UIView учебный класс.

Сделайте это так:

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.swift:

self.view.addSubview(className())

Если вы хотите, чтобы подкласс 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;
    }
}
class func loadFromNib<T: UIView>() -> T {
    let nibName = String(describing: self)
    return Bundle.main.loadNibNamed(nibName, owner: nil, options: nil)![0] as! T
}

Аналогично некоторым ответам выше, но более последовательному расширению 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, а также из других nibs/bundle.

Вы можете сделать это через раскадровку, просто добавив соответствующие ограничения для просмотра. Вы можете сделать это легко, создав подклассы любого представления, скажем BaseView:

Objective-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)
    }
}

Я предоставляю 2 варианта, как добавить ограничения - общий и на языке визуального формата - выберите любой, который вы хотите:)

Также по умолчанию предполагается, что xib имя имеет то же имя, что и имя класса реализации. Если нет - просто поменяй xibName параметр.

Если вы подкласса своего взгляда из BaseView - Вы можете легко поставить любой вид и указать класс в IB.

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

// Используем этот класс как суперпредставление

      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

Ответ Роберта Гаммессона идеален. Но когда мы пытаемся использовать его в SPM или фреймворке, он не работает.
Я изменил, как показано ниже, чтобы заставить его работать.

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

Я предпочитаю расширение ниже

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

Наиболее удобная реализация. Здесь вам нужно два метода, чтобы вернуться непосредственно к объекту вашего класса, а не к 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

Более мощная версия, основанная на ответе Логана

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 }
}
  let bundle = Bundle(for: type(of: self))
   let views = bundle.loadNibNamed("template", owner: self, options: nil)
    self.view.addSubview(views?[0] as! UIView)
Другие вопросы по тегам