Как загрузить UIView с помощью nib-файла, созданного с помощью Interface Builder

Я пытаюсь сделать что-то немного сложнее, но то, что должно быть возможно. Так что это вызов для всех вас, экспертов (на этом форуме много ребят:)).

Я создаю "компонент" вопросника, который я хочу загрузить на NavigationContoller (мой QuestionManagerViewController). "Компонент" является "пустым" UIViewController, который может загружать разные взгляды в зависимости от вопроса, на который нужно ответить.

Я делаю это так:

  1. Создать объект Question1View как UIView подкласс, определяя некоторые IBOutlets,
  2. Создать (используя Interface Builder) Question1View.xib (ЗДЕСЬ, ГДЕ МОЯ ПРОБЛЕМА, ВЕРОЯТНО). Я установил оба UIViewController и UIView быть класса Question1View.
  3. Я связываю торговые точки с компонентом представления (используя IB).
  4. Я отменяю initWithNib из моего QuestionManagerViewController выглядеть так:

    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
        if (self = [super initWithNibName:@"Question1View" bundle:nibBundleOrNil]) {
            // Custom initialization
        }
        return self;
    }
    

Когда я запускаю код, я получаю эту ошибку:

2009-05-14 15: 05: 37.152 iMobiDines [17148: 20b] *** Завершение работы приложения из-за необработанного исключения 'NSInternalInconsistencyExceptionпричина:-[UIViewController _loadViewFromNibNamed:bundle:] загрузил перо "Question1View", но розетка вида не была установлена. '

Я уверен, что есть способ загрузить представление, используя nib-файл, без необходимости создавать класс viewController.

24 ответа

Есть также более простой способ получить доступ к представлению вместо того, чтобы иметь дело с пером как массивом.

1) Создайте пользовательский подкласс View с любыми точками, к которым вы хотите иметь доступ позже. --Мой взгляд

2) в UIViewController, который вы хотите загрузить и обработать перо, создайте свойство IBOutlet, которое будет содержать представление загруженного кончика, например,

в MyViewController (подкласс UIViewController)

  @property (nonatomic, retain) IBOutlet UIView *myViewFromNib;

(не забудьте синтезировать его и выпустить в файле.m)

3) откройте свой кончик (мы назовем его "myViewNib.xib") в IB, установите в качестве владельца файла MyViewController.

4) Теперь подключите выход MyViewFromNib владельца файла к основному виду в кончике.

5) Теперь в MyViewController напишите следующую строку:

[[NSBundle mainBundle] loadNibNamed:@"myViewNib" owner:self options:nil];

Теперь, как только вы сделаете это, вызов вашего свойства "self.myViewFromNib" даст вам доступ к представлению с вашего кончика!

Спасибо вам всем. Я нашел способ сделать то, что хотел.

  1. Создайте свой UIView с IBOutletВам нужно.
  2. Создайте xib в IB, разработайте его по своему вкусу и свяжите так: владелец файла принадлежит к классу UIViewController (Нет пользовательского подкласса, кроме "реального"). Представление "Владелец файла" подключено к основному представлению, а его класс объявлен как класс из шага 1).
  3. Соедините ваши элементы управления с IBOutlets.
  4. DynamicViewController может запустить свою логику, чтобы решить, какой view/xib загрузить. Как только он принял решение, в loadView метод выложить примерно так:

    NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"QPickOneView"
                                                      owner:self
                                                    options:nil];
    
    QPickOneView* myView = [ nibViews objectAtIndex: 1];
    
    myView.question = question;
    

Это оно!

Основной комплект loadNibNamed Метод позаботится об инициализации представления и создании соединений.

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

Я не уверен, о чем идет речь в некоторых ответах, но мне нужно разместить этот ответ здесь, когда я буду искать в Google в следующий раз. Ключевые слова: "Как загрузить UIView из пера" или "Как загрузить UIView из NSBundle".

