Пользовательский UIView, настроенный с InterfaceBuilder - PullRequest
5 голосов
/ 11 апреля 2011

Много людей задавали похожий вопрос, но ответы были либо достаточно расплывчаты, либо не в том направлении, в котором мне нужно было идти, поэтому я попытаюсь задать это как можно более конкретно.Контекст этого вопроса относится к разработке под iOS с использованием XCode 4.

У меня есть приложение, в котором я много раз использую один и тот же виджет.В этом простом упражнении виджет будет представлять собой красный прямоугольник фиксированного размера с меткой вверху, который изменит корневой контроллер, но вы можете представить, что он имеет много ImageViews и ScrollView и выглядит довольно элегантно.Этот последний пункт является ключевым, потому что художники-некодирующие дизайнеры хотят иметь возможность настраивать этот вид глобально, не делая одно и то же снова и снова на каждом дублированном фрагменте пользовательского интерфейса.

В приложении я заменил ImageViews и ScrollViews, которые использовали для создания виджета, на один UIView, а с помощью InterfaceBuilder установил для Custom Class значение Widget.У меня есть розетки в контроллере корневого представления для этих объектов Widget, правильно подключенных.Класс виджетов является подклассом UIView, и код выглядит следующим образом:

@interface Widget : UIView {
    UILabel     *label;
}

@property (nonatomic, retain) IBOutlet  UILabel *label;
@property (nonatomic, copy) NSString *labelName;
@end


@implementation Widget
@synthesize label, labelName;

- (id)initWithCoder:(NSCoder *)aDecoder {
    if ((self = [super initWithCoder:aDecoder])) { 
        // This is the code that I want to disappear    
        CGRect pos = CGRectMake(5, 5, 90, 30);
        label = [[UILabel alloc] initWithFrame:pos];
        [self addSubview:label];
        self.backgroundColor = [UIColor redColor];
    }
    return self;
}

- (NSString *)labelName {
    return label.text;
}

// This live updates the label in this custom view
- (void)setLabelName:(NSString *)name {
    label.text = name;
}

- (void)dealloc {
    [super dealloc];
}
@end

Код прекрасно работает, и я могу в режиме реального времени обновлять текст в пользовательских представлениях из контроллера основного представления.Однако проблема состоит в четырех строках кода в initWithCoder.Помните дизайнеров?У меня есть они в точке, где они могут творить свою магию с Интерфейсным Разработчиком, но я не могу понять, как заставить UIView загружать себя из разработанного ими пера.Ручная попытка и репликация расположения и свойств любого отдельного элемента не очень захватывающая и довольно трудоемкая (и да, я понимаю, что я бы уже сделал, если бы не написал эту публикацию).

Итакочень просто, как я могу взять произвольный файл .xib и заставить его выполнять работу, которую я свернул вручную в initWithCoder?Я предполагаю, что в этот момент будет тривиально подключить IBOutlet для UILabel, но, пожалуйста, прокомментируйте это, если я слишком оптимистичен.

В качестве дополнительного вопроса, как только он будет запущен и запущен нареальный пользовательский интерфейс, у меня будут вещи, как, UIScrollViews подключен.Widget становится UIScrollViewDelegate или где этот код припаркован.Корневой контроллер представления должен не обращать на это внимания, но неуместно помещать код, который я традиционно помещаю в View Controller, в объект UIView.

Ответы [ 3 ]

4 голосов
/ 14 января 2013

Объявите IBOutlet (IBOutlet UIView * Widget;) в вашем контроллере. Поместите виджет Widget (из конструктора) в xib вашего контроллера (если у вашего контроллера нет xib, вы можете создать или указать новый xib для вашего контроллера). Затем подключите этот вид к вышеуказанному IBOutlet. Если вы хотите избежать зацепления надписей, просмотра прокрутки и т. Д. Всякий раз, когда дизайнер вносит изменения, вы должны заменить весь XIB вместо простой замены представления.

0 голосов
/ 02 августа 2012

Что касается вашего первого вопроса, я думаю, что вы ищете следующее:

Widget *widget = nil;
NSArray *nibItems = [[NSBundle mainBundle] loadNibNamed:@"Widget" owner:self options:nil];
for (id o in nibItems) {
    if ([o isTypeOfClass:[Widget class]]) {
        widget = (Widget *)id;
    }
}

// Do whatever you want with the Widget...

Загрузка его из пера через пакет сохранит работу ваших дизайнеров в конструкторе интерфейсов и вызоветвызовите initWithCoder.

Что касается второго вопроса, у вас есть два варианта.Вы можете установить свойство делегата UIScrollView внутри пера (это атрибут, доступный для IB) или установить его вручную в коде в вашем методе initWithCoder.Это зависит от желаемого поведения.

0 голосов
/ 06 мая 2011

Да, вы должны соединить IBOutlet с меткой, и тогда дизайнеры смогут разместить метку в Интерфейсном Разработчике.

Что касается UIScrollViewDelegate, вам также следует установить IBOutlet дляпредставление прокрутки, а затем внутри контроллера do widgetView.scrollView.delegate = self, чтобы установить текущий контроллер в качестве делегата представления прокрутки.

В качестве альтернативы вы можете сделать Widgetпросмотреть делегат представления прокрутки, но, по моему мнению, вы нарушите соответствие MVC, сделав это (код, который обрабатывает события представления прокрутки, является кодом контроллера).

...