Проведіть пальцем до Видалення та кнопку "Докладніше" (наприклад, у додатку Mail на iOS 7)


246

Як створити кнопку "більше", коли користувач проведіть по комірці у вигляді таблиці (як-от поштовий додаток у ios 7)

Я шукав цю інформацію і тут, і на форумі Cocoa Touch, але я не можу знайти відповідь, і сподіваюся, що хтось розумніший, ніж я, може дати мені рішення.

Я хотів би, щоб користувач провела пальцем по комірці перегляду таблиці, щоб відобразити більше однієї кнопки редагування (за замовчуванням - кнопка видалення). У додатку Mail для iOS 7 ви можете провести пальцем, щоб видалити, але відображається кнопка "БІЛЬШЕ".

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



Щоб додати кнопку "Видалити", я реалізую наступні дві функції. - (BOOL) tableView: (UITableView *) tableView canEditRowAtIndexPath: (NSIndexPath *) indexPath; - (void) tableView: (UITableView *) tableView commitEditingStyle: (UITableViewCellEditingStyle) editingStyle forRowAtIndexPath: (NSIndexPath *) indexPath; І я хочу додати кнопку "Більше" поруч.
Гай Калон

3
@MonishBansal Bansal Схоже, хтось із цієї теми ( devforums.apple.com/message/860459#860459 на форумі розробників Apple) пішов наперед і створив власну реалізацію. Ви можете знайти проект, який робить все, що ви хочете, на GitHub: github.com/daria-kopaliani/DAContextMenuTableViewController
Гай Калон

8
@GuyKahlonMatrix дякує за рішення, яке працює як шарм. Це питання є результатом №1 у багатьох пошукових запитах Google, і люди змушені обмінюватися своїми знаннями, використовуючи коментарі, тому що хтось вирішив, що корисніше закрити питання та замість цього проповідувати демократію. Це місце явно потребує кращих мод.
Şafak Gezer

2
Якщо ви можете націлити на iOS 8, моя відповідь нижче буде тим, що ви хочете.
Джонні

Відповіді:


126

Як здійснити

Схоже, iOS 8 відкриває цей API. Підказки щодо такої функціональності є в Beta 2.

Щоб щось працювало, реалізуйте наступні два способи на своєму делегаті UITableView, щоб отримати бажаний ефект (див. Приклад суті).

- tableView:editActionsForRowAtIndexPath:
- tableView:commitEditingStyle:forRowAtIndexPath:


відомі проблеми

У документації йдеться про tableView: commitEditingStyle: forRowAtIndexPath:

"Не закликається редагувати дії за допомогою UITableViewRowAction - замість цього буде викликано обробник дії."

Однак пробіг не працює без нього. Навіть якщо заглушка методу порожня, поки що вона потрібна. Це, очевидно, помилка в бета-2.


Джерела

https://twitter.com/marksands/status/481642991745265664 https://gist.github.com/marksands/76558707f583dbb8f870

Оригінальна відповідь: https://stackoverflow.com/a/24540538/870028


Оновлення:

Приклад коду з цим робочим (У Swift): http://dropbox.com/s/0fvxosft2mq2v5m/DeleteRowExampleSwift.zip

Зразок коду містить цей простий у дотриманні метод у MasterViewController.swift, і тільки за допомогою цього методу ви отримуєте поведінку, показану на екрані OP:

override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [AnyObject]? {

    var moreRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "More", handler:{action, indexpath in
        println("MORE•ACTION");
    });
    moreRowAction.backgroundColor = UIColor(red: 0.298, green: 0.851, blue: 0.3922, alpha: 1.0);

    var deleteRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Delete", handler:{action, indexpath in
        println("DELETE•ACTION");
    });

    return [deleteRowAction, moreRowAction];
}

1
Це здається правильним, але в Xcode 6 GM жест пальцем, здається, не працює. До редагування дій можна отримати доступ, перевівши таблицю в режим редагування. Хтось ще знайде, що проведіть пальцем не працює?
Siegfoult

@Siegfoult Ви спробували реалізувати (навіть якщо він порожній) tableView: commitEditingStyle: forRowAtIndexPath :?
Джонні

