UITableViewCell, показать кнопку удаления при прокрутке

568

Как я могу получить кнопку удаления, чтобы показать при наведении на UITableViewCell? Событие никогда не вызывается и кнопка удаления никогда не появляется.

TheLearner
источник
1
Если вам нужен подробный и обновленный ответ, перейдите к stackoverflow.com/a/37719543/6872794
Munib
Смотрите мой ответ Swift 4 для аналогичного вопроса, который показывает до 3 различных способов создания пролистывания, чтобы удалить действия для UITableViewCells.
Imanou Petit
Я задал этот вопрос 8 лет назад ... пожалуйста, удалите этот вопрос, он сильно устарел. Свифта даже не было!
TheLearner
мы можем сделать фиксирование высоты для боковых кнопок смахивания? Например: моя ячейка 150, и я хочу, чтобы кнопка показывала только 50.0f, возможно ли это?
Сухас Арвинд Патил
это отлично работает на строках, но есть какие-нибудь подсказки о том, как интегрировать его в разделы?
Ледяная Скорбь

Ответы:

1035

Во время запуска в (-viewDidLoad or in storyboard)do:

self.tableView.allowsMultipleSelectionDuringEditing = NO;

Переопределить для поддержки условного редактирования табличного представления. Это нужно реализовать только в том случае, если вы собираетесь возвращаться NOза некоторыми предметами. По умолчанию все элементы доступны для редактирования.

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    // Return YES if you want the specified item to be editable.
    return YES;
}

// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        //add code here for when you hit delete
    }    
}
Kurbz
источник
94
Это работает, но ... - (BOOL) tableView: (UITableView *) tableView canEditRowAtIndexPath: (NSIndexPath *) indexPath ... необходимо реализовать только в том случае, если вы собираетесь возвращать NO для некоторых элементов. По умолчанию все элементы доступны для редактирования, поэтому вам не нужно реализовывать их, если вы всегда возвращаете YES.
Танос Дьякакис
24
Также важно знать: это методы UITableViewDataSource и NOT UITableViewDelegate.
Дейв Альберт
8
Хотите знать, как реализовать удаление ?
ma11hew28
12
Просто для ясности - вы ДОЛЖНЫ переопределить tableView: commitEditingStyle: forRowAtIndexPath: или жест смахивания не будет распознан, и при попытке удаления ничего не произойдет.
Крис
Это не сработало для меня (сначала). Мне также нужно было установить self.tableView.allowsMultipleSelectionDuringEditing = NO;левое движение для работы. Для меня это звучит как ошибка, потому что таблица НЕ находится в состоянии редактирования. Эта опция должна применяться только во время редактирования. Тем не менее, он работает сейчас, и я установил его в YES всякий раз, когда таблица переходит в состояние редактирования.
osxdirk
118

Этот ответ был обновлен до Swift 3

Я всегда думаю, что хорошо иметь очень простой, автономный пример, чтобы ничего не предполагалось, когда я изучал новое задание. Это ответ для удаления UITableViewстрок. Проект работает так:

введите описание изображения здесь

Этот проект основан на примере UITableView для Swift .

Добавить код

Создайте новый проект и замените код ViewController.swift следующим.

import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    // These strings will be the data for the table view cells
    var animals: [String] = ["Horse", "Cow", "Camel", "Pig", "Sheep", "Goat"]

    let cellReuseIdentifier = "cell"

    @IBOutlet var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // It is possible to do the following three things in the Interface Builder
        // rather than in code if you prefer.
        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)
        tableView.delegate = self
        tableView.dataSource = self
    }

    // number of rows in table view
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.animals.count
    }

    // create a cell for each table view row
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell:UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell!

        cell.textLabel?.text = self.animals[indexPath.row]

        return cell
    }

    // method to run when table view cell is tapped
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("You tapped cell number \(indexPath.row).")
    }

    // this method handles row deletion
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

        if editingStyle == .delete {

            // remove the item from the data model
            animals.remove(at: indexPath.row)

            // delete the table view row
            tableView.deleteRows(at: [indexPath], with: .fade)

        } else if editingStyle == .insert {
            // Not used in our example, but if you were adding a new row, this is where you would do it.
        }
    }

}

Метод с одним ключом в приведенном выше коде, который позволяет удалять строки, является последним. Здесь это снова для акцента:

// this method handles row deletion
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

    if editingStyle == .delete {

        // remove the item from the data model
        animals.remove(at: indexPath.row)

        // delete the table view row
        tableView.deleteRows(at: [indexPath], with: .fade)

    } else if editingStyle == .insert {
        // Not used in our example, but if you were adding a new row, this is where you would do it.
    }
}

