スワイプして削除し、[その他]ボタンをクリックします(iOS 7のMailアプリなど)


Answers

私は、iOS 8メールアプリのようなさまざまなトランジションと拡張可能なボタンをサポートする、スワップ可能なボタンを実装するための新しいライブラリを作成しました。

https://github.com/MortimerGoro/MGSwipeTableCell

このライブラリは、UITableViewCellを作成するさまざまな方法と互換性があり、iOS 5、iOS 6、iOS 7、iOS 8でテストされています。

ここでは、いくつかのトランジションのサンプルを示します。

ボーダートランジション:

クリップトランジション

3Dトランジション:

Question

ユーザーがテーブルビューでセルをスワイプしたときに[more]ボタンを作成する方法(ios 7のメールアプリなど)

私はこことCocoa Touchフォーラムの両方でこの情報を探していましたが、答えが見つからないようで、自分よりも賢い人が私に解決策を与えてくれることを願っています。

ユーザーがテーブルビューのセルをスワイプして複数の編集ボタン(デフォルトでは削除ボタン)を表示するようにしたいと思います。 iOS 7のMailアプリではスワイプして削除できますが、表示される「MORE」ボタンがあります。




ここに簡単な解決策があります。 これは、UITableViewCell内でカスタムUIViewを表示および非表示にすることができます。 表示ロジックは、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からテーブルビューセルを単純に拡張します。

次に、UITableViewDelegateを実装するInside UIViewControllerは、左右のスワイプを処理する2つのジェスチャ認識子を作成します。

- (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];
}

2つのスワイプハンドラを追加するよりも

- (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)];
    }
}

今、UITableViewDelegateのcellForRowAtIndexPathの内部で、カスタム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の読み込み方法は、この例のためのものです。 必要に応じて管理してください。




私は自分のアプリケーションに同じ機能を追加しようとしていました。そして、非常に多くのチュートリアル( raywenderlichが最高のDIYソリューション)を経て、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はstyleプロパティの値に基づいてデフォルトの色を割り当てます。

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]
}



iOS 11以降、これはUITableViewDelegate公開されてい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




SwipeCellKitと呼ばれるすばらしいライブラリがあります。 私の意見では、 MGSwipeTableCellよりもMGSwipeTableCellです。 後者は、メールアプリケーションのセルの振る舞いを完全に再現するわけではありませんが、 SwipeCellKitは完全に複製しません。 見てみましょう




リンゴがあなたに必要なものを与えてくれるまで待ちませんか? ここに私の選択があります。

カスタムセルを作成します。 それに2つのuiviewsがある

1. upper
2. lower

下のビューでは、必要なボタンを追加します。 他のIBActionsと同じように行動します。 アニメーションの時間、スタイルなどを決めることができます。

上部のビューにuiswipegestureを追加し、スワイプジェスチャーの下のビューを表示します。 私がこれまでにやってきたのは、これまでの最も簡単な選択肢でした。

助けて欲しい。




迅速なプログラミング

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
}



これはプライベートAPIです(むしろ馬鹿げています)。

次の2つのメソッドはプライベートであり、UITableViewのデリゲートに送信されます。

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

彼らはかなり自明です。




任意のライブラリを使用せずに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]
    }

}