Я не працюю в об'єктивному с .. Цей же код я написав. будь ласка, підкажіть кілька підказок.
Твердий м'який

@SolidSoft У вас є приклад проекту, який я міг би переглянути? Можливо, я міг би допомогти краще таким чином.
Джонні

3
Щоб відповісти на мій власний коментар. Ви зателефонуєте tableView.editing = false( NOв objc) і комірка "закриється".
Ben Lachman

121

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

https://github.com/MortimerGoro/MGSwipeTableCell

Ця бібліотека сумісна з усіма різними способами створення UITableViewCell та її тестуванням на iOS 5, iOS 6, iOS 7 та iOS 8.

Ось зразок деяких переходів:

Перехід кордону:

Перехід кордону

Перехід кліпу

Перехід кліпу

3D перехід:

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


1
Чудова робота! Було б чудово мати зворотні дзвінки для налаштування анімації.
Паку

1
@MortimerGoro Хороший робочий чоловік. Виглядає добре. Я намагаюся реалізувати подібний ефект в одному зі своїх проектів Android. Скажіть, будь ласка, як я можу досягти цього в Android?
Нітеш Кумар

на iOS 8 + iPad, я просто не переходжу її.
ScorpionKing2k5

Це дивовижна бібліотека, і що дуже добре, що вона має підтримку.
confile

@MortimerGoro, я спробував з "MGSwipeTableCel" l рамкою, але проблема полягає в тому, коли я перезавантажую свою таблицю, тоді проведіть пальцем кнопку. Будь-яка робота з цього питання.
Ганеш Гутурі

71

Відповідь Джонні - це правильна пропозиція. Я лише додаю це нижче в aim-c, щоб зрозуміти початківцям (і тим, хто відмовляється вивчати синтаксис Swift :)

Переконайтеся, що ви оголосили uitableviewdelegate та використовуєте наступні методи:

 -(NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
 UITableViewRowAction *button = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"Button 1" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath)
    {
        NSLog(@"Action to perform with Button 1");
    }];
    button.backgroundColor = [UIColor greenColor]; //arbitrary color
    UITableViewRowAction *button2 = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"Button 2" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath)
                                    {
                                        NSLog(@"Action to perform with Button2!");
                                    }];
    button2.backgroundColor = [UIColor blueColor]; //arbitrary color

    return @[button, button2]; //array with all the buttons you want. 1,2,3, etc...
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
// you need to implement this method too or nothing will work:

}
 - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
    {
        return YES; //tableview must be editable or nothing will work...
    }

1
Важливо відзначити canEditRowAtIndexPath
Heckscheibe

Якщо я перезавантажую таблицю після проведення клітинки, то ці кнопки пальця видно чи приховано?
Ганеш Гутурі

25

Це (досить смішно) приватний API.

Наступні два способи є приватними та надсилаються делегату UITableView:

-(NSString *)tableView:(UITableView *)tableView titleForSwipeAccessoryButtonForRowAtIndexPath:(NSIndexPath *)indexPath;
-(void)tableView:(UITableView *)tableView swipeAccessoryButtonPushedForRowAtIndexPath:(NSIndexPath *)indexPath;

Вони досить пояснюють себе.


4
Apple відкрила цю функцію за допомогою iOS 8. Дивіться відповідь Джонні нижче.
Siegfoult

24

Щоб покращити відповідь Джонні, тепер це можна зробити за допомогою публічного API таким чином:

func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {

    let moreRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: "More", handler:{action, indexpath in
        print("MORE•ACTION");
    });
    moreRowAction.backgroundColor = UIColor(red: 0.298, green: 0.851, blue: 0.3922, alpha: 1.0);

    let deleteRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: "Delete", handler:{action, indexpath in
        print("DELETE•ACTION");
    });

    return [deleteRowAction, moreRowAction];
}

17

Я сподіваюся, що ви не можете чекати, поки яблуко не дасть вам того, що вам потрібно? Тож ось мій варіант.

Створіть власну клітинку. Майте в ній два uiview

1. upper
2. lower