Раскадровка

Добавьте UITableViewк View Controller в раскадровке. Используйте автоматическое расположение, чтобы прикрепить четыре стороны табличного представления к краям контроллера вида. Управляйте перетаскиванием из табличного представления в раскадровке на @IBOutlet var tableView: UITableView!строку в коде.

Законченный

Это все. Теперь вы сможете запустить свое приложение и удалить строки, проведя пальцем влево и нажав «Удалить».


вариации

Изменить текст кнопки «Удалить»

введите описание изображения здесь

Добавьте следующий метод:

func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String? {
    return "Erase"
}

Пользовательские действия кнопок

введите описание изображения здесь

Добавьте следующий метод.

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {

    // action one
    let editAction = UITableViewRowAction(style: .default, title: "Edit", handler: { (action, indexPath) in
        print("Edit tapped")
    })
    editAction.backgroundColor = UIColor.blue

    // action two
    let deleteAction = UITableViewRowAction(style: .default, title: "Delete", handler: { (action, indexPath) in
        print("Delete tapped")
    })
    deleteAction.backgroundColor = UIColor.red

    return [editAction, deleteAction]
}

Обратите внимание, что это доступно только в iOS 8. Смотрите этот ответ для более подробной информации.

Обновлено для iOS 11

Действия могут быть размещены в начале или в конце ячейки с помощью методов, добавленных в API UITableViewDelegate в iOS 11.

func tableView(_ tableView: UITableView,
                leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
 {
     let editAction = UIContextualAction(style: .normal, title:  "Edit", handler: { (ac:UIContextualAction, view:UIView, success:(Bool) -> Void) in
             success(true)
         })
editAction.backgroundColor = .blue

         return UISwipeActionsConfiguration(actions: [editAction])
 }

 func tableView(_ tableView: UITableView,
                trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
 {
     let deleteAction = UIContextualAction(style: .normal, title:  "Delete", handler: { (ac:UIContextualAction, view:UIView, success:(Bool) -> Void) in
         success(true)
     })
     deleteAction.backgroundColor = .red

     return UISwipeActionsConfiguration(actions: [deleteAction])
 }

дальнейшее чтение

Suragch
источник
спасибо за примеры и код. Теперь я готов реализовать функцию удаления. Подскажите, пожалуйста, какова цель строки "self.tableView.registerClass (...", которую вы добавили в viewDidLoad ()? И что эквивалентно этому в построителе интерфейса? Этого не было в примере с пользовательской ячейкой. Похоже, что мы указываем cellReuseIdentifier дважды сейчас. Спасибо!
Rockhammer
Если включить строку .registerClass, компиляция не удалась
rockhammer
@rockhammer, Вы правы, вам не нужно (по-видимому, нельзя) установить идентификатор повторного использования ячейки как в коде, так и в Интерфейсном Разработчике. Просто выберите один из способов в соответствии с вашими предпочтениями. Несмотря на то, что этот проект основан на том основные UITableViewодин , это совершенно самостоятельный проект , и вам не нужно ничего делать, не описанные здесь. Причина, по которой я начал устанавливать его в коде, заключается в том, что он требует меньше объяснений в моих ответах. Я должен вернуться и отредактировать основной пример, чтобы использовать код тоже.
Сурагч
Как можно осуществить правильный удар? Скажи, что левый удар «отклоняет» что-то, а правый удар «принимает» что-то в ячейке?
Munib
1
@ return0, насколько я знаю, функция смахивания вправо не встроена, поэтому вам придется создавать ее с нуля. В этой статье вы найдете идеи, с которых можно начать, если вы хотите попробовать. Тем не менее, я бы не советовал делать это, поскольку это не стандартное действие, которое ожидает пользователь. Скорее, я бы показал два варианта выбора кнопок влево, как в разделе действий с пользовательскими кнопками в моем ответе выше.
Сурагч
70

Этот код показывает, как реализовать удаление.

#pragma mark - UITableViewDataSource

// Swipe to delete.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        [_chats removeObjectAtIndex:indexPath.row];
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    }
}

При желании в переопределении инициализации добавьте строку ниже, чтобы показать элемент кнопки «Редактировать»:

self.navigationItem.leftBarButtonItem = self.editButtonItem;
ma11hew28
источник
Вы должны реализовать этот метод. Содержимое внутри должно соответствовать тому, что имеет смысл для вашего варианта использования. В приведенном выше коде _chats - это данные для представления таблицы. Как только пользователь нажимает «удалить», отдельный объект чата должен быть удален из _chat, чтобы источник данных отражал количество новых строк (в противном случае выбрасывается исключение).
ewcy
25

