Подтвердить что ты не робот

IPhone: создайте повторно используемый компонент (элемент управления), в котором есть некоторые компоненты построителя интерфейса и некоторый код

Я хочу создать повторно используемый компонент (настраиваемый элемент управления) для iPhone. Он состоит из нескольких стандартных элементов управления, предварительно установленных на представлении, а затем некоторого связанного кода. Мои цели:

  • Я хочу иметь возможность использовать Interface Builder для создания подпрограмм в моем настраиваемом элементе управления;
  • Я хочу как-то скомпоновать все это, чтобы затем я мог довольно легко перетащить полученный пользовательский компонент в другие виды, без необходимости вручную переустанавливать кучу торговых точек и так далее. (Небольшая ручная перемотка в порядке, я просто не хочу делать тонны и тонны ее.)

Позвольте мне быть более конкретным и рассказать вам конкретно, что должен сделать мой контроль. В моем приложении иногда мне нужно нажать веб-службу, чтобы проверить данные, которые пользователь ввел. Ожидая ответа от веб-службы, я хочу отобразить счетчик (индикатор активности). Если веб-службы отвечают с кодом успеха, я хочу отобразить галочку "Успех". Если веб-служба отвечает кодом ошибки, я хочу отобразить значок ошибки и сообщение об ошибке.

Одноразовый способ сделать это довольно просто: я просто создаю UIView, который содержит UIActivityIndicatorView, два UIImages (один для значка успеха и один для значка ошибки) и UILabel для сообщения об ошибке. Здесь снимок экрана с соответствующими красными деталями:

alt text

Затем я подключаю фрагменты к выходам, и я помещаю код в свой контроллер.

Но как мне упаковать эти фрагменты - код и небольшую коллекцию представлений - чтобы я мог их повторно использовать? Вот несколько вещей, которые я нашел, чтобы получить меня отчасти там, но не так уж и хороши:

  • Я могу перетащить коллекцию представлений и элементов управления в раздел "Пользовательские объекты" библиотеки; то, позже, я могу перетащить их обратно на другие виды. Но (а) он забывает, какие изображения были связаны с двумя UIImages, (б) есть много ручной переустановки из четырех или пяти выходов, и (c) что самое главное, это не приводит к появлению кода. (Возможно, есть простой способ подключить код?)
  • Думаю, я могу создать IBPlugin; не уверен, что это поможет, и это похоже на большую работу, а также не совсем понятно, работает ли IBPlugins для разработки iPhone.
  • Я подумал: "Хм, есть код, связанный с этим, - который пахнет контроллером", поэтому я попытался создать пользовательский контроллер (например, WebServiceValidatorController) с соответствующим XIB файлом. Это действительно кажется многообещающим, но потом я не могу понять, как в Interface Builder перетащить этот компонент на другие виды. WebServiceValidatorController - это контроллер, а не вид, поэтому я могу перетащить его в окно документа, но не в виде.

У меня такое чувство, что я пропускаю что-то очевидное...

4b9b3361

Ответ 1

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

Я начинаю с пустого файла XIB, где добавляю один пользовательский вид на верхнем уровне. Я настраиваю этот пользовательский вид как свой класс. Ниже в иерархии представления я создаю и настраиваю subviews по мере необходимости.

Я создаю все IBOutlets в своем классе пользовательского вида и подключаю их туда. В этом упражнении я полностью игнорирую "Владелец файла".

Теперь, когда мне нужно создать представление (обычно в контроллере как часть while/for-loop, чтобы создать столько, сколько необходимо), я использую такие функции NSBundle, как это:

- (void)viewDidLoad
{
    CGRect fooBarViewFrame = CGRectMake(0, 0, self.view.bounds.size.width, FOOBARVIEW_HEIGHT);
    for (MBSomeData *data in self.dataArray) {
        FooBarView *fooBarView = [self loadFooBarViewForData:data];
        fooBarView.frame = fooBarViewFrame;
        [self.view addSubview:fooBarView];

        fooBarViewFrame = CGRectOffset(fooBarViewFrame, 0, FOOBARVIEW_HEIGHT);
    }
}

- (FooBarView*)loadFooBarViewForData:(MBSomeData*)data
{
    NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"FooBarView" owner:self options:nil];
    FooBarView *fooBarView = [topLevelObjects objectAtIndex:0];
    fooBarView.barView.amountInEuro = data.amountInEuro;
    fooBarView.barView.gradientStartColor = data.color1;
    fooBarView.barView.gradientMidColor = data.color2;
    fooBarView.titleLabel.text = data.name;
    fooBarView.titleLabel.textColor = data.nameColor;
    return fooBarView;
}

