UIView и initWithFrame и файл NIB. Как я могу загрузить файл NIB?

У меня есть UIView, который называется baseView, и в нем есть initWithFrame, где я добавляю несколько других представлений и делаю некоторые нестандартные вещи. В том же представлении есть файл NIB.

Теперь у меня есть UIViewController класс с именем AppController, в котором я хочу добавить представление baseView к представлению AppController, поэтому я делаю следующее:

self.view = baseView; но проблема в том, что файл NIB не загружается. Как мне убедиться, что настроенный материал И файл NIB загружены / запущены?


person dbrasco    schedule 20.02.2011    source источник


Ответы (5)


У вас есть много вариантов, в зависимости от того, как ваш класс «baseView» предназначен для использования и интеграции в ваше приложение. Непонятно, как вы собираетесь использовать этот класс - в качестве представления в подклассе UIViewController или в качестве многоразового модульного компонента, который должен быть создан несколько раз в вашем приложении для использования во многих различных контроллерах представления.

Если ваше представление должно быть единственным представлением в подклассе UIViewController, тогда Phonitive верен - свяжите его вместе с файлом .xib подкласса UIViewController и используйте viewDidLoad UIViewController для окончательной инициализации.

Но если вы хотите, чтобы ваш класс View был подкомпонентом, многократно использовавшимся в разных контроллерах представления, интегрированным либо посредством кода, либо путем включения в файл .xib для другого контроллера, тогда вам необходимо реализовать как метод initWithFrame: init, так и awakeFromNib, для обработки обоих случаев. Если ваша внутренняя инициализация всегда включает некоторые объекты из .xib, тогда в вашем initWithFrame вам нужно будет загрузить ваш .xib вручную, чтобы поддерживать классы «заказчиков», которые хотят создать ваш виджет с помощью кода. Точно так же, если файл .xib содержит ваш объект, вам нужно убедиться, что вы вызываете любую требуемую финализацию кода из awakeFromNib.

Вот пример того, как создать компонент подкласса UIView с дизайном пользовательского интерфейса в пике.

MyView.h:

@interface MyView : UIView
{
    UIView *view;
    UILabel *l;
}
@property (nonatomic, retain) IBOutlet UIView *view;
@property (nonatomic, retain) IBOutlet UILabel *l;

MyView.m:

#import "MyView.h"
@implementation MyView
@synthesize l, view;

- (id)initWithFrame:(CGRect)frame 
{
    self = [super initWithFrame:frame];
    if (self) 
    {
        // Initialization code.
        //
        [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil];
        [self addSubview:self.view];
    }
    return self;
}

- (void) awakeFromNib
{
    [super awakeFromNib];

    // commenters report the next line causes infinite recursion, so removing it
    // [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil];
    [self addSubview:self.view];
}

- (void) dealloc
{
     [l release];
     [view release];
     [super dealloc];
}

Вот как выглядит файл пера (за исключением того, что владельца файла нужно изменить на класс MyView).

введите описание изображения здесь

обязательно подключите выходы просмотра и меток к владельцу файла. Вот и все! Шаблон для создания повторно используемых виджетов UIView.

Самое интересное в этой структуре заключается в том, что вы можете размещать экземпляры своего объекта MyView в других файлах пера, просто поместите UIView в нужное место / размер, а затем измените класс в инспекторе идентичности (CMD-4) на MyView, и бум, у вас есть экземпляр вашего виджета в любых представлениях, которые вы хотите! Как и в случае с объектами UIKit, вы можете реализовать протоколы делегирования, чтобы объекты, использующие ваш виджет, могли получать уведомления об интересных событиях и предоставлять данные для отображения в виджете для его настройки.

