Проведите пальцем по кнопке «Удалить» и кнопке «Дополнительно» (как в приложении «Почта» на iOS 7).

246

Как создать кнопку «еще», когда пользователь проведет пальцем по ячейке в табличном представлении (как почтовое приложение в ios 7)

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

Мне бы хотелось, чтобы, когда пользователь проводит пальцем по ячейке табличного представления, отображалось более одной кнопки редактирования (по умолчанию это кнопка удаления). В приложении Почта для iOS 7 вы можете провести пальцем, чтобы удалить, но есть кнопка «БОЛЬШЕ», которая появляется.

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

Гай Кэлон
источник
6
Попробуйте следующие URL: HHPanningTableViewCell LRSlidingTableViewCell TISwipeableTableView RMSwipeTableViewCell
Тарек Халлак
Чтобы добавить кнопку «Удалить», я реализую следующие две функции. - (BOOL) tableView: (UITableView *) tableView canEditRowAtIndexPath: (NSIndexPath *) indexPath; - (void) tableView: (UITableView *) tableView commitEditingStyle: (UITableViewCellEditingStyle) editStyle forRowAtIndexPath: (NSIndexPath *) indexPath; И я хочу добавить кнопку «Еще» рядом с ней.
Гай Кахлон
3
@MonishBansal Bansal Похоже, что кто-то в этой теме ( devforums.apple.com/message/860459#860459 на форуме разработчиков Apple) пошел дальше и создал собственную реализацию. Вы можете найти проект, который делает то, что вы хотите, на GitHub: github.com/daria-kopaliani/DAContextMenuTableViewController
Гай Кахлон,
8
@GuyKahlonMatrix спасибо за решение, которое работает как шарм. Этот вопрос является результатом № 1 во многих поисках в Google, и люди вынуждены обмениваться своими знаниями, используя комментарии, потому что какой-то парень решил, что более полезно закрыть вопрос и проповедовать демократию. Это место явно нуждается в лучших модах.
Шафак Гезер
2
Если вы можете ориентироваться на iOS 8, мой ответ ниже будет тем, что вы хотите.
Джонни

Ответы:

126

Как реализовать

Похоже, iOS 8 открывает этот API. Намеки на такую ​​функциональность присутствуют в бета-версии 2.

Чтобы что-то работало, реализуйте следующие два метода в делегате вашего UITableView, чтобы получить желаемый эффект (см. Пример в gist).

- 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


Обновить:

Пример кода с этой работой (In 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 жест смахивания не работает. Доступ к editActions можно получить, переведя табличное представление в режим редактирования. Кто-нибудь еще находит, что смахивание не работает?
Зигфулт
@Siegfoult Вы пытались реализовать (даже если оставить пустым) tableView: commitEditingStyle: forRowAtIndexPath :?
Джонни
Я не работаю в цели c .. Тот же код, который я написал. Пожалуйста, предложите несколько советов.
Solid Soft
@SolidSoft У вас есть пример проекта, на который я мог бы взглянуть? Я мог бы помочь лучше таким образом.
Джонни
3
Чтобы ответить на мой собственный комментарий. Вы звоните tableView.editing = false( NOв objc) и ячейка будет "закрыта".
Бен Лахман
121

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

https://github.com/MortimerGoro/MGSwipeTableCell

Эта библиотека совместима со всеми различными способами создания UITableViewCell и протестирована на iOS 5, iOS 6, iOS 7 и iOS 8.

Вот пример некоторых переходов:

Пограничный переход:

Пограничный переход

Клип переход

Клип переход

3D переход:

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

MortimerGoro
источник
1
Отличная работа! Было бы здорово иметь обратные вызовы для настройки анимации.
Pacu
1
@MortimerGoro Хороший рабочий человек. Выглядит хорошо. Я пытаюсь реализовать подобный эффект в одном из моих проектов Android. Подскажите пожалуйста, как мне этого добиться в Android?
Нитеш Кумар
на iOS 8 + iPad, я просто не понимаю, что произойдет.
ScorpionKing2k5
Это удивительная библиотека, и очень хорошо, что она все еще поддерживает.
конфил
@MortimerGoro, я попытался с помощью фреймворка MGSwipeTableCel, но проблема в том, что когда я перезагружаю свою таблицу, тогда кнопка прокрутки скрыта. Любое решение этой проблемы.
Ганеш Гутури
71

Джонни - правильный ответ. Я просто добавляю это ниже в target-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...
    }
timothykc
источник
1
важно упомянуть canEditRowAtIndexPath
Heckscheibe
Если я перезагружу таблицу после того, как проведешь по ячейке, то эти кнопки будут видимыми или скрытыми?
Ганеш Гутури
25

Это (довольно смешно) частный API.

Следующие два метода являются частными и отправляются делегату UITableView:

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

Они довольно понятны.

Джонатан.
источник
4
Apple открыла эту функцию с iOS 8. См. Ответ Джонни ниже.
Зигфулт
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

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

Создайте собственную ячейку. Есть два uiviews в нем

1. upper
2. lower

В нижнем представлении добавьте все нужные вам кнопки. Сделайте свои действия так же, как любые другие IBActions. Вы можете выбрать время анимации, стиль и прочее.

Теперь добавьте uiswipegesture к верхнему виду и покажите свое нижнее представление жестом смахивания. Я делал это раньше, и это самый простой вариант, насколько я понимаю.

Надеюсь, что поможет.

Deepukjayan
источник
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]
    }

}
mriaz0011
источник
6

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

