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




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

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


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

- (NSArray<UITableViewRowAction *> *)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<UITableViewRowAction *> *)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
— не получится сделать более одной кнопки, либо придется теснить их все в собственном слое и туда же вставлять свои кнопки

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

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

Комментарии (3)


  1. Kirpa
    06.07.2015 20:42

    Можно использовать или сторонние компоненты или посмотреть как у них работает. Вот один такой компонент, написаный моим коллегой. www.cocoacontrols.com/controls/afmslidingcell

    На сайте есть и другие, если конкретная реализация не подходит.


    1. bronenos Автор
      06.07.2015 20:57
      +2

      В целом я это и имел в виду, когда писал про усложнение логики своими жестами и прочим.
      Если же говорить об использовании компонента, то да, согласен, тоже вариант.

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


  1. blind_oracle
    08.07.2015 13:35

    (NSString *)tableView:(nonnull UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(nonnull NSIndexPath *)indexPath

    Название где-то за гранью добра и зла :)