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

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

подвижная ячейка

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

Я попытался просто запустить 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}>

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

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


person Aleksander Azizi    schedule 03.04.2014    source источник
comment
Возможно, попробуйте метод из этой недавней статьи:raywenderlich.com/63089/   -  person Jack    schedule 04.04.2014
comment
Это может сработать, и я знаю, что этот метод возможен. Но мой вопрос в том, как включить его с кодом по умолчанию для перемещения ячеек.   -  person Aleksander Azizi    schedule 04.04.2014
comment
@AleksanderAzizi Могу я спросить, почему вы боитесь режима редактирования?   -  person Leo Natan    schedule 26.04.2014
comment
Но почему бы не представить табличное представление в режиме редактирования в первую очередь? Я скажу вам, почему я предлагаю это. Не касаясь частного API, невозможно использовать методы переупорядочивания Apple. Придется реализовывать самому или использовать опенсорс (есть в наличии).   -  person Leo Natan    schedule 26.04.2014
comment
Ответил, используя 100% методы делегата tableview. Я использую этот код в живом приложении.   -  person William Falcon    schedule 27.04.2014
comment
@LeoNatan Я думал, что это тоже может сработать, но смахивание по умолчанию (влево) для удаления затем отключено.   -  person Aleksander Azizi    schedule 27.04.2014


Ответы (5)


На самом деле я делаю что-то подобное для одного из своих приложений. Он использует методы делегата для редактирования таблицы и немного «обманывает» пользователя. 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 - На этом этапе вы должны иметь возможность перетаскивать ячейки, чтобы они не выглядели так, как будто они находятся в режиме редактирования. Здесь мы обманываем пользователя. Создайте свой собственный значок «переместить» (три строки в случае по умолчанию, любой значок, который вы хотите в вашем случае) и добавьте изображение прямо туда, где оно обычно находится в ячейке.

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];
    });
 }
person William Falcon    schedule 26.04.2014
comment
Я думал, что это тоже может работать, у меня точно такой же код, но смахивание по умолчанию (влево) для удаления отключено, когда UITableView находится в режиме редактирования. Итак, мой вопрос о том, как включить движение строки без использования режима редактирования. - person Aleksander Azizi; 27.04.2014
comment
Если вам нужно провести пальцем влево, включите его, используя другую реализацию. Посмотрите на мое приложение, которое использует это, под списком продуктов. Похоже, это именно то, что вы ищете. appstore.com/mealidea - person William Falcon; 27.04.2014
comment
Моя реализация смахивания здесь является базовой, но если вы встроите прокрутку в ячейку, вы можете получить тот же эффект, что и Apple. - person William Falcon; 27.04.2014
comment
Вы не можете редактировать таблицу, не переведя ее в режим редактирования! За исключением распознавателей жестов, скриншотов и ручного перемещения каждой ячейки, это настолько близко, насколько это возможно. - person William Falcon; 27.04.2014
comment
Кроме того, ваш вопрос касается только перемещения строк по умолчанию. В нем НЕТ упоминания о пролистывании для удаления ... - person William Falcon; 27.04.2014
comment
Очень хорошо, я добавил раздел к моему вопросу, который должен быть очевиден. В ответе, который вы дали, используется тот же самый код, который у меня уже есть, и это вызывает проблему компрометации других функций в UITableView, что делает его бесполезным в этом случае. - person Aleksander Azizi; 27.04.2014
comment
Срок действия награды истекает менее чем через 24 часа. Если вы хотите получить полную сумму вознаграждения (300), измените свой ответ, чтобы он заполнил without compromising the default behaviour of the UITableView. Если критерии не соблюдены, вам будет предоставлена ​​только половина (150), и ваш ответ не будет принят. - person Aleksander Azizi; 04.05.2014
comment
Я не знаю, что вы имеете в виду, не ставя под угрозу поведение UITableView по умолчанию. Я думаю, что этот код уже делает это... - person William Falcon; 04.05.2014
comment
compromising the default behaviour в данном случае относится к тому, как работает и ведет себя UITableView при обычном использовании. Добавляя свой код к существующему полностью рабочему UITableView, такие функции, как (включая, но не ограничиваясь) пролистывание для удаления/удаления, скомпрометированы (не работают). - person Aleksander Azizi; 04.05.2014
comment
@AleksanderAzizi - любопытно, почему вам не понравилось мое решение, которое не ставит под угрозу поведение табличного представления по умолчанию. - person TomSwift; 05.05.2014

Ответ Уильяма Фалькона в swift 3

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 - Не требуется в Swift 3

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

Дополнительное примечание

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

tableView.allowsSelectionDuringEditing = true
person James Parker    schedule 25.02.2017

Для Swift 5... Я знаю, что вопрос задается БЕЗ редактирования, но если вам нужны только функциональные возможности

  1. иметь возможность переупорядочивать ячейки и
  2. иметь возможность выделять ячейки

Затем вы можете следовать этому (источник для изменения порядка: https://www.ralfebert.de/ios-examples/uikit/uitableviewcontroller/reorderable-cells/):

  1. установить tableView.isEditing = true
  2. установить tableView.allowsSelectionDuringEditing = true
  3. С помощью UITableViewDataSource добавьте следующие методы:
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
    return .none
}

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

Вам может потребоваться переопределить эти функции, а может и не потребоваться. Мне не нужно было.

  1. (я думаю) в UITableViewDelegate добавьте это:
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
    let movedObject = items[sourceIndexPath.row]
    items.remove(at: sourceIndexPath.row)
    items.insert(movedObject, at: destinationIndexPath.row)
}

где items — это массив источников данных, который обрабатывает количество элементов в табличном представлении.

Ключевым моментом является шаг 2, на котором вы, наконец, можете добавить метод func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath).

person acw    schedule 29.03.2020

Если вы не хотите переводить 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
person TomSwift    schedule 02.05.2014

Чтобы заставить ячейки двигаться, вы должны реализовать соответствующие методы <UITableViewDelegate> для перемещения и явного разрешения перемещения ячейки (indexpath). Значок индикатора перемещения не будет отображаться, поскольку это зависит от режима редактирования. Пока табличное представление находится в состоянии редактирования, значок перемещения будет перекрывать значок аксессуара. Режим редактирования по умолчанию имеет круглую кнопку удаления слева, которую вы можете скрыть во время редактирования.

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (self.editing) return UITableViewCellEditingStyleNone;
    return UITableViewCellEditingStyleDelete;
}

Таким образом, вы сохраняете свой класс UITableView с методами UITableViewDelegate чистыми для понимания, вам не нужно реализовывать какой-то самодельный режим перемещения. И вы можете использовать свойство редактирования таблицы, чтобы определить, как должна выглядеть ячейка в вашем методе tableView:cellForRowAtIndexPath:. Таким образом, даже отключить функцию перемещения проще, вернув для просмотра таблицы значение editing = NO, значок перемещения исчезнет, ​​а символ аксессуара появится снова.

person Ol Sen    schedule 16.01.2020