Вот код почти на 100% прямо из книги Apress Beginning для iPhone 3 (стр. 247, "Использование новой ячейки табличного представления"):

- (void)viewDidLoad {
    [super viewDidLoad];
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:@"Blah"
                                                 owner:self options:nil];
    Blah *blah;
    for (id object in bundle) {
        if ([object isKindOfClass:[Blah class]]) {
            blah = (Blah *)object;
            break;
        }
    }   
    assert(blah != nil && "blah can't be nil");
    [self.view addSubview: blah];
} 

Это предполагает, что у вас есть UIView подкласс называется Blah, перо называется Blah который содержит UIView который имеет свой класс установлен в Blah,


Категория: NSObject+LoadFromNib

#import "NSObject+LoadFromNib.h"

@implementation NSObject (LoadFromNib)

+ (id)loadFromNib:(NSString *)name classToLoad:(Class)classToLoad {
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:name owner:self options:nil];
    for (id object in bundle) {
        if ([object isKindOfClass:classToLoad]) {
            return object;
        }
    }
    return nil;
}

@end

Быстрое расширение

extension UIView {
    class func loadFromNib<T>(withName nibName: String) -> T? {
        let nib  = UINib.init(nibName: nibName, bundle: nil)
        let nibObjects = nib.instantiate(withOwner: nil, options: nil)
        for object in nibObjects {
            if let result = object as? T {
                return result
            }
        }
        return nil
    }
}

И пример в использовании:

class SomeView: UIView {
    class func loadFromNib() -> SomeView? {
        return self.loadFromNib(withName: "SomeView")
    }
}

Для всех тех, кому необходимо управлять более чем одним экземпляром пользовательского представления, а именно коллекцией розеток, я объединил и настроил ответы @Gonso, @AVeryDev и @Olie следующим образом:

  1. Создать кастом MyView : UIView и установите его как " Пользовательский класс " корня UIView в желаемом XIB; пользовательский класс

  2. Создайте все торговые точки, в которых вы нуждаетесь MyView (сделайте это сейчас, потому что после пункта 3 МБ предложит вам подключить розетки к UIViewController а не на кастомный вид как мы хотим); розетка нестандартного класса

  3. Установите свой UIViewController как " Владелец файла " пользовательского представления XIB;

  4. в UIViewController добавить новый UIViews для каждого экземпляра MyView вы хотите, и подключить их к UIViewController создание Outlet Collection: эти представления будут действовать как "оболочки" для пользовательских экземпляров представления;

  5. Наконец, в viewDidLoad вашей UIViewController добавьте следующие строки:

NSArray *bundleObjects;
MyView *currView;
NSMutableArray *myViews = [NSMutableArray arrayWithCapacity:myWrapperViews.count];
for (UIView *currWrapperView in myWrapperViews) {
    bundleObjects = [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil];
    for (id object in bundleObjects) {
        if ([object isKindOfClass:[MyView class]]){
            currView = (MyView *)object;
            break;
        }
    }

    [currView.myLabel setText:@"myText"];
    [currView.myButton setTitle:@"myTitle" forState:UIControlStateNormal];
    //...

    [currWrapperView addSubview:currView];
    [myViews addObject:currView];
}
//self.myViews = myViews; if need to access them later..

Я бы использовал UINib для создания экземпляра пользовательского UIView для повторного использования

UINib *customNib = [UINib nibWithNibName:@"MyCustomView" bundle:nil];
MyCustomViewClass *customView = [[customNib instantiateWithOwner:self options:nil] objectAtIndex:0];
[self.view addSubview:customView];

В этом случае необходимы файлы MyCustomView.xib, MyCustomViewClass.h и MyCustomViewClass.m.[UINib instantiateWithOwner] возвращает массив, поэтому вы должны использовать элемент, который отражает UIView, который вы хотите использовать повторно. В данном случае это первый элемент.