У меня была проблема, которую мне только что удалось решить, поэтому я делюсь ею, так как она может кому-то помочь.

У меня есть UITableView и добавил показанные методы, чтобы разрешить удаление салфетки:

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    // Return YES if you want the specified item to be editable.
    return YES;
}

// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        //add code here for when you hit delete
    }    
}

Я работаю над обновлением, которое позволяет мне перевести таблицу в режим редактирования и активирует множественный выбор. Для этого я добавил код из образца Apple TableMultiSelect . Как только я заработал эту работу, я обнаружил, что моя функция удаления перестала работать.

Оказывается, добавление следующей строки к viewDidLoad было проблемой:

self.tableView.allowsMultipleSelectionDuringEditing = YES;

С этой строкой мультиселект будет работать, но удаление не будет. Без линии все было наоборот.

Исправление:

Добавьте следующий метод в ваш viewController:

- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
    self.tableView.allowsMultipleSelectionDuringEditing = editing; 
    [super setEditing:editing animated:animated];
}

Затем в вашем методе, который переводит таблицу в режим редактирования (например, нажатием кнопки), вы должны использовать:

[self setEditing:YES animated:YES];

вместо:

[self.tableView setEditing:YES animated:YES];

Это означает, что множественный выбор включается только тогда, когда таблица находится в режиме редактирования.

Леон
источник
Это было полезно. Я установил allowMultipleSelection в раскадровке. Это исправило это.
Марк Суман
1
Это решило проблему, которая сводила нас с ума. Теперь я понимаю, что «смахивание для удаления» и «пакетное удаление в режиме редактирования» в основном являются взаимоисключающими, и вы должны контролировать это при входе в режим редактирования / ухода. Большое спасибо за исследование этого!
fbitterlich
18

Ниже UITableViewDataSource поможет вам для удаления

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    // Return YES if you want the specified item to be editable.
    return YES;
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        [arrYears removeObjectAtIndex:indexPath.row];
        [tableView reloadData];
    }
}

arrYears является NSMutableArray, а затем перезагрузите tableView

стриж

 func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
            return true
        }

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    if editingStyle == UITableViewCellEditingStyleDelete {
        arrYears.removeObjectAtIndex(indexPath.row)
        tableView.reloadData()
    }
}
iAnkit
источник
Но это UITableViewDataSource
HotJard
17

В iOS 8 и Swift 2.0 попробуйте это,

override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
   // let the controller to know that able to edit tableView's row 
   return true
}

override func tableView(tableView: UITableView, commitEdittingStyle editingStyle UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath)  {
   // if you want to apply with iOS 8 or earlier version you must add this function too. (just left in blank code)
}

override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]?  {
   // add the action button you want to show when swiping on tableView's cell , in this case add the delete button.
   let deleteAction = UITableViewRowAction(style: .Default, title: "Delete", handler: { (action , indexPath) -> Void in

   // Your delete code here.....
   .........
   .........
   })

   // You can set its properties like normal button
   deleteAction.backgroundColor = UIColor.redColor()

   return [deleteAction]
}
Маса С-Айя
источник
Это хороший ответ, с его помощью вы также можете настроить несколько действий.
Munib
11

Ответ @ Kurbz потрясающий, но я хочу оставить эту заметку и надеюсь, что этот ответ может спасти людей некоторое время.

У меня иногда были эти строки в моем контроллере, и они заставляли функцию свипирования не работать.

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
    return UITableViewCellEditingStyleNone; 
}

Если вы используете UITableViewCellEditingStyleInsertили UITableViewCellEditingStyleNoneкак стиль редактирования, то функция смахивания не работает. Вы можете использовать только UITableViewCellEditingStyleDeleteстиль по умолчанию.

Брайан
источник
1
В моем случае я хотел иметь возможность провести удаление, но также и иметь возможность перемещать свои ячейки. Подвижная ячейка также получает эту кнопку «удалить» в левой части ячейки, которая не вписывалась в мой дизайн, и для удаления этого стиля редактирования должен быть .none. Я решил это следующим образом: "если tableView.isEditing {return .none} else {return .delete}"
Спас мой Axz чувак. Спасибо :)
Сурав Чандра
9

Swift 4

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
    let delete = UITableViewRowAction(style: .destructive, title: "delete") { (action, indexPath) in
        // delete item at indexPath
    tableView.deleteRows(at: [indexPath], with: .fade)

    }
    return [delete]
}
Пратик Лад
источник
1
Хорошо, появляется вкладка удаления, но не удаляется при нажатии. Вам нужно удалить объект в источнике данных и перезагрузить таблицу да?
user3069232
да "// удалить элемент в indexPath" разместить логику вашей строки удаления на основе indexPath
Lad
8

