Завантажте UIView від ручки в Swift


148

Ось мій код Objective-C, який я використовую для завантаження кнопок для моїх індивідуальних UIView:

-(id)init{

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

}

Який еквівалентний код у Swift?

Відповіді:


172

Оригінальне рішення

  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. Цей метод додає верхнє, нижнє, провідні та обмежувальні обмеження - приєднання виду до "Я" з усіх боків (див. 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. SomeClass - це підклас UIView, який завантажує його вміст з файлу SomeClass.xib. Ключове слово "остаточне" не є обов'язковим.
  2. Ініціалізатор, коли представлення використовується у дошці розкадрування (не забудьте використовувати SomeClass як спеціальний клас перегляду вашої розкадровки).
  3. Ініціалізатор, коли представлення створено програмно (тобто: "нехай myView = SomeView ()").
  4. Використання кадру з усіма нулями, оскільки цей вид викладено за допомогою автоматичного макета. Зауважте, що метод "init (frame: CGRect) {..}" не створюється самостійно, оскільки автоматичне розміщення використовується виключно в нашому проекті.
  5. & 6. Завантаження файлу xib за допомогою розширення.

Кредит: Використання загального розширення у цьому рішенні надихнуло відповідь Роберта нижче.

Відредагуйте Змінення "перегляду" на "contentView", щоб уникнути плутанини. Також змінив підрозділ масиву на ".first".


7
Налаштування назви класу, щоб File's Ownerпотрапити на місце ... Дякую!
Aviel Gross

13
У UIView немає перегляду властивостей, тому виклик self.view викликає помилку
Настя Горбань

4
@NastyaGorban self.view фактично посилається в цьому випадку на властивість розетки (з назвою "view), яка GK100 пов'язана з подання верхнього рівня в. "Власність у класах NSView, як ви говорите.
Ausiàs

3
Я отримую збої під час завантаження nib (loadNibNamed). Використовуючи Xcode 6.3 та Swift
karthikPrabhu Alagu

11
виклик fromNib()зсередини init(coder aDecoder: NSCoder)створює нескінченний цикл, коли завантаження ручки всередині fromNib()методу робить дзвінок:init(coder aDecoder: NSCoder)
Меттью Каулі

334

Мій внесок:

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

20
Найкраща відповідь на сьогоднішній день.
CodyMace

7
Найкраща відповідь тут. Чисто та просто
Поважний драггон

3
@YuchenZhong - я віддаю перевагу [0] over .first, тому що повертається необов’язково. Якщо ви змусите його розгортати, це не було б безпечніше. ... і це ставить питання: чому б не повернути необов'язкове як деякі з перерахованих вище рішень? Відповідь: Можна. Нічого поганого в цьому немає. Але ... якщо він коли-небудь поверне нуль, ім'я xib / class не збігається. Це помилка розробника, і її слід негайно спіймати і ніколи не робити її виробництвом. Тут я вважаю за краще збій програми через те, щоб залишити його в якомусь дивному стані. Тільки мої 2 копійки / уподобання.
Роберт Гуммессон

1
@allenlinli - метод являє собою статичне розширення UIView, як належить CustomView. Це працює, тому що компілятор виводить тип, використовуючи явну примітку типу. Оскільки CustomView є підкласом UIView і тип уже зроблений, нам не потрібно робити це знову, тому UIView можна опустити, як показано в моєму другому прикладі. Зауваживши це, ви, очевидно, могли б здійснити дзвінок так, як і його відклали.
Роберт Гуммессон

3
Це рішення не працювало для мене у випадку, коли всередині .xib був спеціальний вид. Я б запропонував виправити цю частину на: return Bundle.main.loadNibNamed (String (описуючи: self), власник: nil, параметри: nil)! [0] as! Т
Надія

79

Тепер можливість -> Selfшвидкого повернення допомагає спростити це трохи. Востаннє підтверджено на Swift 5.

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вводі введіть свій клас


1
Я не можу змусити це працювати під XCode 7.1 beta 3 - не впевнений, чи це бета-річ, але в основному я намагався всіляко створити спеціальний вигляд безпосередньо з гнізда в Swift, і я завжди отримую той же результат: клас, який він створює не відповідає KVC торговим точкам. Не впевнений, чи я щось роблю неправильно, але мій клас досить простий і власник файлу правильний. Раніше я робив це весь час у "Objective-C".
Ешелон

1
@Logan це не дуже пов’язано з вашим кодом, але користувацькі перегляди imo повинні підтримувати завантаження з Storyboard / XIB. Мій коментар був лише сповіщенням для тих, хто хоче створити такі погляди
Микита взяв

1
Примітка. У мене все ще є проблема використання другої форми виклику цієї функції, а саме let myCustomView = UIView.fromNib() as? CustomView. У цьому випадку T.selfвирішується UIViewзамість цього, CustomViewі не вдається знайти ручку. Я не впевнений, чому це так - можливо, виведений тип для letзасобів функція називається як UIView?
Ешелон

2
Важливо зазначити, що спроба використання власника File для підключення торгових точок (як ми це робили в добрі дні) призведе до цього аварії. У IB власник файлу повинен бути нульовим / порожнім, а розетки повинні бути підключені до подання.
Ешелон

1
@Echelon ти врятував мені день !!! Я підключив свої торгові точки за допомогою власника файлу, і він не працював, використовуючи подання замість цього.
Ян Шлорф

19

спробуйте наступний код.

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
    }


}

