Статьи

iOS SDK: пользовательские делегаты

Делегаты являются полезным инструментом в общении между объектами. В этом уроке мы создадим и реализуем пользовательский делегат, который позволит трем UISliders настроить цвет фона ViewController .


Сообщения в Objective-C — это улица с односторонним движением. Родительский класс может отправить сообщение своему дочернему элементу, но дочерний элемент не может самостоятельно отправить сообщение своему родительскому элементу. Однако с помощью делегата можно установить двустороннюю связь. UIScrollView и UITableView регулярно используют делегатов для связи между моделью, представлением и контроллером. Есть много причин, по которым шаблон делегата полезен. Делегат может использоваться для многократного использования объекта, для предоставления гибкого способа отправки сообщений или для реализации настройки.


Запустите Xcode и нажмите «Файл»> «Создать»> «Проект». Выберите приложение iOS Single View и нажмите «Далее». Назовите свой продукт «Делегаты» и введите имя для своего идентификатора компании, например «com.companyName.delegates». Выберите семейство устройств iPhone и нажмите «Далее». Выберите место для хранения вашего проекта и нажмите «Создать».

Делегаты - настройка проекта

Нажмите Файл> Создать> Файл и выберите класс Cocoa Touch Objective-C. Назовите свой класс «MTSlider» и выберите UISlider из выпадающего меню «Подкласс». Нажмите «Далее», затем нажмите «Создать».

Делегаты - подкласс UISlider

Сначала нам нужно объявить методы для делегата. Нажмите на «MTSlider.h.» Введите следующий код над интерфейсом.

1
2
3
4
5
6
7
@class MTSlider;
@protocol MTSliderDelegate <NSObject>
@optional
— (void)MTSliderDidChange:(MTSlider *)MTSlider withValue:(CGFloat)value;
@required
— (CGFloat)startPositionForMTSlider:(MTSlider *)MTSlider;
@end

Обратите внимание на строку @class MTSlider . Размещая этот код над всем остальным, компилятору сообщают, что в какой-то момент в MTSlider будет объявлен MTSlider . Без этой директивы компилятора компилятор выдаст вам предупреждение, потому что он ожидает найти интерфейс для MTSlider прямо сейчас.

Методы делегата объявляются начиная с @protocol . Протокол MTSliderDelegate соответствует протоколу MTSliderDelegate по определенной причине. Протокол NSObject содержит метод respondsToSelector: который можно использовать для гарантии того, что объект делегата фактически реализует необязательный метод перед вызовом метода. Вызов метода, который не реализован объектом делегата, приведет к сбою приложения.

Проще говоря, необязательный метод — это метод, который не должен быть реализован объектом делегата; в этом случае объектом делегата будет ViewController , однако это может быть любой объект. Необязательный метод MTSliderDidChange:withValue: в объекте делегата при изменении значения ползунка.

С другой стороны, обязательный метод — это метод, который должен быть реализован объектом делегата, иначе вы получите предупреждение компилятора. Обязательный метод startPositionForMTSlider: запрашивает объект делегата, с которого должны начинаться ползунки, и получает значение его начальной позиции в ответ от делегата.

Еще в «MTSlider.h» введите следующий код непосредственно под @interface чтобы объявить переменную экземпляра, или ivar, для делегата.

1
id <MTSliderDelegate> sliderDelegate;

Если вы используете ARC, введите следующий код:

1
__weak id <MTSliderDelegate> sliderDelegate;

Ivar имеет тип id поэтому он гибкий и может принимать любой тип объекта. В следующей части MTSliderDelegate говорится, что любой объект, в конечном итоге назначаемый sliderDelegate будет содержать методы протокола MTSliderDelegate как часть его собственной реализации.

Закончите, синтезируя методы сеттера и геттера. Введите следующий код чуть ниже закрывающей скобки переменной экземпляра.

