Класс 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
, и этот учебник охватывает только небольшую их часть. Но дело в том, что все эти элементы можно настраивать и создавать программно, чтобы повысить удобство работы пользователя.