Мені потрібно викликати цей метод всередині методу ініціалізації об'єкта, який є init () у Swift.
Bagusflyer

12

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

У xib власником файлів є MyView, а клас кореневого перегляду - MyView.

Використання

let view = MyView.fromNib()

3
Це, безумовно, найелегантніше, найпростіше рішення, і я поняття не маю, чому це не прийнята відповідь!
horseshoe7

@ підкова7, оскільки його написано через 4 роки після питання.
Freeubi

11

Я домігся цього за допомогою 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 на своє власне клас класу, щоб почати підключення будь-яких додаткових торгових точок.

Сподіваюся, це допомагає!


10

Тестовано на 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


9

Якщо у вашому проекті багато спеціальних представлень, ви можете створити клас UIViewFromNib

Швидкий 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 {

}

8

Спираючись на вищезазначені рішення.

Це буде працювати в усіх наборах проектів і не потрібно генеріки при виклику зNib ().

Швидкий 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")

6

Швидкий 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()

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

Але в init () Swift не повертається значення. Я забув згадати, що мені потрібно викликати loadNibNamed при ініціалізації UIView.
Bagusflyer

Що ви маєте на увазі "немає значення повернення"? selfявно повертається з усіх initметодів ...
Grimxn

1
Що я маю на увазі, це я називаю loadNibNamed всередині методу init. завантажений UIView призначається самому в ObjC. Але швидко, це не так.
Bagusflyer

5

Приємний спосіб зробити це зі Свіфтом - це використовувати 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).first as? UIView
    }
}

Тоді у своєму коді ви можете просто використовувати:

let view = Views.view1.getView()

2
Зауважте, що якщо ви зробите це з порожнім файлом nib або файлом nib без жодного кореневого вузла UIView, ви зринетесь, оскільки ви не будете перевіряти розмір масиву або елемент у позиції 0.
Matthew Cawley

4

Я вважаю за краще це рішення (виходячи з відповіді, якщо @ 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
      }
    
    
      ...
    }

Я вважаю за краще використовувати init з рамкою, тому я викорінив це! одне, що слід зазначити ... додайте self.view.frame = кадр, якщо ви хочете, щоб представлення відповідало кадру, який ви проходите
Майк Е,

3

Швидка версія 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
        }
    }
}

3

Ось чистий та декларативний спосіб програмного завантаження представлення даних за допомогою розширення протоколу та протоколу (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 повинні мати однакове ім’я.

2

Я просто так роблю:

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

Цей зразок використовує перший вид у вікні "MyView.xib" в основному пакеті. Але ви можете змінювати або індекс, назву ручки, або комплект (основний за замовчуванням).

Я звик прокидатися думками про метод init view або робити загальні методи, як у рішеннях вище (які, до речі, розумні), але більше не роблю цього.

Таким чином я можу використовувати різні макети або риси, зберігаючи однакову логіку та код перегляду.

Мені легше дозволити заводському об’єкту (зазвичай viewController, який буде використовувати представлення), створити його так, як йому потрібно. Іноді потрібен власник (зазвичай, коли створений вигляд отримав розетку, підключену до творця), іноді ні.

Можливо, тому Apple не включила initFromNibметод у свій клас UIView ...

Для прикладу наземного рівня ви не знаєте, як ви народилися. Ви просто народжені. Так само погляди;)


2

Все, що вам потрібно зробити - це викликати метод 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
    }
}

Тепер, якщо ви хочете додати цей вигляд як підпогляд у контролері подання, зробіть це так у файлі viewlleller.swift:

self.view.addSubview(className())

це чудова відповідь, але щось не так, я відредагую це.
C0mrade

Це спосіб, який я реалізував. Але ви можете імпровізувати це. Заздалегідь дякую @ C0mrade
Alap Anerao

1

Подібно до деяких відповідей вище, але більш послідовне розширення 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, але також і з інших ниб / пакетів.


1

Це можна зробити за допомогою раскадровки, просто додайте належні обмеження для перегляду. Це можна зробити легко, підкласифікувавши будь-який вид з власного, скажімо BaseView:

Ціль-С

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

Швидкий 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- ви можете легко поставити будь-який вид і вказати клас в ІБ.


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

0

Більш потужна версія, заснована на відповіді Логана

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

0

Найзручніша реалізація. Тут вам потрібні два способи, щоб повернутися безпосередньо до об’єкту вашого класу, а не до 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

0

Якщо ви хочете, щоб підклас 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;
    }
}

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

Відповіді з коду відмовляються. Будь ласка, додайте пояснення, як це вирішує проблему чи чим вона відрізняється від існуючих відповідей. З огляду
Нік

0

Я віддаю перевагу нижче розширення

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

Яку версію Xcode ви використовуєте? Переконайтеся, що ви використовуєте останню версію XCode. Для мене добре працює з XCode 11.5 (остання версія на дату).
iCoder

0
    let nibs = Bundle.main.loadNibNamed("YourView", owner: nil, options: nil)
    let shareView = nibs![0] as! ShareView
    self.view.addSubview(shareView)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.