Класс UIAlertView прост в реализации, но если вы не попадаете под капот и не возитесь с ним, он будет каждый раз выглядеть одинаково. Этот краткий совет показывает, как создавать собственные графические изображения программно и применять их к предупреждению. Изменение графики в вашем приложении может быть просто залогом нового и привлекательного дизайна!
Шаг 1: настройте файл Xcode
Запустите Xcode и создайте новый проект, щелкнув Файл> Новый> Новый проект. Мы хотим создать
iOS пустое приложение. Назовите ваш продукт «Custom Alert» и введите идентификатор компании по вашему выбору. Выберите «iPhone» в меню «Семейство устройств» и удалите все проверки перед «Использовать базовые данные», «Использовать автоматический подсчет ссылок» и «Включить юнит-тесты». Нажмите «Далее» и выберите место для сохранения вашего проекта. Убедитесь, что поле рядом с «Source Control» не отмечено и нажмите «Создать», чтобы создать проект.

Шаг 2. Создайте UIAlertView.
Сначала мы хотим UIAlertView подкласс UIAlertView . Нажмите Файл> Новый> Новый файл, чтобы создать новый подкласс. Выберите класс iOS Cocoa Touch Objective-C и нажмите «Далее». Назовите свой класс «CustomAlertView.» и выберите UIView из меню «Подкласс». Прежде чем нажать «Далее», выберите папку «CustomAlert» в меню «Группы» и убедитесь, что «CustomAlert» отмечен в поле «Цели». Нажмите «Создать», чтобы добавить новый подкласс.
Недавно созданный класс является классом UIView , но он нам нужен для наследования от UIAlertView . Нажмите на файл «CustomAlertView.h» и измените наследование на UIAlertView .
|
1
2
3
4
5
|
#import <UIKit/UIKit.h>
@interface CustomAlertView : UIAlertView
@end
|
В заголовочном файле AppDelegate импортируйте файл «CustomAlertView.h» и выполните протокол UIAlertViewDelegate .
|
1
2
3
4
5
6
7
|
#import <UIKit/UIKit.h>
#import «CustomAlertView.h»
@interface AppDelegate : UIResponder <UIApplicationDelegate, UIAlertViewDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
|
Перейдите к файлу реализации AppDelegate и alertView:clickedButtonAtIndex: метод экземпляра alertView:clickedButtonAtIndex:
buttonIndex используется для определения, какая кнопка была нажата.
|
1
2
3
4
5
6
7
8
9
|
— (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0) {
NSLog(@»THE ‘NO’ BUTTON WAS PRESSED»);
}
if (buttonIndex == 1) {
NSLog(@»THE ‘YES’ BUTTON WAS PRESSED»);
}
}
|
Теперь вставьте следующий код для создания экземпляра объекта оповещения в application:didFinishLaunchingWithOptions: method.
Обратите внимание, что для делегата установлено значение self в методе init , чтобы получить обратный вызов при нажатии кнопки.
|
1
2
3
4
5
6
7
|
CustomAlertView *customAlertView = [[CustomAlertView alloc] initWithTitle:@»Custom Alert View»
message:@»Customize the look of your app’s alert view programmatically.»
delegate:self
cancelButtonTitle:@»NO»
otherButtonTitles:@»YES»,nil];
[customAlertView show];
[customAlertView release];
|
Если вы создадите и запустите приложение прямо сейчас, вы получите типичную графику стандартного UIAlertView .
Шаг 3: настроить графику
Давайте удалим стандартную графику и UIAlertView .
Переопределение макета Subviews:
Во-первых, нам нужно переопределить layoutSubviews layoutSubviews: метод UIView в файле «CustomAlertView.m», где мы скроем синий фон и найдем метки, чтобы изменить внешний вид текста.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
— (void)layoutSubviews
{
for (UIView *subview in self.subviews){ //Fast Enumeration
if ([subview isMemberOfClass:[UIImageView class]]) {
subview.hidden = YES;
}
if ([subview isMemberOfClass:[UILabel class]]) { //Point to UILabels To Change Text
UILabel *label = (UILabel*)subview;
label.textColor = [UIColor colorWithRed:210.0f/255.0f green:210.0f/255.0f blue:210.0f/255.0f alpha:1.0f];
label.shadowColor = [UIColor blackColor];
label.shadowOffset = CGSizeMake(0.0f, 1.0f);
}
}
}
|
Поскольку layoutSubviews: вызывается после инициализации объекта, но перед layoutSubviews: объекта, у вас есть возможность добавить код, который обеспечит дополнительную настройку, прежде чем он появится на экране. В приведенном выше коде мы используем быстрое перечисление для перебора массива существующих подпредставлений оповещения в поисках UIImageView , который содержит фон и две UILabels которые содержат текст. Используя isMemberOfClass: метод isMemberOfClass: логическое значение true или false в зависимости от того, является ли подпредставление экземпляром указанного класса. Когда классы расположены, UIImageView скрыт и установлены UILabels' текста UILabels' цвета теней и смещения теней.
Переопределение drawRect
Импорт готовой векторной графики может быть проблематичным. Между изменением разрешения экрана и организацией файлов импорт изображений, как правило, требует больше усилий, чем в некоторых случаях. Если вы ищете гибкую, идеальную по пикселям графику, лучше всего переопределить представление drawRect: метод.
Графика для UIAlertView созданная с помощью следующего кода, обеспечивает тонкую разницу в цвете, тени и текстуре, но сохраняет ту же общую форму стандартного оповещения. Вставьте разделы кода, описанные в этом руководстве, в drawRect: метод в подклассе UIAlertView .
Получить ссылку на текущий графический контекст
|
1
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
Функция UIGraphicsGetCurrentContext() возвращает ссылку на графический контекст текущего представления, позволяя вносить изменения в его состояние.
Создать базовую форму
|
01
02
03
04
05
06
07
08
09
10
|
CGRect activeBounds = self.bounds;
CGFloat cornerRadius = 10.0f;
CGFloat inset = 6.5f;
CGFloat originX = activeBounds.origin.x + inset;
CGFloat originY = activeBounds.origin.y + inset;
CGFloat width = activeBounds.size.width — (inset*2.0f);
CGFloat height = activeBounds.size.height — (inset*2.0f);
CGRect bPathFrame = CGRectMake(originX, originY, width, height);
CGPathRef path = [UIBezierPath bezierPathWithRoundedRect:bPathFrame cornerRadius:cornerRadius].CGPath;
|
Мы начнем с создания базовой формы с закругленными углами. Обратите внимание, что параметры для формы основаны на текущих границах UIAlertView . Это позволяет рисовать фигуру в соответствии с границами оповещения, компенсируя любые возникающие изменения. При создании этой фигуры важны два компонента: cornerRadius и cornerRadius . cornerRadius определяет округлость углов, в то время как inset определяет, как далеко внутри границ нарисована фигура. Наконец, мы создаем фигуру с помощью UIBezierPath из bezierPathWithRoundedRect: cornerRadius: метода bezierPathWithRoundedRect: cornerRadius:
Доступ к свойству CGPath вновь созданного объекта UIBezierPath позволяет использовать его в контексте.
Добавить заливку и внешнюю тень
|
1
2
3
4
|
CGContextAddPath(context, path);
CGContextSetFillColorWithColor(context, [UIColor colorWithRed:210.0f/255.0f green:210.0f/255.0f blue:210.0f/255.0f alpha:1.0f].CGColor);
CGContextSetShadowWithColor(context, CGSizeMake(0.0f, 1.0f), 6.0f, [UIColor colorWithRed:0.0f/255.0f green:0.0f/255.0f blue:0.0f/255.0f alpha:1.0f].CGColor);
CGContextDrawPath(context, kCGPathFill);
|
Когда рисуете в Core Graphics, думайте о слоях, накладываемых друг на друга. В этом проекте первый слой состоит из теней, поскольку они являются самым нижним и внешним слоями. Мы устанавливаем параметры первого графического состояния, в частности его форму, цвет заливки и цвет тени. Затем мы рисуем контекст, используя CGContextDrawPath() и CGPathDrawingMode kCGPathFill которые по существу создадут внешнюю тень.
Клип контекст
|
1
2
3
|
CGContextSaveGState(context);
CGContextAddPath(context, path);
CGContextClip(context);
|
Теперь мы хотим нарисовать внутри базовой формы. Для этого нам нужно обрезать контекст по фигуре до рисования следующего состояния. Все, что нарисовано позже, но перед вызовом CGContextRestoreGState() остается в области отсечения и не покрывает внешнюю тень.
Рисовать градиент
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
size_t count = 3;
CGFloat locations[3] = {0.0f, 0.57f, 1.0f};
CGFloat components[12] =
{
70.0f/255.0f, 70.0f/255.0f, 70.0f/255.0f, 1.0f, //1
55.0f/255.0f, 55.0f/255.0f, 55.0f/255.0f, 1.0f, //2
40.0f/255.0f, 40.0f/255.0f, 40.0f/255.0f, 1.0f //3
};
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, count);
CGPoint startPoint = CGPointMake(activeBounds.size.width * 0.5f, 0.0f);
CGPoint endPoint = CGPointMake(activeBounds.size.width * 0.5f, activeBounds.size.height);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGColorSpaceRelease(colorSpace);
CGGradientRelease(gradient);
|
Далее мы добавляем тонкий градиент к фону. Чтобы нарисовать градиент, нам понадобится ссылка на градиент, которая определяет цвета и их расположение в градиенте. startPoint endPoint и endPoint градиента основаны на границах представления предупреждений, охватывающих все предупреждения, не выходя за пределы области отсечения. Обратите внимание, что мы выпустили CGColorSpaceRef и CGGradientRef теперь, когда мы закончили с ними. В общем, когда вы видите вызов функции Core Graphics со словом & quotcreate & quot в нем, вам нужно освободить ссылку, когда вы закончите.
Создать заштрихованный фон
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
CGFloat buttonOffset = 92.5f;
CGContextSaveGState(context);
CGRect hatchFrame = CGRectMake(0.0f, buttonOffset, activeBounds.size.width, (activeBounds.size.height — buttonOffset+1.0f));
CGContextClipToRect(context, hatchFrame);
CGFloat spacer = 4.0f;
int rows = (activeBounds.size.width + activeBounds.size.height/spacer);
CGFloat padding = 0.0f;
CGMutablePathRef hatchPath = CGPathCreateMutable();
for(int i=1; i<=rows; i++) {
CGPathMoveToPoint(hatchPath, NULL, spacer * i, padding);
CGPathAddLineToPoint(hatchPath, NULL, padding, spacer * i);
}
CGContextAddPath(context, hatchPath);
CGPathRelease(hatchPath);
CGContextSetLineWidth(context, 1.0f);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:0.0f/255.0f green:0.0f/255.0f blue:0.0f/255.0f alpha:0.15f].CGColor);
CGContextDrawPath(context, kCGPathStroke);
CGContextRestoreGState(context);
|
Следующий фрагмент кода касается заштрихованных диагональных линий за кнопками. Чтобы линии рисовались только под кнопками, нам нужно создать новую форму прямоугольника, обрезать контекст для прямоугольника и добавить заштрихованные линии в контекст. buttonOffset — это высота пространства для заштрихованных линий. Шаблон движения, созданный циклом for основан на границах представления предупреждения. Чтобы изменить внешний вид заштрихованных линий, просто CGContextSetLineWidth() spacer CGContextSetLineWidth() или значения hatchFrame .
Нарисуйте разделительную линию
|
01
02
03
04
05
06
07
08
09
10
11
12
|
CGMutablePathRef linePath = CGPathCreateMutable();
CGFloat linePathY = (buttonOffset — 1.0f);
CGPathMoveToPoint(linePath, NULL, 0.0f, linePathY);
CGPathAddLineToPoint(linePath, NULL, activeBounds.size.width, linePathY);
CGContextAddPath(context, linePath);
CGPathRelease(linePath);
CGContextSetLineWidth(context, 1.0f);
CGContextSaveGState(context);
CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:0.0f/255.0f green:0.0f/255.0f blue:0.0f/255.0f alpha:0.6f].CGColor);
CGContextSetShadowWithColor(context, CGSizeMake(0.0f, 1.0f), 0.0f, [UIColor colorWithRed:255.0f/255.0f green:255.0f/255.0f blue:255.0f/255.0f alpha:0.2f].CGColor);
CGContextDrawPath(context, kCGPathStroke);
CGContextRestoreGState(context);
|
Теперь мы хотим нарисовать разделительную линию между кнопками и надписями. Создание линии с использованием CGPathCreateMutable рисует ее с использованием указанных точек. Расположение линии в представлении предупреждений основано на buttonOffset и охватывает ее ширину. Еще раз, мы используем вызов функции, содержащий слово «create», CGPathCreateMutable() . Поэтому мы выпускаем CGMutablePathRef один после того, как мы закончим.
Создать внутреннюю тень
|
1
2
3
4
5
|
CGContextAddPath(context, path);
CGContextSetLineWidth(context, 3.0f);
CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:210.0f/255.0f green:210.0f/255.0f blue:210.0f/255.0f alpha:1.0f].CGColor);
CGContextSetShadowWithColor(context, CGSizeMake(0.0f, 0.0f), 6.0f, [UIColor colorWithRed:0.0f/255.0f green:0.0f/255.0f blue:0.0f/255.0f alpha:1.0f].CGColor);
CGContextDrawPath(context, kCGPathStroke);
|
Чтобы создать видимость UIBezierPath границы вокруг представления предупреждений, мы рисуем исходную UIBezierPath с тенью. Путь будет по-прежнему обрезаться при первом вызове, чтобы обрезать контекст, в результате чего тень будет появляться только внутри штриховой линии. Чтобы изменить размер внутренней тени, установите параметр размытия внутри CGContextSetShadowWidthColor на другое число.
Перерисовать путь, чтобы избежать Pixilation
|
1
2
3
4
5
6
|
CGContextRestoreGState(context);
CGContextAddPath(context, path);
CGContextSetLineWidth(context, 3.0f);
CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:210.0f/255.0f green:210.0f/255.0f blue:210.0f/255.0f alpha:1.0f].CGColor);
CGContextSetShadowWithColor(context, CGSizeMake(0.0f, 0.0f), 0.0f, [UIColor colorWithRed:0.0f/255.0f green:0.0f/255.0f blue:0.0f/255.0f alpha:0.1f].CGColor);
CGContextDrawPath(context, kCGPathStroke);
|
Закругленные углы имеют тенденцию казаться пикселизированными после обрезки, поэтому наш последний фрагмент кода перерисовывает внешнюю строку, чтобы обеспечить ее четкость и чистоту. Перед тем, как перерисовать внешнюю линию, мы вызываем CGContextRestoreGState() которая освобождает нашу первую область отсечения, поэтому мы можем рисовать поверх исходной линии.
Шаг 4: протестируйте UIAlertView
Запустите приложение на симуляторе или на своем устройстве, чтобы просмотреть UIAlertView и его пользовательскую графику.

Вывод
Создание графики с использованием Core Graphics — это тонкость и многоуровневость эффектов. Существует множество графических элементов, которые можно настроить при настройке UIAlertView , и этот учебник охватывает только небольшую их часть. Но дело в том, что все эти элементы можно настраивать и создавать программно, чтобы повысить удобство работы пользователя.