person Bogatyr    schedule 20.02.2011
comment
Обычно, когда я создаю приложение, мне нравится создавать некоторые представления, и я хочу использовать эти представления в контроллере представления. Я не хочу создавать контроллер представления для каждого представления, поэтому я создаю отдельные представления, которые хочу включить в представление контроллера представления. - person dbrasco; 22.02.2011
comment
Мне нравится то, что вы говорите, что я могу реализовать initWithFrame и awakeFromNib, но я не уверен, какую инициализацию мне нужно выполнить и в каком порядке. Не могли бы вы привести пример? Допустим, я добавил несколько меток и кнопок в XIB с IB. В моем представлении есть несколько IBOutlets. Теперь я хочу, чтобы в initWithFrame что-то делали с этим объектом. Как мне инициировать просмотр с помощью NIB, чтобы он знал об этих объектах? - person dbrasco; 22.02.2011
comment
Если вы загружаете представление только из пера, вам никогда не понадобится initWithFrame. Единственная цель initWithFrame - создать объект в коде и присвоить ему начальный фрейм (расположение и размер в супервизоре). Вы помещаете любую дополнительную инициализацию, которая нужна вашему объекту, в awakeFromNib (или в свою процедуру инициализации, если вы создаете из кода). Я добавлю небольшой пример в свой ответ - person Bogatyr; 22.02.2011
comment
Этот. Является. Красивый. Спасибо! - person Joe D'Andrea; 09.03.2011
comment
Добро пожаловать! Рад помочь. Как только я получил это прозрение, это действительно повлияло на то, как я структурирую приложения для iOS. - person Bogatyr; 09.03.2011
comment
Верно. Конечно, есть несколько случаев, когда я могу просто отправить другое представление VC + на навигационный контроллер, но это не всегда так. Теперь мне нужно перечитать IBPlugins, например: cocoawithlove.com/2009/07/ (подождите, нет, не для iOS, но все же ...) - person Joe D'Andrea; 09.03.2011
comment
Да, я видел эту статью - у меня еще не было необходимости нырять так глубоко, я уверен, что когда-нибудь ... - person Bogatyr; 09.03.2011
comment
Это хорошее решение проблемы. Я обнаружил, что передача следующих свойств представлению в init / awake также была полезной: self.view.frame = self.frame; self.view.autoresizingMask = self.autoresizingMask; - person Willster; 09.03.2012
comment
loadNibNamed внутри awakeFromNib вызывает бесконечную рекурсию - person Gargo; 31.10.2012
comment
@Gargo: Не заметил этой проблемы, проверю, когда в следующий раз буду работать над iOS. - person Bogatyr; 31.10.2012
comment
Это правда, @Gargo верен, я просто тестирую этот код (отличный ответ), но я входил в бесконечный цикл при инициализации, после удаления loadNibNamed внутри awaleFromNib эта проблема прекратилась. Спасибо. - person Ospho; 24.11.2012
comment
@Oliver, спасибо за подтверждение, сейчас я не могу это проверить. Я обновлю ответ, чтобы удалить эту строку. Я полагаю, это имеет смысл, поскольку вы уже загружаете перо, вам не нужно снова загружать перо. По общему признанию, я не часто использую этот путь, поэтому спасибо за исправление! - person Bogatyr; 25.11.2012
comment
Как мне отправить NSArray с моим вызовом init. Должен ли я сделать свой собственный метод инициализации? - person Morten Gustafsson; 13.03.2013
comment
если вы это сделаете, вы создали два MyView. один из initWithFrame: один из Nib, один из Nib добавлен к одному из initWithFrame: - person aelam; 19.12.2013
comment
@aelam вы прочитали и поняли проблему, которую это решает? Множественные представления необходимы (насколько я могу судить) для преимуществ этого подхода. Во что бы то ни стало, не стесняйтесь размещать собственное полное решение в OP. - person Bogatyr; 27.01.2014
comment
В моем случае [[NSBundle mainBundle] loadNibNamed: @MyView owner: self options: nil]; необходимо, если я хочу, чтобы все это работало. Без него self.view присваивается nil. РЕДАКТИРОВАТЬ: этот комментарий все еще действителен, но вы должны прочитать мою редакцию ответа; ВАЖНО: ... абзац. - person Lukas Kalinski; 03.04.2014
comment
Я не мог жить нормальной жизнью, зная, что есть два экземпляра класса представления. Я закончил тем, что избавился от XIB и сделал это по-старому, с прямым кодом! - person pnizzle; 26.06.2014
comment
Установлены ли свойства IBOutlet на сохранение, потому что представление является владельцем самого себя? - person Oren; 02.06.2015
comment
UIViews не имеет свойства view, поэтому строка [self addSubview:self.view]; не работает. Может, должно было быть что-то вроде этого? NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"SubscriptionPrompt" owner:self options:nil]; [self addSubview:subviewArray[0]]; Может быть, раньше на UIViews было свойство просмотра. - person ftvs; 09.03.2016
comment
@ftvs снова проверьте файл заголовка. - person Bogatyr; 09.03.2016
comment
Хм. Это означает, что MyView является владельцем файла и также ссылается на представление через IBOutlet. [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil]; затем устанавливает свойство view, которое затем добавляется в подпредставление. MyView (созданный с помощью initWithFrame) будет иметь подвид MyView (созданный с помощью loadNibNamed и загруженный в свойство представления). - person ftvs; 10.03.2016

