Статьи

iOS SDK: пользовательская графика UIAlertView

Класс UIAlertView прост в реализации, но если вы не попадаете под капот и не возитесь с ним, он будет каждый раз выглядеть одинаково. Этот краткий совет показывает, как создавать собственные графические изображения программно и применять их к предупреждению. Изменение графики в вашем приложении может быть просто залогом нового и привлекательного дизайна!


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


Сначала мы хотим 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 .


Давайте удалим стандартную графику и UIAlertView .

Во-первых, нам нужно переопределить 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: метод.

Графика для 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 на другое число.

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


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


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