Подclass UIView со своим XIB

Я создал пользовательский подclass UIView и предпочел бы не размещать UI в коде в подclassе UIView. Я хотел бы использовать xib для этого. Итак, я сделал следующее.

Я создал class «ShareView», который подclassифицирует UIView. Я создал файл XIB с владельцем файла, установленным в «ShareView». Затем я связываю некоторые выходы, объявленные в моем «ShareView.h».

Затем у меня есть ViewController, MainViewController, который добавляет ShareView в качестве подзапроса. с этим кодом:

NSArray *arr = [[NSBundle mainBundle] loadNibNamed:@"ShareView" owner:nil options:nil]; UIView *fv = [[arr objectAtIndex:0] retain]; fv.frame = CGRectMake(0, 0, 320, 407); [self.view addSubview:fv]; 

Но теперь я получаю ошибки NSUnknownKeyException в точках, объявленных в моем ShareView.

Причина, по которой я все это сделал, – это то, что я хочу UIView, с собственной логикой в ​​отдельном XIB-файле. Я читал в нескольких местах, что ViewControllers используются только для управления полным экраном, то есть не для частей экрана … Так что я делаю неправильно? Я хочу, чтобы моя логика для ShareView в отдельном classе, поэтому мой class MainController не раздувается с помощью логики из ShareView (который, я думаю, является типом решения этой проблемы?)

ThomasM,

У нас были аналогичные идеи об инкапсулировании поведения внутри пользовательского представления (скажем, слайдер со значками компаньона для значений min / max / current, причем события с изменением значения также обрабатывались внутренним элементом управления).

В нашей современной практике мы разработали ShareView в Interface Builder ( ShareView.xib ), как описано Эймантасом в его ответе. Затем мы вставляем ShareView в иерархию представлений в MainViewController.xib .

Я написал, как мы вставляем пользовательские виджеты внутри других Nibs в наш блог разработчиков iOS. -awakeAfterUsingCoder: переопределяет -awakeAfterUsingCoder: в вашем пользовательском представлении заменяет объект, загруженный из MainViewController.xib, с загруженного из «встроенного» ниба (ShareView.xib).

Что-то в этом роде:

 // ShareView.m - (id) awakeAfterUsingCoder:(NSCoder*)aDecoder { BOOL theThingThatGotLoadedWasJustAPlaceholder = ([[self subviews] count] == 0); if (theThingThatGotLoadedWasJustAPlaceholder) { // load the embedded view from its Nib ShareView* theRealThing = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([ShareView class]) owner:nil options:nil] objectAtIndex:0]; // pass properties through theRealThing.frame = self.frame; theRealThing.autoresizingMask = self.autoresizingMask; [self release]; self = [theRealThing retain]; } return self; } 

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

Вы должны определить загрузчик xib как владельца (т. Е. Диспетчер представлений, ответственный за загрузку xib). Затем добавьте отдельный объект UIView в xib и определите его как экземпляр ShareView . Затем при загрузке xib.

 ShareView *shareView = [[[[NSBundle mainBundle] loadNibNamed:@"ShareView" owner:self options:nil] objectAtIndex:0] retain]; 

Вы также можете определить shareView как интерфейс IBOutlet в интерфейсе controllerа (и подключить выход от владельца файла к этому представлению в самом xib). Затем, когда вы загружаете xib, не будет необходимости переназначать shareView экземпляра shareView так как процесс загрузки xib будет напрямую подключать представление к переменной экземпляра.

Я хотел бы добавить к ответу. Я надеюсь, что люди улучшат этот ответ.

В первую очередь это работает.

XIB:

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

Результат:

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

Я хотел бы подclassифицировать UIView в течение длительного времени, особенно для tableViewCell.

Вот как я это сделал.

Это успешно, но, по моему мнению, какая-то часть по-прежнему «неудобна».

Сначала я создал обычный файл .h, .m и xib. Обратите внимание, что у Apple нет флажка для автоматического создания xib, если созданный вами подclass не является подclassом UIViewController. Ну, все равно создавайте их.

 #import  #import "Business.h" @interface BGUIBusinessCellForDisplay : UITableViewCell + (NSString *) reuseIdentifier; - (BGUIBusinessCellForDisplay *) initWithBiz: (Business *) biz; @end 

Действительно простой UITableViewCell, который я хочу инициализировать последним с помощью biz.

Я использую reuseidentifier, который вы должны сделать для UITableViewCell

 //#import "Business.h" @interface BGUIBusinessCellForDisplay () @property (weak, nonatomic) IBOutlet UILabel *Title; @property (weak, nonatomic) IBOutlet UIImageView *Image; @property (weak, nonatomic) IBOutlet UILabel *Address; @property (weak, nonatomic) IBOutlet UILabel *DistanceLabel; @property (weak, nonatomic) IBOutlet UILabel *PinNumber; @property (strong, nonatomic) IBOutlet BGUIBusinessCellForDisplay *view; @property (weak, nonatomic) IBOutlet UIImageView *ArrowDirection; @property (weak, nonatomic) Business * biz; @end @implementation BGUIBusinessCellForDisplay - (NSString *) reuseIdentifier { return [[self class] reuseIdentifier]; }; + (NSString *) reuseIdentifier { return NSStringFromClass([self class]); }; 