Кроме того, это может быть достигнуто в SWIFT с использованием метода следующим

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    if (editingStyle == UITableViewCellEditingStyle.Delete){
        testArray.removeAtIndex(indexPath.row)
        goalsTableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
    }
}
DrPatience
источник
8

Свифт 3

Все, что вам нужно сделать, это включить эти две функции:

func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {

    return true

}

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

    if editingStyle == UITableViewCellEditingStyle.delete {
        tableView.reloadData()
    }

}
Мачадо
источник
7

Я знаю, это старый вопрос, но ответ @Kurbz просто нужен для Xcode 6.3.2 и SDK 8.3

Мне нужно добавить [tableView beginUpdates]и [tableView endUpdates](спасибо @ bay.phillips здесь )

// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle: (UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    // Open "Transaction"
    [tableView beginUpdates];

    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // your code goes here
        //add code here for when you hit delete
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
     }

    // Close "Transaction"
    [tableView endUpdates];
}
Бето
источник
6

Когда вы удаляете ячейку вашего табличного представления, вы также должны удалить объект массива с индексом x.

Я думаю, что вы можете удалить его, используя жест пальцем. Представление таблицы вызовет делегата:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        //add code here for when you hit delete
        [dataSourceArray removeObjectAtIndex:indexPath.row];
    }    
}

После удаления объекта. Вы должны перезагрузить использование таблицы. Добавьте следующую строку в ваш код:

[tableView reloadData];

после этого вы успешно удалили строку. И когда вы перезагрузите представление или добавите данные в DataSource, объект больше не будет там.

Для всего остального правильный ответ от Курбца.

Я только хотел напомнить вам, что функции делегата будет недостаточно, если вы хотите удалить объект из массива DataSource.

Я надеюсь, что помог вам.

Robybyte
источник
4
Вместо того, чтобы [tableView reloadData]звонить [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic].
ma11hew28
6
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath 
{
    if (editingStyle == UITableViewCellEditingStyleDelete)
    {
        //add code here for when you hit delete
        [dataSourceArray removeObjectAtIndex:indexPath.row];
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    }    
}    
Рахул К Раджан
источник
dataSourceArray - массив, из которого поступает содержимое ячейки
Rahul K Rajan
2

Swift 2.2:

override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
    return true
}

override func tableView(tableView: UITableView,
    editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
    let delete = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "DELETE"){(UITableViewRowAction,NSIndexPath) -> Void in

    print("Your action when user pressed delete")
}
let edit = UITableViewRowAction(style: UITableViewRowActionStyle.Normal, title: "EDIT"){(UITableViewRowAction,NSIndexPath) -> Void in

    print("Your action when user pressed edit")
}
    return [delete, block]
}
LCL
источник
2

Для Swift, просто напишите этот код

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        if editingStyle == .Delete {
            print("Delete Hit")
        }
}

Для Objective C, просто напишите этот код

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
       if (editingStyle == UITableViewCellEditingStyleDelete) {           
            NSLog(@"index: %@",indexPath.row);
           }
}
Киран Джасвани
источник
2

для кода swift4 сначала включите редактирование:

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

затем вы добавляете действие удаления к делегату редактирования:

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
    let action = UITableViewRowAction(style: .destructive, title: "Delete") { (_, index) in
        // delete model object at the index
        self.models[index.row]
        // then delete the cell
        tableView.beginUpdates()
        tableView.deleteRows(at: [index], with: .automatic)
        tableView.endUpdates()

    }
    return [action]
}
Abdoelrhman
источник
0

Swift 4,5

Чтобы удалить ячейку при пролистывании, есть два встроенных метода UITableView. Запишите этот метод в расширение DataSource TableView.

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        let delete = deleteProperty(at: indexPath)
        return UISwipeActionsConfiguration(actions: [delete])
    }

//Declare this method in Viewcontroller Main and modify according to your need

func deleteProperty(at indexpath: IndexPath) -> UIContextualAction {
        let action = UIContextualAction(style: .destructive, title: "Delete") { (action, view, completon) in
            self.yourArray.remove(at: indexpath) //Removing from array at selected index

            completon(true)
        action.backgroundColor = .red //cell background color
    }
        return action
    }
МАЛИКК ХАБИБ УР РЕМАН
источник
0

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

class DataSource: UITableViewDiffableDataSource<SectionType, ItemType> {

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

    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            if let identifierToDelete = itemIdentifier(for: indexPath) {
                var snapshot = self.snapshot()
                snapshot.deleteItems([identifierToDelete])
                apply(snapshot)
            }
        }
    }
}
Остин Конлон
источник