Знизу додайте потрібні кнопки. Робіть свої дії, як і будь-які інші МБА. ви можете визначити час анімації, стиль та будь-що.

тепер додайте uiswipeureure у верхній вигляд і розкрийте нижній погляд на жесті пальцем. Я робив це раніше і найпростіший варіант, що стосується мене.

Сподіваюся, що допоможе.


7

Це неможливо за допомогою стандартного SDK. Однак існують різні сторонні рішення, які більш-менш імітують поведінку в Mail.app. Деякі з них (наприклад, MCSwipeTableViewCell , DAContextMenuTableViewController , RMSwipeTableViewCell ) виявляють перетягування за допомогою розпізнавачів жестів, деякі з них (наприклад, SWTableViewCell ) ставлять другий UISScrollView нижче стандартного UITableViewCellScrollView(приватний підгляд UITableViewCell) та деякі з них змінюють поведінку UITableViewCellScrollView.

Останній підхід мені найбільше подобається, оскільки керування на дотик відчуває себе найбільш природно. Зокрема, MSCMoreOptionTableViewCell хороший. Ваш вибір може відрізнятися залежно від ваших конкретних потреб (чи потрібна вам також панель зліва направо, чи потрібна сумісність iOS 6 тощо). Також слід пам’ятати, що більшість із цих підходів постає з тягарем: вони можуть легко зламатись у майбутній версії iOS, якщо Apple UITableViewCellвнесе зміни в ієрархії підгляду.


7

Код версії Swift 3 без використання бібліотеки:

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        tableView.tableFooterView = UIView(frame: CGRect.zero) //Hiding blank cells.
        tableView.separatorInset = UIEdgeInsets.zero
        tableView.dataSource = self
        tableView.delegate = self
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return 4
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath)

        return cell
    }

    //Enable cell editing methods.
    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {

        return true
    }

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

    }

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

        let more = UITableViewRowAction(style: .normal, title: "More") { action, index in
            //self.isEditing = false
            print("more button tapped")
        }
        more.backgroundColor = UIColor.lightGray

        let favorite = UITableViewRowAction(style: .normal, title: "Favorite") { action, index in
            //self.isEditing = false
            print("favorite button tapped")
        }
        favorite.backgroundColor = UIColor.orange

        let share = UITableViewRowAction(style: .normal, title: "Share") { action, index in
            //self.isEditing = false
            print("share button tapped")
        }
        share.backgroundColor = UIColor.blue

        return [share, favorite, more]
    }

}

6

Вам потрібно UITableViewCellметод підкласу та підкласу, willTransitionToState:(UITableViewCellStateMask)stateякий викликається щоразу, коли користувач провела клітинку. Thestate прапори будуть перешкоджати вам знати , якщо кнопка Delete показує, і показати / вашу кнопку Більше там сховатися.

На жаль, цей метод не дає ні ширини кнопки «Видалити», ні часу анімації. Тож вам потрібно спостерігати за жорстким кодом рамки кнопки «Додатково» та часу анімації у свій код (я особисто думаю, що Apple має щось із цим зробити).


7
"Я особисто думаю, що Apple має щось з цим зробити". Я згоден. Ви вже писали їм звіт про помилку / запит про функції?
Tafkadasoh

4

Для швидкого програмування

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
  if editingStyle == UITableViewCellEditingStyle.Delete {
    deleteModelAt(indexPath.row)
    self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
  }
  else if editingStyle == UITableViewCellEditingStyle.Insert {
    println("insert editing action")
  }
}

func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [AnyObject]? {
  var archiveAction = UITableViewRowAction(style: .Default, title: "Archive",handler: { (action: UITableViewRowAction!, indexPath: NSIndexPath!) in
        // maybe show an action sheet with more options
        self.tableView.setEditing(false, animated: false)
      }
  )
  archiveAction.backgroundColor = UIColor.lightGrayColor()

  var deleteAction = UITableViewRowAction(style: .Normal, title: "Delete",
      handler: { (action: UITableViewRowAction!, indexPath: NSIndexPath!) in
        self.deleteModelAt(indexPath.row)
        self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic);
      }
  );
  deleteAction.backgroundColor = UIColor.redColor()

  return [deleteAction, archiveAction]
}

