Как работает loadNibNamed? Выходы UIView не инициализируются с помощью loadNibNamed

Я знаю, что это довольно просто, но после слишком много потянув за волосы, я не близок к решению.

Я видел уроки, объясняющие, как создать представление, используя XIB и все. Но никто из них не решает ситуацию, которая у меня здесь есть.

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

Проблема в том, что я не знаю, как полностью инициализировать весь пользовательский интерфейс.

Вот мой код для подкласса UIView:

@interface MCPTGenericView : UIView

+(id)createInstance : (bool) bPortrait;

@property (weak, nonatomic) IBOutlet UIView *topView;
@property (weak, nonatomic) IBOutlet UIView *titleView;
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@property (weak, nonatomic) IBOutlet UIButton *logoButton;
@property (weak, nonatomic) IBOutlet UITextField *searchTextField;
@property (weak, nonatomic) IBOutlet UIButton *menuButton;
@end

Позже я также планирую использовать этот же файл XIB для альбомной ориентации этого UIView, и я планирую использовать те же вышеприведенные выходы с альбомно-ориентированными элементами управления в том же XIB.

И вот реализация:

@implementation MCPTGenericView
//@synthesize topView, titleLabel, titleView;

+(id)createInstance : (bool) bPortrait
{
    UIView * topLevelView = nil;
    MCPTGenericView * instance = [MCPTGenericView new];
    NSArray * views = [[NSBundle mainBundle] loadNibNamed:@"MoceptGenericView" owner:instance options:nil];

    int baseTag = (bPortrait)?PORTRAIT_VIEW_TAG_OFFSET:LANDSCAPE_VIEW_TAG_OFFSET;

    // make sure customView is not nil or the wrong class!

   for (UIView * view in views)
   {

       if (view.tag == baseTag)
       {
           topLevelView = view;
           break;
       }
   }

    instance.topView = (MCPTGenericView *)[topLevelView viewWithTag:baseTag + 1];
    instance.searchTextField = (UITextField *)[topLevelView viewWithTag:baseTag + 2];
    instance.menuButton = (UIButton *)[topLevelView viewWithTag:baseTag + 3];
    instance.logoButton = (UIButton *)[topLevelView viewWithTag:baseTag + 4];
    instance.titleView = [topLevelView viewWithTag:baseTag + 5];
    instance.titleLabel = (UILabel *)[topLevelView viewWithTag:baseTag + 6];

    return instance;
}

-(id)initWithCoder:(NSCoder *)aDecoder
{
    if ((self = [super initWithCoder:aDecoder]))
    {
        [self addSubview:[[[NSBundle mainBundle] loadNibNamed:@"MCPTGenericView" owner:self options:nil] objectAtIndex:0]];
    }
    return self;
}

-(void)awakeFromNib
{
    [super awakeFromNib];
    [self addSubview: self.titleView];
    [self addSubview:self.topView];
}

- (id) init
{
    self = [super init];
    if (self)
    {
        [[NSBundle mainBundle] loadNibNamed:@"MCPTGenericView" owner:self options:nil];
        [self addSubview:self.topView];
        [self addSubview:self.titleView];

    }
    return self;
}

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

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
    // Drawing code
}
*/

@end

Что-то, что сработало:

Мне удалось позвонить initWithFrame:frame от моего viewcontroller. Таким образом, я мог видеть, что все элементы управления правильно инициализированы. Но тогда зачем мне указывать фрейм, если я уже нарисовал XIB? Разве loadNibNamed не должен обрабатывать настройки фреймов и макетов, так как это предполагаемое использование XIB?

Я также сбит с толку тем, как loadNibNamed нуждается в объекте владельца. Почему нам уже нужен объект, чтобы получить тот же объект из XIB? Это тоже недоделанный?

Пожалуйста помоги...

2 ответа

Решение

Что меня озадачило, так это путь loadnibnamed теряет макет XIB и информацию о выходе. Я наконец нашел способ достичь этого.

Вот резюме того, что работает:

1) Предположим, MyCustomView - это ваш пользовательский класс представлений - вы разрабатываете его и его подпредставления как часть XIB. Вы делаете это через конструктор интерфейса, так что не требует пояснений.

2) Добавьте MyCustomView.h и MyCustomView.m (шаблон) через Xcode -> File -> New -> Objective C Class.

3) Затем, в MyCustomView.xib, установите File's Owner = MyCustomView (имя класса только что добавлено). Не трогайте верхний пользовательский класс большинства View - оставьте его как UIView. Иначе это закончится рекурсией!!!

4) В MyCustomView.h создайте несколько выходов, соответствующих подпредставлениям в MyCustomView.xib.

Такие как:

@property (weak)  IBOutlet UILabel * label1;
@property (weak)  IBOutlet UIButton * button1;

5) Зайдите в MyCustomView.xib. Выберите каждое подпредставление (метка, кнопка), щелкните правой кнопкой мыши, перетащите из "New Referencing Outlet" и перетащите его вверх к владельцу файла.

Откроется список торговых точек, соответствующих типу подпредставления, из которого вы перетащили. Если вы перетянули с метки, на ней появится всплывающая метка1 и так далее. Это показывает, что все, что вы сделали до этого шага, правильно.

Если вы, с другой стороны, облажались на любом этапе, всплывающее окно не появится. Проверьте шаги, особенно 3 и 4.

Если вы не выполните этот шаг правильно, Xcode приветствует вас следующим исключением:

setValue:forUndefinedKey: this class is not key value coding-compliant for the key

6) В вашем MyCustomView.m вставьте / перезапишите следующий код:

-(id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];

    if (self)
    {
        NSString * nibName = @"MyCustomView";
        [[[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil] firstObject];

        [self addSubview:self.labelContinentName];
    }
    return self;
}

Этот шаг очень важен - он устанавливает значения ваших выходов (label1, button1) от nil до ощутимых подпредставлений и, самое главное, устанавливает их фрейм в соответствии с тем, что вы установили в MyCustomView.xib.

7) В своем файле раскадровки добавьте представление типа MyCustomView - как и любое другое представление:

  • Перетащите UIView в прямоугольник основного вида вашего контроллера.
  • Выберите недавно добавленный вид
  • В Utilities -> Identity Inspector установите значение пользовательского класса = MyCustomView.

Это должно быть и работает без проблем!

loadNibNamed не обрабатывает настройку фрейма, он только загружает контент и делает объект доступным для вашего кода. initWithFrame: должен быть вызван для вставки нового объекта в иерархию представления окна.

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