1
@property (nonatomic, assign) id <MTSliderDelegate> sliderDelegate;

Если вы используете ARC, введите следующий код:

1
@property (nonatomic, weak) id <MTSliderDelegate> sliderDelegate;

Нажмите «MTSlider.m» и введите следующий код чуть ниже @implementation чтобы завершить свойство.

1
@synthesize sliderDelegate;

Нажмите на файл «ViewController.h». Введите следующий код для соответствия протоколу MTSliderDelegate и импортируйте «MTSlider.h».

1
2
#import «MTSlider.h»
@interface ViewController : UIViewController <MTSliderDelegate>

Нажмите на файл «ViewController.m» и введите следующий код для реализации MTSliderDelegate протокола MTSliderDelegate .

1
2
3
4
5
6
7
— (CGFloat)startPositionForMTSlider:(MTSlider *)MTSlider{
 
}
 
— (void)MTSliderDidChange:(MTSlider *)MTSlider withValue:(CGFloat)value{
 
}

Создание пользовательского инициализатора является ключом к получению начальной позиции для ползунков. В «MTSlider.h» добавьте следующий код, чтобы объявить новый инициализатор.

1
— (id)initWithFrame:(CGRect)frame andDelegate:(id<MTSliderDelegate>)delegateObject;

Нажмите «MTSlider.m» и найдите метод initWithFrame: . Удалите существующий метод и замените его следующим кодом.

1
2
3
4
5
6
7
8
— (id)initWithFrame:(CGRect)frame andDelegate:(id<MTSliderDelegate>)delegateObject{
    self = [super initWithFrame:frame];
    if (self) {
        self.sliderDelegate = delegateObject;
        self.value = [sliderDelegate startPositionForMTSlider:self];
    }
    return self;
}

Установка делегата во время инициализации позволяет немедленно вызвать метод делегата. Метод startPositionForMTSlider: получает начальную позицию для ползунков. Поскольку он вызывается в инициализаторе, позиции ползунков устанавливаются до их отрисовки на экране.

Метод UISlider setValue:animated: вызывается автоматически при каждом перемещении колышка ползунка. В файле «MTSlider.m» добавьте следующий метод.

1
2
3
4
5
6
— (void)setValue:(float)value animated:(BOOL)animated{
    [super setValue:value animated:animated];
    if (sliderDelegate != nil && [sliderDelegate respondsToSelector:@selector(MTSliderDidChange:withValue:)]){
        [[self sliderDelegate] MTSliderDidChange:self withValue:value];
    }
}

При переопределении setValue:animated: каждый раз, когда перемещается ползунок, сообщение отправляется объекту делегата. Обратите внимание на вызов супер, super setValue:animated: Важно, чтобы мы случайно не испортили что-то, что метод делает за кадром при переопределении существующего метода.

Метод делегата MTSliderDidChange:withValue: это необязательный метод протокола, объявленный ранее. Объект делегата передается каждый раз при смене слайдера. Помните, что вызов метода, который не был реализован, приведет к сбою приложения. Вызов respondsToSelector: на объекте делегата проверяет, можно ли идти вперед и сообщать необязательный метод делегата.


Нажмите на файл «ViewController.m» и введите следующий код внутри viewDidLoad чтобы установить цвет фона представления с четырьмя компонентами цвета и создать красный, зеленый и синий объекты MTSlider . Если вы используете ARC, обязательно удалите строки [redSlider release]; , [greenSlider release]; и [blueSlider release]; так как эти звонки не нужны.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
CGFloat sliderColorPosition = 0.3f;
self.view.backgroundColor = [UIColor colorWithRed:sliderColorPosition green:sliderColorPosition blue:sliderColorPosition alpha:1.0f];
     
CGRect redSliderFrame = CGRectMake(20.0f, 20.0f, 280.0f, 28.0f);
MTSlider *redSlider = [[MTSlider alloc] initWithFrame:redSliderFrame andDelegate:self];
redSlider.tag = 1;
[self.view addSubview:redSlider];
[redSlider release];
     