func deleteModelAt(index: Int) {
  //... delete logic for model
}

@bibscy ви можете запропонувати зміни. Давно не користувався стрімко, тому не впевнений, що правильний синтаксис
Михайло Ягудаєв

3

ЦЕ МОЖЕ ДОПОМОГАТИ ВАМ.

-(NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
 UITableViewRowAction *button = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"Button 1" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath)
    {
        NSLog(@"Action to perform with Button 1");
    }];
    button.backgroundColor = [UIColor greenColor]; //arbitrary color
    UITableViewRowAction *button2 = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"Button 2" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath)
                                    {
                                        NSLog(@"Action to perform with Button2!");
                                    }];
    button2.backgroundColor = [UIColor blueColor]; //arbitrary color

    return @[button, button2]; //array with all the buttons you want. 1,2,3, etc...
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
// you need to implement this method too or nothing will work:

}
 - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
    {
        return YES; //tableview must be editable or nothing will work...
    }

3

Я шукав, щоб додати ту саму функціональність до свого додатка, і, пройшовши стільки різних навчальних посібників ( raywenderlich є найкращим рішенням для самостійної роботи), я з’ясував, що Apple має свій UITableViewRowActionклас, що дуже зручно.

Ви повинні змінити метод бойлера Tableview на такий:

override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [AnyObject]?  {
    // 1   
    var shareAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Share" , handler: { (action:UITableViewRowAction!, indexPath:NSIndexPath!) -> Void in
    // 2
    let shareMenu = UIAlertController(title: nil, message: "Share using", preferredStyle: .ActionSheet)

    let twitterAction = UIAlertAction(title: "Twitter", style: UIAlertActionStyle.Default, handler: nil)
    let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil)

    shareMenu.addAction(twitterAction)
    shareMenu.addAction(cancelAction)


    self.presentViewController(shareMenu, animated: true, completion: nil)
    })
    // 3
    var rateAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Rate" , handler: { (action:UITableViewRowAction!, indexPath:NSIndexPath!) -> Void in
    // 4
    let rateMenu = UIAlertController(title: nil, message: "Rate this App", preferredStyle: .ActionSheet)

    let appRateAction = UIAlertAction(title: "Rate", style: UIAlertActionStyle.Default, handler: nil)
    let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil)

    rateMenu.addAction(appRateAction)
    rateMenu.addAction(cancelAction)


    self.presentViewController(rateMenu, animated: true, completion: nil)
    })
    // 5
    return [shareAction,rateAction]
  }

Ви можете дізнатися більше про це на цьому веб-сайті . Власна документація Apple дуже корисна для зміни кольору фону:

Колір фону кнопки дії.

Декларація OBJECTIVE-C @property (неатомічна, копія) UIColor * backgroundColor Обговорення За допомогою цього властивості можна вказати колір фону для вашої кнопки. Якщо ви не вказали значення для цього властивості, UIKit призначає колір за замовчуванням на основі значення у властивості стилю.

Доступність Доступно в iOS 8.0 та новіших версіях.

Якщо ви хочете змінити шрифт кнопки, це трохи складніше. Я бачив ще один пост на SO. Для надання коду, а також посилання, ось код, який вони там використовували. Вам доведеться змінити зовнішній вигляд кнопки. Вам доведеться конкретно посилатися на tableviewcell, інакше ви змінили б вигляд кнопки в усьому додатку (я цього не хотів, але ви можете, я не знаю :))

Мета C:

+ (void)setupDeleteRowActionStyleForUserCell {

    UIFont *font = [UIFont fontWithName:@"AvenirNext-Regular" size:19];

    NSDictionary *attributes = @{NSFontAttributeName: font,
                      NSForegroundColorAttributeName: [UIColor whiteColor]};

    NSAttributedString *attributedTitle = [[NSAttributedString alloc] initWithString: @"DELETE"
                                                                          attributes: attributes];

    /*
     * We include UIView in the containment hierarchy because there is another button in UserCell that is a direct descendant of UserCell that we don't want this to affect.
     */
    [[UIButton appearanceWhenContainedIn:[UIView class], [UserCell class], nil] setAttributedTitle: attributedTitle
                                                                                          forState: UIControlStateNormal];
}