Затем я исключил большинство кодов инициализации и поставил это вместо этого:

 - (BGUIBusinessCellForDisplay *) initWithBiz: (Business *) biz { if (self.biz == nil) //First time set up { self = [super init]; //If use dequeueReusableCellWithIdentifier then I shouldn't change the address self points to right NSString * className = NSStringFromClass([self class]); //PO (className); [[NSBundle mainBundle] loadNibNamed:className owner:self options:nil]; [self addSubview:self.view]; //What is this for? self.view is of type BGCRBusinessForDisplay2. That view should be self, not one of it's subview Things don't work without it though } if (biz==nil) { return self; } self.biz = biz; self.Title.text = biz.Title; //Let's set this one thing first self.Address.text=biz.ShortenedAddress; //if([self.distance isNotEmpty]){ self.DistanceLabel.text=[NSString stringWithFormat:@"%dm",[biz.Distance intValue]]; self.PinNumber.text =biz.StringPinLineAndNumber; 

Заметьте, что это действительно неудобно.

Прежде всего, init можно использовать двумя способами.

  1. Его можно использовать сразу после aloc
  2. Его можно использовать, имея еще один существующий class, а затем мы просто хотим, чтобы эта существующая ячейка была создана для другого бизнеса.

Так я и сделал:

 if (self.biz == nil) //First time set up { self = [super init]; //If use dequeueReusableCellWithIdentifier then I shouldn't change the address self points to right NSString * className = NSStringFromClass([self class]); //PO (className); [[NSBundle mainBundle] loadNibNamed:className owner:self options:nil]; [self addSubview:self.view]; //What is this for? self.view is of type BGCRBusinessForDisplay2. That view should be self, not one of it's subview Things don't work without it though } 

Другие неприятные вещи, которые я сделал, – это когда я делаю [self addSubview: self.view];

Дело в том, что я хочу, чтобы я был точкой зрения. Не self.view. Так или иначе, это работает. Поэтому да, пожалуйста, помогите мне улучшить, но это, по сути, способ реализовать свой собственный подclass UIView.

Вы можете создать свой собственный UIView, созданный в xib, и даже заставить Interface Builder отображать его внутри других xib-файлов или раскадровки в новом Xcode 6 с использованием IB_DESIGNABLE. В xib установите владельца файла в свой собственный class, но не устанавливайте class UIView, чтобы избежать проблем с загрузкой ревальвации. Просто оставьте class UIView по умолчанию, и вы добавите этот UIView в качестве поднабора вашего пользовательского представления classа. Подключите все свои торговые точки к владельцу файла и в своем пользовательском classе загрузите xib, как в приведенном ниже коде. Здесь вы можете ознакомиться с моим видеоуроком: https://www.youtube.com/watch?v=L97MdpaF3Xg

 IB_DESIGNABLE @interface CustomControl : UIView @end @implementation CustomControl - (instancetype)initWithCoder:(NSCoder *)aDecoder { if (self = [super initWithCoder:aDecoder]) { [self load]; } return self; } - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { [self load]; } return self; } - (void)load { UIView *view = [[[NSBundle bundleForClass:[self class]] loadNibNamed:@"CustomControl" owner:self options:nil] firstObject]; [self addSubview:view]; view.frame = self.bounds; } @end 

Если вы используете автозапуск, вы можете изменить: view.frame = self.bounds; чтобы:

 [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)]]; [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)]]; 

Чтобы использовать шаблон Yang с Auto-Layout, вам нужно добавить следующее в методе -awakeWithCoder:.

  theRealThing.translatesAutoresizingMaskIntoConstraints = NO; 

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

EDIT: Автомакет все еще может быть болью. Определенные ограничения не соблюдаются, но другие (например, закрепление на дне не работает, но привязка к вершине делает). Мы не совсем уверены, почему, но вы можете обойти это, вручную передав ограничения от заполнителя на RealThing.

Также стоит отметить, что этот шаблон работает точно так же, как и с Storyboards, как и с обычными xxs (т. Е. Вы можете создать элемент UI в xix и перевести его в controller StoryBoard View, выполнив следующие шаги).

Вместо подclassа UIView, почему бы вам не подclass UIViewController. Проверьте следующую ссылку. В этом были созданы «RedView» и «BlueView» UIViewControllers с их xib и добавлены в представление MultipleViewsController, создав и экземпляр предыдущих двух classов и добавив [self.view addSubview:red.view] и [self.view addSubview:blue.view] в [self.view addSubview:blue.view] в MultipleViewsController

Несколько мониторов в одном представлении

Просто добавьте (id) отправителя к функции нажатия кнопки в RedView и BlueView в коде вышеуказанной ссылки.

Interesting Posts

Xcode / LLDB: как получить информацию об исключении, которое было просто выброшено?

В Firefox как отключить «переключиться на вкладку» в строке URL?

Необходимо восстановить данные с ноутбука, прежде чем отправлять его на ремонт

Получить список отмеченных флажков в div с помощью jQuery

Как смоделировать дисплей сетчатки (режим HiDPI) в Mac OS X 10.8 Mountain Lion на дисплее без сетчатки?

Вывод из вложенных циклов в Java

Как предотвратить несколько экземпляров активности при запуске с различными намерениями

Невозможно заставить Project Lombok работать над Eclipse (Helios)

Справочник Excel – подключение списка к базе данных

Как конвертировать WMV в MP4?

Сжатие pdf, созданное офисом 2007

Получить текст после элемента span с помощью jquery

Как я могу использовать или использовать интегрированную веб-камеру на своем ноутбуке в качестве веб-камеры на своем рабочем столе?

Android – Google Play как вкладки

Почему не удается записать файл 4Gb на флеш-накопитель с 10Gb бесплатно?

Давайте будем гением компьютера.