К сожалению, этот метод не дает ни ширину кнопки «Удалить», ни время анимации. Поэтому вам нужно наблюдать и жестко кодировать время и время анимации вашей кнопки More в своем коде (лично я думаю, что 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 вы можете предложить изменить. Давно не пользовался swift, поэтому не уверен, какой правильный синтаксис
Михаил Ягудаев
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...
    }
santoshIOS
источник
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 (nonatomic, copy) 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];
}

Swift:

    //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])

Это самая легкая и самая оптимизированная версия ИМХО. Надеюсь, поможет.

Обновление: вот версия 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]
}
Septronic
источник
1
Спасибо за ваш ответ, я уверен, что это поможет многим разработчикам. Да, вы правы, на самом деле Apple предоставляет это решение от iOS 8. Но, к сожалению, это нативное решение не обеспечивает полную функциональность. Например, в приложении Apple Mail у вас есть кнопки с двух сторон (одна кнопка с левой стороны и три с правой стороны) с текущим API от Apple, вы не можете добавлять кнопки с обеих сторон, а также текущий API не поддерживает действие по умолчанию, когда пользователь проводит пальцем в каждую сторону. Наилучшим решением на данный момент IMHO является MGSwipeTableCell с открытым исходным кодом.
Гай Кэлон
@GuyKahlon: да, вы абсолютно правы в отношении вопроса о смахивании влево и вправо, и я согласен, что для дополнительной настройки MGSwipeTableCell является лучшим. Apple не самый сложный вариант, но я нашел его наиболее простым для простых задач.
Септроник
@Septronic Не могли бы вы обновить свой код до Swift 3? shareMenu.не берет addActionметод. Спасибо
bibscy
@bibscy Я добавил быструю версию. Вам нужен бит для атрибута? sharemenu - это просто UIAlertController, поэтому он должен принять меры. Попробуйте и дайте мне знать, если повезет :)
Septronic
3

Actual Swift 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 и 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, чтобы показать несколько данных, после того, как swipe () справа налево на ячейку, он покажет две кнопки Approve и reject, есть два метода, первый - 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 'subviews'.
  2. Когда подпредставления изменятся, найдите представление подтверждения удаления в scrollview.subviews. Это сделано в обратном вызове наблюдения.
  3. Удвойте размер этого представления и добавьте кнопку UIB слева от его единственного подпредставления. Это также делается в обратном вызове наблюдения. Единственное подпредставление представления подтверждения удаления - кнопка удаления.
  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. Последний не полностью повторяет поведение ячеек приложения Почта, в то время как SwipeCellKitделает. Посмотри

Андрей Чернуха
источник
Я попробовал SwipeCellKitи был впечатлен ... пока я не получил одно из тех исключений, потому что число строк до обновления табличного представления не было таким же, как после обновления +/- изменения в строках. Дело в том, что я никогда не менял свой набор данных. Так что, если это не беспокоит, я не знаю, что это. Поэтому я решил не использовать его и просто использовал новые методы UITableViewDelegate. Если вам нужно больше настроек, вы всегда можете переопределитьwillBeginEditingRowAt: ....
horseshoe7
@ horseshoe7, это странно. Я никогда не сталкивался с какими-либо исключениями при использовании SwipeCellKit. В конце концов, какое отношение может иметь ячейка к такому исключению, которое происходит из-за изменений источника данных?
Андрей Чернуха
1

Swift 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
}
Vini App
источник
что такое исходный вид в ваших кодах? это значок или изображение?
Саид Рахматолахи
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 только для этого примера. Управляй как хочешь.

slobodans
источник