Я нашел этот пост после того, как у меня возникла проблема с попыткой сделать это в моем приложении. Я пытался создать экземпляр представления из NIB в методе ViewDidLoad, но элементы управления работали так, как будто они были отключены. Я боролся с этим, пытаясь напрямую установить свойство userInteractionEnabled и программно установить селектор событий касания для кнопки в этом представлении. Ничего не получилось. Я наткнулся на другой пост и обнаружил, что viewDidLoad, вероятно, слишком рано для загрузки этого NIB. Переместил нагрузку на метод ViewWillAppear и все заработало. Надеюсь, это поможет кому-то еще бороться с этим. Основной ответ был отличным и хорошо работает для меня теперь, когда он вызывается из нужного места.

person crissag    schedule 10.08.2012
comment
Пожалуйста, поделитесь с нами ссылкой на этот пост. - person ; 11.08.2012
comment
Вероятно, он имел в виду stackoverflow.com/a/819265/779419. При создании экземпляра пользовательского представления убедитесь, что действительно используете initWithFrame с правильным CGRect. - person schieferstapel; 11.03.2013

если вы хотите использовать NIB, лучше, чтобы ваш UIView был связан с UIViewController, в этом случае вы можете использовать

UIViewController *vc=[[UIViewController alloc] initWithNibName:@"YourNIBWihtOUTEXTENSION" bundle:nil]

[self.view addSubView:vc.view ];

из-за утечек памяти необходимо выпустить vc

person Phonitive    schedule 20.02.2011
comment
Не правда; Если вам нужно изменить содержимое UIView с помощью Interface Builder, вам нужно будет использовать метод Богатыря. Причина, по которой вам нужно будет использовать UIView вместе с Nib, заключается в том, что Apple не рекомендует использовать вложенные UIViewControllers, потому что родительские вызовы могут прерываться. То есть, если вы поддерживаете ‹iOS 5. stackoverflow.com/questions/1141015/ - person Ospho; 24.11.2012
comment
Это не лучшая реализация - я бы исследовал контейнер vcs - person Adam Waite; 15.08.2013

Если у вас есть собственный UIView с файлом xib.

- (id)initWithFrame:(CGRect)frame
{
   self = [super initWithFrame:frame];

   id mainView;
   if (self)
   {
       NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"HomeAllAdsView" owner:self options:nil];
       mainView = [subviewArray objectAtIndex:0];    
   }
   return mainView;
}

- (void) awakeFromNib
{
   [super awakeFromNib];
}
person felixwcf    schedule 03.11.2015

Этот пост помог мне Создание многоразовых представлений с помощью Interface Builder и Auto Layout. Уловка заключалась в установке IBOutlets в FileOwner, а затем добавлении представления содержимого самому себе после загрузки пера.

person Maria    schedule 02.03.2017