Вы не должны устанавливать класс вашего контроллера представления как подкласс UIView в Интерфейсном Разработчике. Это определенно, по крайней мере, часть вашей проблемы. Оставьте это как либо UIViewControllerкакой-то его подкласс или какой-то другой пользовательский класс, который у вас есть.

Что касается загрузки только представления из XIB, я предполагал, что вам нужно иметь какой-то контроллер представления (даже если он не расширяется UIViewController(который может быть слишком тяжелым для ваших нужд) установите в качестве владельца файла в Интерфейсном Разработчике, если вы хотите использовать его для определения вашего интерфейса. Я провёл небольшое исследование, чтобы подтвердить это. Это потому, что в противном случае не было бы никакого доступа к любому из элементов интерфейса в UIViewи не было бы способа, чтобы ваши собственные методы в коде запускались событиями.

Если вы используете UIViewController в качестве владельца вашего файла для ваших просмотров, вы можете просто использовать initWithNibName:bundle: загрузить его и вернуть объект контроллера представления назад. В IB убедитесь, что вы установили view выход на просмотр с вашим интерфейсом в xib. Если вы используете какой-либо другой тип объекта в качестве владельца вашего файла, вам нужно будет использовать NSBundle"s loadNibNamed:owner:options: метод для загрузки пера, передавая экземпляр владельца файла методу. Все его свойства будут установлены правильно в соответствии с точками, которые вы определили в IB.

Вы также можете использовать initWithNibName UIViewController вместо loadNibNamed. Это проще, я нахожу.

UIViewController *aViewController = [[UIViewController alloc] initWithNibName:@"MySubView" bundle:nil];
[self.subview addSubview:aViewController.view];
[aViewController release];  // release the VC

Теперь вам просто нужно создать MySubView.xib и MySubView.h / m. В MySubView.xib установите для класса "Владелец файла" значение UIViewController, а для класса представления - "MySubView".

Вы можете позиционировать и размер subview используя родительский файл XIB.

Это то, что должно быть проще. Я закончил расширение UIViewController и добавив loadNib:inPlaceholder: селектор. Теперь я могу сказать

self.mySubview = (MyView *)[self loadNib:@"MyView" inPlaceholder:mySubview];

Вот код для категории (она делает то же самое ригамароле, как описано Гонсо):

@interface UIViewController (nibSubviews)

- (UIView *)viewFromNib:(NSString *)nibName;
- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder;

@end

@implementation UIViewController (nibSubviews)

- (UIView *)viewFromNib:(NSString *)nibName
{
  NSArray *xib = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil]; 
  for (id view in xib) { // have to iterate; index varies
    if ([view isKindOfClass:[UIView class]]) return view;
  }
  return nil;
}

- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder
{
  UIView *nibView = [self viewFromNib:nibName];
  [nibView setFrame:placeholder.frame];
  [self.view insertSubview:nibView aboveSubview:placeholder];
  [placeholder removeFromSuperview];
  return nibView;
}

@end

Это отличный вопрос (+1), и ответы были почти полезны;) Извините, ребята, но у меня было огромное количество времени, пробирающегося через это, хотя и Gonso, и AVeryDev дали хорошие подсказки. Надеюсь, этот ответ поможет другим.

MyVC является контроллером представления, содержащим все эти вещи.

MySubview это представление, которое мы хотим загрузить из XIB

  • В MyVC.xib создайте представление типа MySubView это правильный размер и форма и расположен там, где вы хотите.
  • В MyVC.h есть

    IBOutlet MySubview *mySubView
    // ...
    @property (nonatomic, retain) MySubview *mySubview;
    
  • В MyVC.m, @synthesize mySubView; и не забудьте выпустить его в dealloc,

  • В MySubview.h есть выход / свойство для UIView *view (может быть ненужным, но сработало для меня.) Синтезировать и выпустить его в.m
  • В MySubview.xib
    • установить тип владельца файла MySubviewи свяжите свойство view с вашим view.
    • Выложите все биты и подключитесь к IBOutletпо желанию
  • Вернуться в MyVC.m, есть

    NSArray *xibviews = [[NSBundle mainBundle] loadNibNamed: @"MySubview" owner: mySubview options: NULL];
    MySubview *msView = [xibviews objectAtIndex: 0];
    msView.frame = mySubview.frame;
    UIView *oldView = mySubview;
    // Too simple: [self.view insertSubview: msView aboveSubview: mySubview];
    [[mySubview superview] insertSubview: msView aboveSubview: mySubview]; // allows nesting
    self.mySubview = msView;
    [oldCBView removeFromSuperview];
    

