[Из песочницы] UITableView: добавляем иконку в UITableViewRowAction

iOS SDK дает нам «из коробки» два варианта настроить кнопку, которая будет показана при сдвиге UITableViewCell влево.

cc971fd59740497ab8e7d188300dc740.jpg

Первый вариант, доступный с первых версий и до наших дней, мало привлекателен для обычных кнопок, поскольку предназначен именно для «опасных» действий, ибо фон окрашивается системой в красный цвет и не поддается легкому изменению через публичный API.

- (NSString *)tableView:(nonnull UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
    return @"Настроить";
}

Второй вариант, доступный с iOS 8, больше привлекает, поскольку есть возможность указать стиль кнопки (Destructive, Normal), а также в случае надобности установить конкретный цвет фона через backgroundColor.

- (NSArray *)tableView:(nonnull UITableView *)tableView editActionsForRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
    UITableViewRowAction *configureAction;
    configureAction = [UITableViewRowAction
                       rowActionWithStyle:UITableViewRowActionStyleNormal
                       title:@"Настроить"
                       handler:^(UITableViewRowAction * __nonnull action, NSIndexPath * __nonnull indexPath) {
                          // handle
                       }];
    
    return @[ configureAction ];
}

Ни первый, ни второй варианты не дают возможности назначить иконку, что довольно неприятно.
Но мы всё равно попробуем что-нибудь сделать.


Попытка раз

UITableViewRowAction выглядит более богатым к настройке. Попробуем поставить ему backgroundColor с помощью patternImage:

UIImage *patternImage = [UIImage imageNamed:@"gear_compact"]; // 50x50
configureAction.backgroundColor = [UIColor colorWithPatternImage:patternImage];


image

Результат ожидаемо кривоват, но мы хотя бы получили иконку на кнопке.
Оставим вопрос цвета фона и текста на потом, сперва попробуем настроить саму иконку.


Попытка два

Поскольку patternImage призван повторяться, попробуем увеличить сам pattern, чтобы он повторялся поменьше — создадим pattern по размеру кнопки:

UIImage *patternImage = [UIImage imageNamed:@"gear_fullsize"]; // 240x90
configureAction.backgroundColor = [UIColor colorWithPatternImage:patternImage];


image

Теперь смотрится гораздо лучше. Но если, например, поле окажется многострочным, а следом по высоте растянется и ячейка, то будет не очень приятно:

image


Попытка три

Попробуем сделать иначе. Что происходит, когда мы сдвигаем строку влево? Верно, она просто уезжает левее, обнажая то, что находится ниже по иерархии: кнопку. Попробуем добавить слой сверху, который будет пристегнут к правому краю ячейки (компонент PureLayout в помощь для быстроты реализации).

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [UITableViewCell new];
    cell.textLabel.text = self.objects[indexPath.row];
    cell.textLabel.numberOfLines = 0;
    
    UIView *actionView = [UIView new];
    actionView.backgroundColor = [UIColor greenColor];
    [cell addSubview:actionView];
    [actionView autoMatchDimension:ALDimensionHeight toDimension:ALDimensionHeight ofView:cell];
    [actionView autoMatchDimension:ALDimensionWidth toDimension:ALDimensionWidth ofView:cell];
    [actionView autoPinEdge:ALEdgeLeft toEdge:ALEdgeRight ofView:cell];
    [actionView autoAlignAxis:ALAxisHorizontal toSameAxisOfView:cell.contentView];
    
    UIImageView *actionIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"gear_compact"]];
    [actionView addSubview:actionIcon];
    [actionIcon autoAlignAxis:ALAxisHorizontal toSameAxisOfView:actionView];
    [actionIcon autoPinEdge:ALEdgeLeft toEdge:ALEdgeLeft ofView:actionView withOffset:10];
    
    UILabel *actionLabel = [UILabel new];
    actionLabel.text = @"Настроить";
    [actionView addSubview:actionLabel];
    [actionLabel autoAlignAxis:ALAxisHorizontal toSameAxisOfView:actionView];
    [actionLabel autoPinEdge:ALEdgeLeft toEdge:ALEdgeRight ofView:actionIcon withOffset:5];
    
        return cell;
}


image

Теперь результат гораздо приятнее, правда есть одно но в данном подходе: ширина сдвига ячейки определяется текстом в UITableViewRowAction.title. Соответственно, придется регулировать видимую часть кнопки именно этим параметром. Как вариант — заполнять его соответствующими нужной ширине пробелами, или дописывать что-то в начало или конец.

К слову, данный элемент не отрабатывает нажатия сам, поэтому они спокойно пересылаются в UITableViewRowAction.handler.


Попытка четыре

- (NSArray *)tableView:(nonnull UITableView *)tableView editActionsForRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
    UITableViewRowAction *configureAction;
    configureAction = [UITableViewRowAction
                       rowActionWithStyle:UITableViewRowActionStyleNormal
                       title:@"(i) Настроить"
                       handler:^(UITableViewRowAction * __nonnull action, NSIndexPath * __nonnull indexPath) {
                          // handle
                       }];
    
    return @[ configureAction ];
}


image

Похоже, теперь всё выглядит отлично.

В итоге есть два но при таком подходе:
— ширина сдвига определяется отдельно параметром UITableViewRowAction.title
— не получится сделать более одной кнопки, либо придется теснить их все в собственном слое и туда же вставлять свои кнопки

Однако это видится мне приятнее, чем распространенный подход по встраиванию кнопки в саму ячейку с обработкой жестов, поскольку усложняется логика.

Возможно, есть еще более лаконичный способ добиться желаемого, но мне о таком пока неизвестно.

© Habrahabr.ru