IOS - Разрешить перемещение строки по умолчанию в `UITableView` без режима редактирования

Я хочу разрешить перемещение строк по умолчанию в UITableView, не находясь в режиме редактирования, и без ущерба для поведения по умолчанию UITableView.

movable cell

На изображении выше отображается ячейка в режиме редактирования с включенным движением.

Я попробовал просто запустить for (UIView *subview in cell.subviews) (пока мой UITableView находился в режиме редактирования), но кнопка не включалась:

<UITableViewCellScrollView: 0x8cabd80; frame = (0 0; 320 44); autoresize = W+H; gestureRecognizers = <NSArray: 0x8c9ba20>; layer = <CALayer: 0x8ca14b0>; contentOffset: {0, 0}>

Как разрешить/добавить движение "button" без включения режима редактирования в моем UITableView?

Создание и добавление UIButton с по умолчанию function для перемещения также является опцией.

Ответ 1

Я действительно делаю что-то подобное для одного из моих приложений. Он использует методы делегата для редактирования таблицы и немного "обманывает" пользователя. 100% встроенных функциональных возможностей Apple.

1 - Задайте таблицу для редактирования (я делаю это в viewWillAppear)

-(void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    [self.tableView setEditing:YES];
}

2 - Скрыть значок аксессуара по умолчанию:

-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView 
        editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
        //remove any editing accessories (AKA) delete button 
        return UITableViewCellAccessoryNone;
       }

3 - Держите режим редактирования от перемещения всего вправо (в ячейке)

 -(BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath{
return NO;
}

4 - На этом этапе вы сможете перетаскивать ячейки, не выглядя так, как будто они находятся в режиме редактирования. Здесь мы обманываем пользователя. Создайте свой собственный значок "move" (три строки в случае по умолчанию, любой значок, который вы хотите в своем случае) и добавьте изображение вправо, где он обычно будет проходить по ячейке.

5 - Наконец, реализуем метод делегата, чтобы фактически изменить ваш базовый источник данных.

-(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{

    //Get original id
    NSMutableArray *originalArray = [self.model.items objectAtIndex:sourceIndexPath.section];
    Item * original = [originalArray objectAtIndex:sourceIndexPath.row];

    //Get destination id
    NSMutableArray *destinationArray = [self.model.items objectAtIndex:destinationIndexPath.section];
    Item * destination = [destinationArray objectAtIndex:destinationIndexPath.row];

    CGPoint temp = CGPointMake([original.section intValue], [original.row intValue]);

    original.row = destination.row;
    original.section = destination.section;

    destination.section = @(temp.x);
    destination.row = @(temp.y);

    //Put destination value in original array
    [originalArray replaceObjectAtIndex:sourceIndexPath.row withObject:destination];

    //put original value in destination array
    [destinationArray replaceObjectAtIndex:destinationIndexPath.row withObject:original];

    //reload tableview smoothly to reflect changes
    dispatch_async(dispatch_get_main_queue(), ^{
        [UIView transitionWithView:tableView duration:duration options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
            [tableView reloadData];
        } completion:NULL];
    });
 }

Ответ 2

Ответ Уильяма Фалькона в быстро

1 - Задайте таблицу для редактирования (я делаю это в viewWillAppear)

override func viewWillAppear(_ animated: Bool) {
   super.viewWillAppear(animated: animated)
   tableView.setEditing(true, animated: false)
}

2 - Скрыть значок аксессуара по умолчанию:

override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
    return .none
}

3 - Держите режим редактирования от перемещения всего вправо (в ячейке)

override func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
    return false
}

4 - Не требуется в быстрой 3

5 - Изменить порядок массива

Дополнительная заметка

Если вы хотите, чтобы ячейки вашей таблицы были выбраны, добавьте следующий код в функцию viewWillAppear().

tableView.allowsSelectionDuringEditing = true

Ответ 3

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

Я представляю довольно полное решение, которое позволяет пользователю перемещать строки вокруг видимой области UITableView. Он работает, находится ли режим UITableView в режиме редактирования или нет. Вам нужно будет расширить его, если вы хотите, чтобы таблица прокручивалась, когда строка перетаскивается вверху или внизу видимой области. Вероятно, некоторые неудачные и крайние случаи вам понадобятся для поиска.

@implementation TSTableViewController
{
    NSMutableArray* _dataSource;
}

- (void) viewDidLoad
{
    [super viewDidLoad];

    _dataSource = [NSMutableArray new];
    for ( int i = 0 ; i < 10 ; i++ )
    {
        [_dataSource addObject: [NSString stringWithFormat: @"cell %d", i]];
    }
}