Швидкий:

    //create your attributes however you want to
    let attributes = [NSFontAttributeName: UIFont.systemFontOfSize(UIFont.systemFontSize())] as Dictionary!            

   //Add more view controller types in the []
    UIButton.appearanceWhenContainedInInstancesOfClasses([ViewController.self])

Це найпростіша та найпоточніша версія IMHO. Сподіваюся, це допомагає.

Оновлення: Ось версія Swift 3.0:

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
    var shareAction:UITableViewRowAction = UITableViewRowAction(style: .default, title: "Share", handler: {(action, cellIndexpath) -> Void in
        let shareMenu = UIAlertController(title: nil, message: "Share using", preferredStyle: .actionSheet)

        let twitterAction = UIAlertAction(title: "Twitter", style: .default, handler: nil)
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)

        shareMenu.addAction(twitterAction)
        shareMenu.addAction(cancelAction)


        self.present(shareMenu,animated: true, completion: nil)
    })

    var rateAction:UITableViewRowAction = UITableViewRowAction(style: .default, title: "Rate" , handler: {(action, cellIndexpath) -> Void in
        // 4
        let rateMenu = UIAlertController(title: nil, message: "Rate this App", preferredStyle: .actionSheet)

        let appRateAction = UIAlertAction(title: "Rate", style: .default, handler: nil)
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)

        rateMenu.addAction(appRateAction)
        rateMenu.addAction(cancelAction)


        self.present(rateMenu, animated: true, completion: nil)
    })
    // 5
    return [shareAction,rateAction]
}

1
Дякую за вашу відповідь, я впевнений, що це допоможе багатьом розробникам. Так, ви маєте рацію, насправді Apple пропонує це рішення з iOS 8. Але, на жаль, це нативне рішення не забезпечує повного функціоналу. Наприклад, у додатку Apple Mail ви маєте кнопки з двох сторін (одна кнопка з лівої сторони та три з правої сторони). У поточному API від Apple ви не можете додавати кнопки в обидві сторони, а також поточний API не підтримує дію за замовчуванням, коли користувач довго переводить пальцем на кожну сторону. Найкраще рішення на даний момент IMHO - це відкритий код MGSwipeTableCell.
Гай Калон

@GuyKahlon так, ви абсолютно праві, що стосується проблеми правого і правого пальця, і я погоджуюся, що для більшої налаштування найкращим є MGSwipeTableCell. Apple не є найскладнішим варіантом, але я вважаю, що це найпростіший варіант для простих завдань.
Септроник

@Septronic Не могли б ви оновити свій код до Swift 3? shareMenu.не приймає addActionметод. Дякую
химерність

@bibscy Я додав швидку версію. Вам також потрібен біт для атрибута? sharemenu - це лише UIAlertController, тому він повинен вжити заходів. Спробуйте і дайте мені знати, чи є удача :)
Septronic

3

Фактична швидка відповідь 3

Це ТІЛЬКА функція, яка вам потрібна. Вам не потрібні функції CanEdit або CommitEditingStyle для користувацьких дій.

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
    let action1 = UITableViewRowAction(style: .default, title: "Action1", handler: {
        (action, indexPath) in
        print("Action1")
    })
    action1.backgroundColor = UIColor.lightGray
    let action2 = UITableViewRowAction(style: .default, title: "Action2", handler: {
        (action, indexPath) in
        print("Action2")
    })
    return [action1, action2]
}

3

Станом на iOS 11 це доступне для всіх UITableViewDelegate. Ось приклад коду:

- (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath {
    UIContextualAction *delete = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive
                                                                         title:@"DELETE"
                                                                       handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
                                                                           NSLog(@"index path of delete: %@", indexPath);
                                                                           completionHandler(YES);
                                                                       }];

    UIContextualAction *rename = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal
                                                                         title:@"RENAME"
                                                                       handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
                                                                           NSLog(@"index path of rename: %@", indexPath);
                                                                           completionHandler(YES);
                                                                       }];

    UISwipeActionsConfiguration *swipeActionConfig = [UISwipeActionsConfiguration configurationWithActions:@[rename, delete]];
    swipeActionConfig.performsFirstActionWithFullSwipe = NO;

    return swipeActionConfig;
}