Немного сложным для меня было то, что подсказки в других ответах загружали мое представление из xib, но НЕ заменяли представление в MyVC (да!) - мне пришлось поменять его самостоятельно.

Кроме того, чтобы получить доступ к mySubviewМетоды view свойство в файле.xib должно быть установлено на MySubview, В противном случае, он возвращается как старый UIView,

Если есть способ загрузить mySubview Прямо из его собственной XIB, это было бы здорово, но это привело меня туда, где я должен был быть.

В быстром

На самом деле я решил эту проблему, загрузив представление в viewDidLoad в моем CustonViewController, где я хотел использовать представление следующим образом:

myAccessoryView = NSBundle.mainBundle().loadNibNamed("MyAccessoryView", owner: self, options: nil)[0] as! MyAccessoryView

Не загружайте вид в loadView() метод! Метод loadView служит для загрузки представления для вашего пользовательского ViewController.

Я сделал категорию, которая мне нравится:

UIView+NibInitializer.h

#import <UIKit/UIKit.h>

@interface UIView (NibInitializer)
- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil;
@end

UIView+NibInitializer.m

#import "UIView+NibInitializer.h"

@implementation UIView (NibInitializer)

- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
    if (!nibNameOrNil) {
        nibNameOrNil = NSStringFromClass([self class]);
    }
    NSArray *viewsInNib = [[NSBundle mainBundle] loadNibNamed:nibNameOrNil
                                                        owner:self
                                                      options:nil];
    for (id view in viewsInNib) {
        if ([view isKindOfClass:[self class]]) {
            self = view;
            break;
        }
    }
    return self;
}

@end

Затем позвоните так:

MyCustomView *myCustomView = [[MyCustomView alloc] initWithNibNamed:nil];

Используйте имя пера, если ваше перо названо не так, как имя вашего класса.

Чтобы переопределить его в ваших подклассах для дополнительного поведения, это может выглядеть так:

- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
    self = [super initWithNibNamed:nibNameOrNil];
    if (self) {
        self.layer.cornerRadius = CGRectGetHeight(self.bounds) / 2.0;
    }
    return self;
}

Я тоже хотел сделать что-то подобное, вот что я нашел: (SDK 3.1.3)

У меня есть контроллер представления A (принадлежащий контроллеру Nav), который загружает VC B нажатием кнопки:

В AViewController.m

BViewController *bController = [[BViewController alloc] initWithNibName:@"Bnib" bundle:nil];
[self.navigationController pushViewController:bController animated:YES];
[bController release];

Теперь VC B имеет свой интерфейс от Bnib, но когда нажата кнопка, я хочу перейти в "режим редактирования", в котором есть отдельный пользовательский интерфейс от другого пера, но я не хочу новый VC для режима редактирования, Я хочу, чтобы новый кончик был связан с моим существующим B VC.

Итак, в BViewController.m (в методе нажатия кнопки)

NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:@"EditMode" owner:self options:nil];
UIView *theEditView = [nibObjects objectAtIndex:0];
self.editView = theEditView;
[self.view addSubview:theEditView];

Затем нажмите другую кнопку (чтобы выйти из режима редактирования):

[editView removeFromSuperview];

и я вернулся к своему первоначальному Бнибу.

