Начиная писать приложения для iOS, невольно берёшь в пример самые крутые из уже созданных: Twitter, iBooks, Find My Friends, The Rules и т.д. Все эти приложения объединяет одно — нестандартный интерфейс. И это не тот нестандартный интерфейс, который получается, например, при портировании приложения с Windows Mobile, а именно красивый интерфейс, соответствующий iOS Human Interface Guidelines.
И вот, когда я начинал свой тернистый путь iOS-разработчика, передо мной встала довольно-таки нетривиальная на тот момент задача: сделать нестандартный UITableView.
Исходные данные
На входе мы имеем класс UITableView, который может делать примерно так:
Как мы видим, в нашей таблице имеются две секции, которые в свою очередь имеют три поля:
- Заголовок — header;
- Ячейки — cell;
- Подвал — footer.
Стоит рассмотреть каждое из них поближе.
Ячейка
Каждая ячейка имеет два полезных свойства — backgroundView и selectedBackgroundView. Это, фактически, обычные UIView, и, соответственно, их можно изменить на свою реализацию. Например, создать subclass от UIView. Назовём его CustomCellBackground.
CustomCellBackground.h
#import <UIKit/UIKit.h>
@interface CustomCellBackground : UIView
@end
CustomCellBackground.m
#import "CustomCellBackground.h"
@implementation CustomCellBackground
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorRef lightGrayColor = [UIColor colorWithRed:220.0/255.0
green:230.0/255.0
blue:230.0/255.0
alpha:1.0].CGColor;
CGContextSetFillColorWithColor(context, lightGrayColor);
CGContextFillRect(context, self.bounds);
}
@end
Теперь остаётся только слегка модифицировать метод tableView: cellForRowAtIndexPath:, добавив в него следующие строки:
cell.backgroundView = [[CustomCellBackground alloc] init];
cell.selectedBackgroundView = [[CustomCellBackground alloc] init];
// И перед тем, как сделать return cell, добавить:
cell.textLabel.backgroundColor = [UIColor clearColor];
В результате всех наших манипуляций мы получим что-то вроде этого:
Ну что ж, у получилось, хотя и не очень красиво. Сейчас мы можем немного украсить результат, например, добавив к нему градиент. Это можно сделать как обычной картинкой, так и средствами фрэймворка Core Graphics. Создадим наследник класса NSObject и назовём его Gradient. Очищаем .h и .m файлы и заполняем их следующим образом:
Gradient.h
#import <Foundation/Foundation.h>
void drawLinearGradient(CGContextRef context, CGRect rect,
CGColorRef startColor, CGColorRef endColor);
Gradient.m
#import "Gradient.h"
void drawLinearGradient(CGContextRef context, CGRect rect,
CGColorRef startColor, CGColorRef endColor) {
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = {0.0, 1.0};
NSArray *colors = [NSArray arrayWithObjects:
(__bridge id)startColor,
(__bridge id)endColor,
nil];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace,
(__bridge CFArrayRef) colors,
locations);
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect),
CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect),
CGRectGetMaxY(rect));
CGContextSaveGState(context);
CGContextAddRect(context, rect);
CGContextClip(context);
CGContextDrawLinearGradient(context,
gradient,
startPoint,
endPoint,
0);
CGContextRestoreGState(context);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
}
Функция, рисующая градиент, готова. Теперь нам нужно изменить реализацию CustomCellBackground:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorRef whiteColor = [UIColor colorWithRed:1.0
green:1.0
blue:1.0
alpha:1.0].CGColor;
CGColorRef lightGrayColor = [UIColor colorWithRed:220.0/255.0
green:230.0/255.0
blue:230.0/255.0
alpha:1.0].CGColor;
drawLinearGradient(context, self.bounds, whiteColor, lightGrayColor);
}
То, что у нас получилось, уже можно назвать нормальным результатом:
Естественно, если вы хотите, чтобы ячейка визуально реагировала на нажатие, для selectedBackgroundView вам нужен UIView, отличный от используемого в backgroundView. Сделайте его сами. Сделали? Тогда мы можем продолжить.
Заголовок
Как мы знаем, заголовок тоже являет собой UIView, соответственно, и его мы можем легко изменить. Всё делается по уже испытанному нами алгоритму. Прежде всего создадим подкласс UIView и назовём его TableHeader.
TableHeader.h
#import <UIKit/UIKit.h>
@interface TableHeader : UIView {
UILabel *_titleLabel;
UIColor *_lightColor;
UIColor *_darkColor;
CGRect _coloredBoxRect;
CGRect _paperRect;
}
@property (retain) UILabel *titleLabel;
@property (retain) UIColor *lightColor;
@property (retain) UIColor *darkColor;
@end
TableHeader.m
#import "TableHeader.h"
#import "Gradient.h"
@implementation TableHeader
@synthesize titleLabel = _titleLabel;
@synthesize lightColor = _lightColor;
@synthesize darkColor = _darkColor;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (id)init {
if ((self = [super init])) {
self.backgroundColor = [UIColor clearColor];
self.opaque = NO;
self.titleLabel = [[UILabel alloc] init];
_titleLabel.textAlignment = UITextAlignmentCenter;
_titleLabel.opaque = NO;
_titleLabel.backgroundColor = [UIColor clearColor];
_titleLabel.font = [UIFont boldSystemFontOfSize: 15.0];
_titleLabel.textColor = [UIColor whiteColor];
_titleLabel.shadowColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
_titleLabel.shadowOffset = CGSizeMake(0, -1);
[self addSubview:_titleLabel];
self.lightColor = [UIColor colorWithRed:105.0f/255.0f green:179.0f/255.0f
blue:216.0f/255.0f alpha:1.0];
self.darkColor = [UIColor colorWithRed:21.0/255.0 green:92.0/255.0
blue:136.0/255.0 alpha:1.0];
}
return self;
}
-(void) layoutSubviews {
CGFloat coloredBoxMargin = 6.0;
CGFloat coloredBoxHeight = 30.0;
_coloredBoxRect = CGRectMake(coloredBoxMargin,
coloredBoxMargin,
self.bounds.size.width-coloredBoxMargin*2,
coloredBoxHeight);
CGFloat paperMargin = 9.0;
_paperRect = CGRectMake(paperMargin,
CGRectGetMaxY(_coloredBoxRect),
self.bounds.size.width-paperMargin*2,
self.bounds.size.height-CGRectGetMaxY(_coloredBoxRect));
_titleLabel.frame = _coloredBoxRect;
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorRef whiteColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0].CGColor;
CGColorRef lightColor = _lightColor.CGColor;
CGColorRef darkColor = _darkColor.CGColor;
CGColorRef shadowColor = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:0.5].CGColor;
CGContextSetFillColorWithColor(context, whiteColor);
CGContextFillRect(context, _paperRect);
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, CGSizeMake(0, 2), 3.0, shadowColor);
CGContextSetFillColorWithColor(context, lightColor);
CGContextFillRect(context, _coloredBoxRect);
CGContextRestoreGState(context);
}
@end
Теперь нам нужно модифицировать наш UITableViewController:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
TableHeader *header = [[TableHeader alloc] init];
if (section == 0) {
header.titleLabel.text = @"Header №1";
}
else {
header.titleLabel.text = @"Header №2";
}
if (section == 0) {
header.lightColor = [UIColor lightGrayColor];
header.darkColor = [UIColor grayColor];
}
else {
header.lightColor = [UIColor redColor];
header.darkColor = [UIColor brownColor];
}
return header;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 40;
}
После этого компилируем код и получаем симпатичные заголовки для каждой секции таблицы.
Немного о footer
Footer практически ничем не отличается от Header. Разница лишь в методах, с помощью которых мы получаем его:
-(CGFloat) tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
return 15;
}
- (UIView *) tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
return [[CustomFooter alloc] init];
}
Думаю, %username% не составит труда самому сделать свой красивый footer.
Заключение
Ну вот и всё, мой коротенький рассказ окончен. Если вы хотите ещё почитать на тему кастомизации интерфейсов в iOS, рекомендую вам сайт www.raywenderlich.com, где есть множество интересной информации по этой теме. (И откуда, собственно, я и научился тому, о чём рассказал выше.)
Автор: PATOGEN