Також доступні:

- (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView leadingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath;

Документи: https://developer.apple.com/documentation/uikit/uitableviewdelegate/2902367-tableview?language=objc


3

Swift 4 та iOs 11+

@available(iOS 11.0, *)
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {

    let delete = UIContextualAction(style: .destructive, title: "Delete") { _, _, handler in

        handler(true)
        // handle deletion here
    }

    let more = UIContextualAction(style: .normal, title: "More") { _, _, handler in

        handler(true)
        // handle more here
    }

    return UISwipeActionsConfiguration(actions: [delete, more])
}

2

Я використовував tableViewCell для показу декількох даних, після проведення пальцем праворуч ліворуч на комірці буде показано дві кнопки «Підтвердити і відхилити», є два методи: перший - ApproveFunc, який бере один аргумент, а другий - RejectFunc, який також бере один аргумент.

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

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
        let Approve = UITableViewRowAction(style: .normal, title: "Approve") { action, index in

            self.ApproveFunc(indexPath: indexPath)
        }
        Approve.backgroundColor = .green

        let Reject = UITableViewRowAction(style: .normal, title: "Reject") { action, index in

            self.rejectFunc(indexPath: indexPath)
        }
        Reject.backgroundColor = .red



        return [Reject, Approve]
    }

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

    func ApproveFunc(indexPath: IndexPath) {
        print(indexPath.row)
    }
    func rejectFunc(indexPath: IndexPath) {
        print(indexPath.row)
    }

Чи можете ви додати відповідь до своєї відповіді, щоб читач міг навчитися з неї?
Ніко Хааз

Дякуємо за цей фрагмент коду, який може надати деяку обмежену, негайну допомогу. Належне пояснення було б значно поліпшити свою довгострокову цінність , показуючи , чому це хороше рішення проблеми, і зробить його більш корисним для читачів майбутніх з іншими подібними питаннями. Будь ласка, відредагуйте свою відповідь, щоб додати пояснення, включаючи зроблені вами припущення.
Тім Дікманн

1

Ось дещо крихкий спосіб зробити це, що не передбачає приватних API та побудови власної системи. Ви хеджуєте свої ставки, що Apple цього не порушує, і, сподіваємось, вони випустять API, на який можна замінити ці кілька рядків коду.

  1. KVO self.contentView.superview.layer.sublayer. Робіть це init. Це шар UIScrollView. Ви не можете "підзагляди" KVO.
  2. Коли субпідзміни змінюються, знайдіть подання підтвердження видалення в scrollview.subviews. Це робиться в режимі зворотного дзвінка спостереження.
  3. Подвойте розмір цього виду і додайте UIButton зліва від його єдиного підпогляду. Це також робиться в зворотному режимі спостереження. Єдиним підвидом перегляду підтвердження видалення є кнопка видалення.
  4. (необов’язково) Подія UIButton повинна шукати self.superview, поки не знайде UITableView, а потім викликає джерело даних або метод делегування, створений вами, наприклад tableView: commitCustomEditingStyle: forRowAtIndexPath :. Ви можете знайти indexPath комірки, використовуючи [tableView indexPathForCell: self].

Це також вимагає, щоб ви реалізували стандартний режим редагування подання таблиці делегації зворотних викликів.

static char kObserveContext = 0;

@implementation KZTableViewCell {
    UIScrollView *_contentScrollView;
    UIView *_confirmationView;
    UIButton *_editButton;
    UIButton *_deleteButton;
}

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        _contentScrollView = (id)self.contentView.superview;

        [_contentScrollView.layer addObserver:self
             forKeyPath:@"sublayers"
                options:0
                context:&kObserveContext];

        _editButton = [UIButton new];
        _editButton.backgroundColor = [UIColor lightGrayColor];
        [_editButton setTitle:@"Edit" forState:UIControlStateNormal];
        [_editButton addTarget:self
                        action:@selector(_editTap)
              forControlEvents:UIControlEventTouchUpInside];

    }
    return self;
}

