Лёгкая кастомизация UITableView

в 6:01, , рубрики: Cocoa, ios development, objective-c, UITableView, UITableViewCell, xcode, интерфейс, Песочница, разработка под iOS, метки: , , , , , ,

Начиная писать приложения для iOS, невольно берёшь в пример самые крутые из уже созданных: Twitter, iBooks, Find My Friends, The Rules и т.д. Все эти приложения объединяет одно — нестандартный интерфейс. И это не тот нестандартный интерфейс, который получается, например, при портировании приложения с Windows Mobile, а именно красивый интерфейс, соответствующий iOS Human Interface Guidelines.

И вот, когда я начинал свой тернистый путь iOS-разработчика, передо мной встала довольно-таки нетривиальная на тот момент задача: сделать нестандартный UITableView.

Исходные данные

На входе мы имеем класс 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];

В результате всех наших манипуляций мы получим что-то вроде этого:

Лёгкая кастомизация UITableView

Ну что ж, у получилось, хотя и не очень красиво. Сейчас мы можем немного украсить результат, например, добавив к нему градиент. Это можно сделать как обычной картинкой, так и средствами фрэймворка 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);
}

То, что у нас получилось, уже можно назвать нормальным результатом:

Лёгкая кастомизация UITableView

Естественно, если вы хотите, чтобы ячейка визуально реагировала на нажатие, для 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;
}

После этого компилируем код и получаем симпатичные заголовки для каждой секции таблицы.

Лёгкая кастомизация UITableView

Немного о 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


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js