Обратите внимание, как я установил владельца nib на self - нет никакого вреда, поскольку я не подключил "Владелец файла" ни к чему. Единственным ценным результатом загрузки этого элемента является его первый элемент - мой взгляд.

Если вы хотите адаптировать этот подход для создания представлений в IB, это довольно легко. Внедрить загрузку subview в основное пользовательское представление. Установите рамки subview в основные рамки просмотра, чтобы они были одинакового размера. Основной вид станет контейнером для вашего реального пользовательского представления, а также интерфейсом для внешнего кода - вы открываете только необходимые свойства его подзонов, а остальные инкапсулируются. Затем вы просто отбрасываете пользовательский вид в IB, настраиваете его как свой класс и используете его как обычно.

Ответ 2

Вот как я решаю аналогичную проблему: я хотел:

  • создавать повторно используемые автономные классы модулей виджета, реализующие сложный вид, построенный из нескольких компонентов UIKit (и других классов вложенных модулей виджета!). Классы классов более высокого уровня этих классов виджетов не заботятся о том, что UILabel, что UIImageView внутри виджета, классы клиентов заботятся только о таких понятиях, как "отображение оценки" или "отображение логотипа команды".

  • в классе модуля виджета, выложите интерфейс для виджета и подключения к нему с помощью конструктора интерфейсов

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

Эти виджеты не являются контроллерами верхнего уровня: поэтому нет смысла подклассы UIViewController. Тогда также есть совет Apple не иметь больше одного VC на видимом экране за раз. Кроме того, существует так много советов и мнений, что "MVC! MVC!" Нужно отделить ваш взгляд и ваш контроль! MVC! " что люди так сильно не поощряются от подкласса UIView или когда-либо размещающих логику приложения в подклассе UIView.

Но я решил сделать это в любом случае (подкласс UIView). Я несколько раз занимался блоком (но довольно новым для iPhone SDK/UIKit), и я очень чувствителен к дизайну, который является уродливым, и это может вызвать проблемы, и я откровенно не вижу проблемы с подклассифицированием UIView и добавлением точек и действий. На самом деле есть много преимуществ сделать ваш многоразовый виджет подклассом UIView, а не UIViewController:

  • Вы можете поместить прямой экземпляр класса виджетов в макет компоновщика интерфейса клиента, а кадр представления UIView виджетов будет правильно инициализирован, когда класс клиента будет загружен из xib. Для меня это намного чище, чем просто "представление местазаменителя" в клиенте, затем программным образом создавая модуль виджета и устанавливая рамку виджета в рамку замещающего, а затем заменяя представление-заполнитель для представления виджетов.

  • Вы можете создать чистый и простой xib файл, чтобы выложить компоненты виджета в построителе интерфейса. Владелец файла установлен в ваш класс виджетов. Файл nib содержит корневой (ванильный) UIView, где весь GUI выложен, а затем в awakeFromNib: метод виджета, это представление nib добавляется к самому виджету как подвью. Любые настройки размера кадра можно обрабатывать здесь, полностью в коде виджета, если это необходимо. Например:

- (void) awakeFromNib
{
    [[NSBundle mainBundle] loadNibNamed:@"MyWidgetView" owner:self options:nil];
    [self addSubview:self.view];
}
  • Виджет самостоятельно инициализирует свой пользовательский интерфейс, используя метод NSBundle loadNibNamed: owner: options в своем собственном методе awakeFromNib, поэтому интеграция виджета в классы клиента чиста: просто отпустите UIView в представлении клиента в IB, измените UIView в MyWidgetView, а в клиенте init или viewDidLoad настроены все значения по умолчанию на дисплее виджетов, используя API-интерфейс виджета, который вы пишете самостоятельно (setScore: setTeamImage: etc.)

Мне кажется, что нет никакого различия в полученном коде между подклассификацией UIView таким образом, чтобы сделать многоразовый "виджет" и использовать UIViewController. В обоих случаях файлы .h содержат элементы класса виджетов, выходы, объявления действий и специальные декларации API. В обоих случаях файл .m содержит реализацию действий и специальную реализацию API. И вид IS отделен от элемента управления - представление находится в файле xib!

Итак, в общем, это то, что я делаю для упаковки сложных виджетов виджетов:

  • Сделать класс виджетов подклассом UIView
  • Создайте виджет, где бы вы ни находились, в других представлениях вашего приложения в IB напрямую, перетащив UIView из библиотеки инструментов и изменив класс на свой "MyWidgetClass".
  • Создайте свой интерфейс виджета в IB в банке в корневом ваниле UIView.
  • в классе виджетов awakeFromNib: метод, загрузите интерфейс виджета из xib и выполните [self addSubview:self.theXibRootView]

  • Кодируйте виджет так же, как и UIViewController: выходы, действия, специальные API

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

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