-(void)dealloc {
    [_contentScrollView.layer removeObserver:self forKeyPath:@"sublayers" context:&kObserveContext];
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if(context != &kObserveContext) {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
        return;
    }
    if(object == _contentScrollView.layer) {
        for(UIView * view in _contentScrollView.subviews) {
            if([NSStringFromClass(view.class) hasSuffix:@"ConfirmationView"]) {
                _confirmationView = view;
                _deleteButton = [view.subviews objectAtIndex:0];
                CGRect frame = _confirmationView.frame;
                CGRect frame2 = frame;
                frame.origin.x -= frame.size.width;
                frame.size.width *= 2;
                _confirmationView.frame = frame;

                frame2.origin = CGPointZero;
                _editButton.frame = frame2;
                frame2.origin.x += frame2.size.width;
                _deleteButton.frame = frame2;
                [_confirmationView addSubview:_editButton];
                break;
            }
        }
        return;
    }
}

-(void)_editTap {
    UITableView *tv = (id)self.superview;
    while(tv && ![tv isKindOfClass:[UITableView class]]) {
        tv = (id)tv.superview;
    }
    id<UITableViewDelegate> delegate = tv.delegate;
    if([delegate respondsToSelector:@selector(tableView:editTappedForRowWithIndexPath:)]) {
        NSIndexPath *ip = [tv indexPathForCell:self];
        // define this in your own protocol
        [delegate tableView:tv editTappedForRowWithIndexPath:ip];
    }
}
@end

Я дуже радий, якщо ви можете надати зразок коду, Дякую
Хлопець Калон

Зроблено. Може виникнути помилка або два, але ви отримаєте суть.
xtravar

1

Існує дивовижна бібліотека, яка називається SwipeCellKit, вона має отримати більше визнання. На мою думку, це крутіше, ніж MGSwipeTableCell. Останнє не повністю повторює поведінку комірок програми Mail, тоді як SwipeCellKit. Подивитися


Я спробував SwipeCellKitі був вражений ... доки я не отримав один із цих винятків, оскільки кількість рядків перед оновленням таблиці не було таким, як після оновлення +/- зміни рядків. Справа в тому, що я ніколи не змінював свій набір даних. Тож якщо це не хвилює, я не знаю, що таке. Тому я вирішив проти його використання і просто використав нові методи UITableViewDelegate. Якщо вам потрібно більше налаштування, ви завжди можете перекритиwillBeginEditingRowAt: ....
підкова7

@ підкова7 це дивно. Я ніколи не стикався з будь-яким винятком при використанні SwipeCellKit. Зрештою, яке відношення може мати клітина до такого винятку, що відбувається через зміни джерела даних?
Андрій Чернуха

1

Швидкий 4

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    let delete = UIContextualAction(style: .destructive, title: "Delete") { (action, sourceView, completionHandler) in
        print("index path of delete: \(indexPath)")
        completionHandler(true)
    }
    let rename = UIContextualAction(style: .normal, title: "Edit") { (action, sourceView, completionHandler) in
        print("index path of edit: \(indexPath)")
        completionHandler(true)
    }
    let swipeActionConfig = UISwipeActionsConfiguration(actions: [rename, delete])
    swipeActionConfig.performsFirstActionWithFullSwipe = false
    return swipeActionConfig
}

що таке вигляд джерела у кодах? це значок чи зображення?
Саїд Рахматолахі

1
@SaeedRahmatolahi - sourceViewце "Вид, в якому відображалася дія". Для отримання додаткової інформації шукайте "UIContextualAction.Handler".
Марк Мойкенс

0

Ось одне просте рішення. Він здатний відображати та приховувати власні UIView всередині UITableViewCell. Логіка відображення міститься всередині класу, розширеного від UITableViewCell, BaseTableViewCell.

BaseTableViewCell.h

#import <UIKit/UIKit.h>

@interface BaseTableViewCell : UITableViewCell

@property(nonatomic,strong)UIView* customView;

-(void)showCustomView;