CGRect greenSliderFrame = CGRectMake(20.0f, 70.0f, 280.0f, 28.0f);
MTSlider *greenSlider = [[MTSlider alloc] initWithFrame:greenSliderFrame andDelegate:self];
greenSlider.tag = 2;
[self.view addSubview:greenSlider];
[greenSlider release];
     
CGRect blueSliderFrame = CGRectMake(20.0f, 120.0f, 280.0f, 28.0f);
MTSlider *blueSlider = [[MTSlider alloc] initWithFrame:blueSliderFrame andDelegate:self];
blueSlider.tag = 3;
[self.view addSubview:blueSlider];
[blueSlider release];

Используя пользовательский инициализатор MTSlider , initWithFrame:andDelegate: делегат устанавливается, и объект ViewController становится объектом делегата каждого ползунка. Обычно вы можете ожидать увидеть набор делегатов, используя следующий код: redSlider.sliderDelegate=self; , Однако в этом случае назначение делегата передается во время инициализации.

Обратите внимание, что свойство тега каждого слайдера установлено. Первоначально цвет фона ViewController установлен на темно-серый. При настройке каждого ползунка цвет фона будет меняться соответственно, поскольку тег определяет, какой ползунок активен.


Найдите реализацию startPositionForMTSlider: и введите следующий код внутри фигурных скобок, чтобы установить начальное значение ползунков на 0,3.

1
2
CGFloat sliderStartPosition = 0.3f;
return sliderStartPosition;

Найдите реализацию MTSliderDidChange:withValue: и добавьте следующий код для настройки цвета фона.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if (MTSlider.tag == 1) { //Red Slider
    CGColorRef bgColor = self.view.backgroundColor.CGColor;
    const CGFloat *colorsPointer = CGColorGetComponents(bgColor);
    CGFloat currentGreen = colorsPointer[1];
    CGFloat currentBlue = colorsPointer[2];
    self.view.backgroundColor = [UIColor colorWithRed:value green:currentGreen blue:currentBlue alpha:1.0f];
 
}
if (MTSlider.tag == 2) { //Green Slider
    CGColorRef bgColor = self.view.backgroundColor.CGColor;
    const CGFloat *colorsPointer = CGColorGetComponents(bgColor);
    CGFloat currentRed = colorsPointer[0];
    CGFloat currentBlue = colorsPointer[2];
    self.view.backgroundColor = [UIColor colorWithRed:currentRed green:value blue:currentBlue alpha:1.0f];
 
}
if (MTSlider.tag == 3) { //Blue Slider
    CGColorRef bgColor = self.view.backgroundColor.CGColor;
    const CGFloat *colorsPointer = CGColorGetComponents(bgColor);
    CGFloat currentRed = colorsPointer[0];
    CGFloat currentGreen = colorsPointer[1];
    self.view.backgroundColor = [UIColor colorWithRed:currentRed green:currentGreen blue:value alpha:1.0f];
}

Каждый раз, когда изменяется ползунок, в метод делегата отправляется сообщение. Сообщение содержит текущий MTSlider и его значение. Доступ к свойству тега текущего слайдера позволяет определить, какой слайдер отправил сообщение, и цвет фона соответствующим образом обновляется.


Выберите «Продукт»> «Выполнить» или нажмите стрелку «Выполнить» в верхнем левом углу, чтобы просмотреть ползунки в действии. Настройте ползунки, чтобы увидеть, как дочерние элементы могут контролировать цвет фона родительского элемента.


Есть много других способов управления цветом фона UIViewController , включая цели или уведомления. Apple разработала UISlider для использования целевого шаблона для передачи данных. Однако, если вы хотите добавить больше функциональности, лучший способ общения от дочернего элемента к его родительскому элементу — создать пользовательский делегат.