Это прекрасно работает, но учтите, что мой EditMode.nib содержит только 1 объект верхнего уровня, объект UIView. Неважно, установлен ли Владелец файла в этом перо как BViewController или NSObject по умолчанию, НО убедитесь, что для параметра View Outlet в Владельце файла НЕ установлено ничего. Если это так, то я получаю аварийное завершение exc_bad_access, и xcode переходит к загрузке 6677 кадров стека, показывая неоднократно вызываемый внутренний метод UIView... так что это выглядит как бесконечный цикл. (Однако в моем исходном Bnib установлен View Outlet)

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

Для пользователя Swift с настраиваемой опцией:

  1. Создать кастом UIView подкласс и файлы xib, которые мы назовем в честь нашего собственного имени класса: в нашем случае MemeView. Внутри класса Meme View не забудьте определить его как дизайн с помощью @IBDesignable атрибут перед объявлением класса
  2. Помните, чтобы установить владельца файла в xib с нашим обычаем UIView подкласс в панели Indetity Inspector

  3. Теперь в xib-файле мы можем построить наш интерфейс, создать ограничения, создать точки сбыта, действия и т. Д.

  4. Нам нужно реализовать несколько методов для нашего пользовательского класса, чтобы открыть XIB после инициализации

    class XibbedView: UIView {

    weak var nibView: UIView!
    
    override convenience init(frame: CGRect) {
        let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
        self.init(nibName: nibName)
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
        let nib = loadNib(nibName)
        nib.frame = bounds
        nib.translatesAutoresizingMaskIntoConstraints = false
        addSubview(nib)
        nibView = nib
        setUpConstraints()
    }
    
    init(nibName: String) {
        super.init(frame: CGRectZero)
        let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
        let nib = loadNib(nibName)
        nib.frame = bounds
        nib.translatesAutoresizingMaskIntoConstraints = false
        addSubview(nib)
        nibView = nib
        setUpConstraints()
    }
    
    func setUpConstraints() {
        ["V","H"].forEach { (quote) -> () in
            let format = String(format:"\(quote):|[nibView]|")
            addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(format, options: [], metrics: nil, views: ["nibView" : nibView]))
        }
    }
    
    func loadNib(name: String) -> UIView {
        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: name, bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
    
        return view
    }
    

    }

  5. В нашем пользовательском классе мы также можем определить некоторые неповторимые свойства, чтобы иметь полный контроль над ними из конструктора интерфейса

    @IBDesignable class MemeView: XibbedView {

    @IBInspectable var memeImage: UIImage = UIImage() {
        didSet {
            imageView.image = memeImage
        }
    }
    @IBInspectable var textColor: UIColor = UIColor.whiteColor() {
        didSet {
            label.textColor = textColor
        }
    }
    @IBInspectable var text: String = "" {
        didSet {
            label.text = text
        }
    }
    @IBInspectable var roundedCorners: Bool = false {
        didSet {
            if roundedCorners {
                layer.cornerRadius = 20.0
                clipsToBounds = true
            }
            else {
                layer.cornerRadius = 0.0
                clipsToBounds = false
            }
        }
    }
    
    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var imageView: UIImageView!
    

}

Несколько примеров:

Если нам нужно добавить больше информации, представление пока отображается внутри раскадровки или другого XIB, для этого мы можем реализовать prepareForInterfaceBuilder(), этот метод будет выполняться только при открытии файла в конструкторе интерфейсов. Если вы выполнили все, что я написал, но ничего не работает, это способ отладить представление sigle, добавив точки останова в его реализацию.
Вот иерархия просмотров. Просмотр hiearchy

Надеюсь, что это помогает полный образец можно скачать здесь

Я нашел эту запись в блоге Аарона Хиллегаса (автор, инструктор, ниндзя Какао) очень поучительной. Даже если вы не примете его модифицированный подход к загрузке файлов NIB через назначенный инициализатор, вы, по крайней мере, лучше поймете происходящий процесс. Я использую этот метод в последнее время с большим успехом!