-(void)hideCustomView;

@end

BaseTableViewCell.M

#import "BaseTableViewCell.h"

@interface BaseTableViewCell()
{
    BOOL _isCustomViewVisible;
}

@end

@implementation BaseTableViewCell

- (void)awakeFromNib {
    // Initialization code
}

-(void)prepareForReuse
{
    self.customView = nil;
    _isCustomViewVisible = NO;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

-(void)showCustomView
{
    if(nil != self.customView)
    {
        if(!_isCustomViewVisible)
        {
            _isCustomViewVisible = YES;

            if(!self.customView.superview)
            {
                CGRect frame = self.customView.frame;
                frame.origin.x = self.contentView.frame.size.width;
                self.customView.frame = frame;
                [self.customView willMoveToSuperview:self.contentView];
                [self.contentView addSubview:self.customView];
                [self.customView didMoveToSuperview];
            }

            __weak BaseTableViewCell* blockSelf = self;
            [UIView animateWithDuration:.5 animations:^(){

                for(UIView* view in blockSelf.contentView.subviews)
                {
                    CGRect frame = view.frame;
                    frame.origin.x = frame.origin.x - blockSelf.customView.frame.size.width;
                    view.frame = frame;
                }
            }];
        }
    }
}

-(void)hideCustomView
{
    if(nil != self.customView)
    {
        if(_isCustomViewVisible)
        {
            __weak BaseTableViewCell* blockSelf = self;
            _isCustomViewVisible = NO;
            [UIView animateWithDuration:.5 animations:^(){
                for(UIView* view in blockSelf.contentView.subviews)
                {
                    CGRect frame = view.frame;
                    frame.origin.x = frame.origin.x + blockSelf.customView.frame.size.width;
                    view.frame = frame;
                }
            }];
        }
    }
}

@end

Щоб отримати цю функціональність, просто розгорніть клітинку подання таблиці з BaseTableViewCell.

Далі, всередині UIViewController, які реалізують UITableViewDelegate, створюють два розпізнавача жестів для обробки пальців ліворуч та праворуч.

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    [self.tableView registerNib:[UINib nibWithNibName:CUSTOM_CELL_NIB_NAME bundle:nil] forCellReuseIdentifier:CUSTOM_CELL_ID];

    UISwipeGestureRecognizer* leftSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleLeftSwipe:)];
    leftSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
    [self.tableView addGestureRecognizer:leftSwipeRecognizer];

    UISwipeGestureRecognizer* rightSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleRightSwipe:)];
    rightSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
    [self.tableView addGestureRecognizer:rightSwipeRecognizer];
}

Потім додайте два пальці оброблювача

- (void)handleLeftSwipe:(UISwipeGestureRecognizer*)recognizer
{
    CGPoint point = [recognizer locationInView:self.tableView];
    NSIndexPath* index = [self.tableView indexPathForRowAtPoint:point];

    UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:index];

    if([cell respondsToSelector:@selector(showCustomView)])
    {
        [cell performSelector:@selector(showCustomView)];
    }
}

- (void)handleRightSwipe:(UISwipeGestureRecognizer*)recognizer
{
    CGPoint point = [recognizer locationInView:self.tableView];
    NSIndexPath* index = [self.tableView indexPathForRowAtPoint:point];

    UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:index];

    if([cell respondsToSelector:@selector(hideCustomView)])
    {
        [cell performSelector:@selector(hideCustomView)];
    }
}

Тепер усередині cellForRowAtIndexPath, UITableViewDelegate, ви можете створити користувацький UIView і приєднати його до вилученої комірки.

-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CustomCellTableViewCell* cell = (CustomCellTableViewCell*)[tableView dequeueReusableCellWithIdentifier:@"CustomCellTableViewCell" forIndexPath:indexPath];

    NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"CellCustomView"
                                                      owner:nil
                                                    options:nil];

    CellCustomView* customView = (CellCustomView*)[ nibViews objectAtIndex: 0];

    cell.customView = customView;

    return cell;
}

Звичайно, такий спосіб завантаження користувацького UIView - саме для цього прикладу. Керуйте ним, як хочете.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.