Как работает 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:
должен быть вызван для вставки нового объекта в иерархию представления окна.