- (void) longPress: (UILongPressGestureRecognizer*) lpgr
{
    static NSString* dragCellData = nil;
    static UIView*   dragCellView = nil;
    static NSInteger dragCellOffset = 0;

    // determine the cell we're hovering over, etc:
    CGPoint pt = [lpgr locationInView: self.tableView];
    NSIndexPath* ip = [self.tableView indexPathForRowAtPoint: pt];
    UITableViewCell* cell = [self.tableView cellForRowAtIndexPath: ip];
    CGPoint ptInCell = [lpgr locationInView: cell];

    // where the current placeholder cell is, if any:
    NSInteger placeholderIndex = [_dataSource indexOfObject: @"placeholder"];

    switch ( lpgr.state )
    {
        case UIGestureRecognizerStateBegan:
        {
            // get a snapshot-view of the cell we're going to drag:
            cell.selected = cell.highlighted = NO;
            dragCellView = [cell snapshotViewAfterScreenUpdates: YES];
            dragCellView.clipsToBounds       = NO;
            dragCellView.layer.shadowRadius  = 10;
            dragCellView.layer.shadowColor   = [UIColor blackColor].CGColor;
            dragCellView.layer.masksToBounds = NO;
            dragCellView.frame = [cell convertRect: cell.bounds
                                        toView: self.tableView.window];

            // used to position the dragCellView nicely:
            dragCellOffset = ptInCell.y;

            // the cell will be removed from the view hierarchy by the tableview, so transfer the gesture recognizer to our drag view, and add it into the view hierarchy:
            [dragCellView addGestureRecognizer: lpgr];
            [self.tableView.window addSubview: dragCellView];


            // swap out the cell for a placeholder:
            dragCellData = _dataSource[ip.row];
            _dataSource[ip.row] = @"placeholder";

            [self.tableView reloadRowsAtIndexPaths: @[ip]
                                  withRowAnimation: UITableViewRowAnimationNone];

            break;
        }

        case UIGestureRecognizerStateChanged:
        {
            // where should we move the placeholder to?
            NSInteger insertIndex = ptInCell.y < cell.bounds.size.height / 2.0 ? ip.row : ip.row + 1;
            if ( insertIndex != placeholderIndex )
            {
                // remove from the datasource and the tableview:
                [_dataSource removeObjectAtIndex: placeholderIndex];
                [self.tableView deleteRowsAtIndexPaths: @[ [NSIndexPath indexPathForRow: placeholderIndex inSection: 0] ]
                                      withRowAnimation: UITableViewRowAnimationFade];
                // adjust:
                if ( placeholderIndex < insertIndex )
                {
                    insertIndex--;
                }

                // insert to the datasource and tableview:
                [_dataSource insertObject: @"placeholder"
                                  atIndex: insertIndex];
                [self.tableView insertRowsAtIndexPaths: @[ [NSIndexPath indexPathForRow: insertIndex inSection: 0] ]
                                      withRowAnimation: UITableViewRowAnimationFade];
            }

            // move our dragCellView
            CGRect f = dragCellView.frame;
            f.origin.y = pt.y - dragCellOffset;
            dragCellView.frame = f;

            break;
        }

        case UIGestureRecognizerStateEnded:
        {
            // replace the placeholdercell with the cell we were dragging
            [_dataSource replaceObjectAtIndex: placeholderIndex
                                   withObject: dragCellData];
            [self.tableView reloadRowsAtIndexPaths: @[ [NSIndexPath indexPathForRow: placeholderIndex inSection: 0] ]
                                  withRowAnimation: UITableViewRowAnimationFade];

            // reset state
            [dragCellView removeFromSuperview];
            dragCellView = nil;
            dragCellData = nil;

            break;
        }

        default:
        {
            break;
        }
    }
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return _dataSource.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString* cellData = _dataSource[indexPath.row];
    if ( [cellData isEqualToString: @"placeholder" ] )
    {
        // an empty cell to denote where the "drop" would go
        return [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault
                                      reuseIdentifier: nil];
    }

    // a cell...
    UITableViewCell* cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault
                                                   reuseIdentifier: nil];

    // our "fake" move handle & gesture recognizer

    UILongPressGestureRecognizer* lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget: self action: @selector( longPress:) ];
    lpgr.minimumPressDuration = 0.3;

    UILabel* dragLabelView = [UILabel new];
    dragLabelView.text = @"☰";
    dragLabelView.userInteractionEnabled = YES;
    [dragLabelView addGestureRecognizer: lpgr];
    [dragLabelView sizeToFit];

    cell.textLabel.text = cellData;

    if ( tableView.isEditing )
    {
        cell.editingAccessoryView = dragLabelView;
    }
    else
    {
        cell.accessoryView = dragLabelView;
    }

    return cell;
}

@end