Вот способ сделать это в Swift (в настоящее время пишу Swift 2.0 в XCode 7 beta 5).

От твоего UIView Подкласс, который вы задали как "Пользовательский класс" в Интерфейсном Разработчике, создает метод, подобный этому (мой подкласс называется RecordingFooterView):

class func loadFromNib() -> RecordingFooterView? {
    let nib = UINib(nibName: "RecordingFooterView", bundle: nil)
    let nibObjects = nib.instantiateWithOwner(nil, options: nil)
    if nibObjects.count > 0 {
        let topObject = nibObjects[0]
        return topObject as? RecordingFooterView
    }
    return nil
}

Тогда вы можете просто назвать это так:

let recordingFooterView = RecordingFooterView.loadFromNib()

Предыдущий ответ не учитывает изменения в структуре NIB (XIB), которые произошли между 2.0 и 2.1 из iPhone SDK. Пользовательское содержимое теперь начинается с индекса 0 вместо 1.

Вы можете использовать макрос 2.1, который действителен для всех версий 2.1 и выше (это два подчеркивания перед IPHONE:

 // Cited from previous example
 NSArray* nibViews =  [[NSBundle mainBundle] loadNibNamed:@"QPickOneView" owner:self options:nil];
 int startIndex;
 #ifdef __IPHONE_2_1
 startIndex = 0;
 #else
 startIndex = 1;
 #endif
 QPickOneView* myView = [ nibViews objectAtIndex: startIndex];
 myView.question = question;

Мы используем метод, подобный этому, для большинства наших приложений.

гулянка

У меня была причина сделать то же самое (программная загрузка представления из файла XIB), но мне нужно было сделать это полностью из контекста подкласса подкласса UIView (т.е. без участия контроллера вида). Для этого я создал этот служебный метод:

+ (id) initWithNibName:(NSString *)nibName withSelf:(id)myself {

    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:nibName
                                                    owner:myself options:nil];
    for (id object in bundle) {
        if ([object isKindOfClass:[myself class]]) {
            return object;
        }
    }  

    return nil;
} 

Тогда я называю это из моего подкласса initWithFrame метод вроде так:

- (id)initWithFrame:(CGRect)frame {

    self = [Utilities initWithNibName:@"XIB1" withSelf:self];
    if (self) {
        // Initialization code.
    }
    return self;
}

Опубликовано для общего интереса; если кто-то видит какие-либо проблемы без этого, пожалуйста, дайте мне знать.

Ни один из ответов не объясняет, как создать автономный XIB, который является корнем этого вопроса. Здесь нет Xcode 4 опция "Создать новый файл XIB".

Сделать это

1) Choose "New File..."
2) Choose the "User Interface" category under the iOS section
3) Choose the "View" item
4) You will then be prompted to choose an iPhone or iPad format

Это может показаться простым, но это может сэкономить вам несколько минут, ища это, так как слово "XIB" нигде не появляется.

@AVeryDev

6) Чтобы прикрепить загруженный вид к виду вашего контроллера:

[self.view addSubview:myViewFromNib];

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

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

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

Потратив много часов, я придумал следующее решение. Выполните следующие шаги для создания пользовательских UIView,

1) Создать класс myCustomView, унаследованный от UIView.

2) Создайте .xib с именем myCustomView.

3) Измените класс UIView внутри вашего.xib файла, назначьте там класс myCustomView.

4) Создание IBOutlets

5) Загрузите.xib в myCustomView * customView. Используйте следующий пример кода.

myCustomView * myView = [[[NSBundle mainBundle] loadNibNamed:@"myCustomView" owner:self options:nil] objectAtIndex:0];
[myView.description setText:@"description"];

Примечание: для тех, кто все еще сталкивается с проблемой, можете прокомментировать, я предоставлю им ссылку примера проекта с myCustomView

Кратчайшая версия:

RSFavoritePlaceholderView *favoritePlaceholderView = [[[NSBundle mainBundle] loadNibNamed:@"RSFavoritePlaceholderView" owner:self options:nil] firstObject];

У меня есть соглашение о присвоении имен Xibs с представлениями в них так же, как представление. То же самое, что можно сделать для контроллера представления. Затем мне не нужно записывать имена классов в коде. Я загружаю UIView из файла пера с тем же именем.

Пример для класса под названием MyView.

  • Создайте файл пера MyView.xib в Интерфейсном Разработчике
  • В Интерфейсном Разработчике добавьте UIView. Установите его класс в MyView. Настройте по своему вкусу, подключите переменные экземпляра MyView к подпредставлениям, к которым вы, возможно, захотите обратиться позже.
  • В своем коде создайте новый MyView следующим образом:

    MyView * myView = [MyView nib_viewFromNibWithOwner: owner];

Вот категория для этого:

@implementation UIView (nib)

+ (id) nib_viewFromNib {
    return [self nib_viewFromNibWithOwner:nil];
}

+ (id) nib_viewFromNibWithOwner:(id)owner {
    NSString *className = NSStringFromClass([self class]);
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:className owner:owner options:nil];
    UIView *view = nil;
    for(UIView *v in nib) {
        if ([v isKindOfClass:[self class]]) {
            view = v;
            break;
        }
    }
    assert(view != nil && "View for class not found in nib file");
    [view nib_viewDidLoad];
    return view;
}

// override this to do custom setup
-(void)nib_viewDidLoad {

}

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

Чтобы программно загрузить представление из nib/xib в Swift 4:

// Load a view from a Nib given a placeholder view subclass
//      Placeholder is an instance of the view to load.  Placeholder is discarded.
//      If no name is provided, the Nib name is the same as the subclass type name
//
public func loadViewFromNib<T>(placeholder placeholderView: T, name givenNibName: String? = nil) -> T {

    let nib = loadNib(givenNibName, placeholder: placeholderView)
    return instantiateView(fromNib: nib, placeholder: placeholderView)
}

// Step 1: Returns a Nib
//
public func loadNib<T>(_ givenNibName: String? = nil, placeholder placeholderView: T) -> UINib {
    //1. Load and unarchive nib file
    let nibName = givenNibName ?? String(describing: type(of: placeholderView))

    let nib = UINib(nibName: nibName, bundle: Bundle.main)
    return nib
}

// Step 2: Instantiate a view given a nib
//
public func instantiateView<T>(fromNib nib: UINib, placeholder placeholderView: T) -> T {
    //1. Get top level objects
    let topLevelObjects = nib.instantiate(withOwner: placeholderView, options: nil)

    //2. Have at least one top level object
    guard let firstObject = topLevelObjects.first else {
        fatalError("\(#function): no top level objects in nib")
    }

    //3. Return instantiated view, placeholderView is not used
    let instantiatedView = firstObject as! T
    return instantiatedView
}

Я закончил тем, что добавил категорию в UIView для этого:

 #import "UIViewNibLoading.h"

 @implementation UIView (UIViewNibLoading)

 + (id) loadNibNamed:(NSString *) nibName {
    return [UIView loadNibNamed:nibName fromBundle:[NSBundle mainBundle] retainingObjectWithTag:1];
 }

 + (id) loadNibNamed:(NSString *) nibName fromBundle:(NSBundle *) bundle retainingObjectWithTag:(NSUInteger) tag {
    NSArray * nib = [bundle loadNibNamed:nibName owner:nil options:nil];
    if(!nib) return nil;
    UIView * target = nil;
    for(UIView * view in nib) {
        if(view.tag == tag) {
            target = [view retain];
            break;
        }
    }
    if(target && [target respondsToSelector:@selector(viewDidLoad)]) {
        [target performSelector:@selector(viewDidLoad)];
    }
    return [target autorelease];
 }

 @end

объяснение здесь: viewcontroller меньше загружает представление в ios&mac

Другие вопросы по тегам