web-dev-qa-db-ja.com

UITableViewのスワイプを実装してUICollectionViewを削除する方法

UICollectionViewで削除するために、UITableViewのスワイプと同じ動作をどのように実装できるかを質問したいだけです。チュートリアルを探していますが、見つかりません。

また、iOS 5をサポートするためにPSTCollectionViewラッパーを使用しています。

ありがとうございました!

編集:スワイプ認識機能は既に優れています。必要なのは、削除モードをキャンセルするときのUITableViewと同じ機能です。ユーザーがセルまたはテーブルビューの空白スペースをタップしたとき(つまり、ユーザーが[削除]ボタンの外側をタップしたとき)。 UITapGestureRecognizerは、タッチのリリース時にタップを検出するだけなので、機能しません。 UITableViewは、ジェスチャーの開始時(リリース時ではなく)のタッチを検出し、すぐに削除モードをキャンセルします。

19
MiuMiu

非常にシンプルです。customContentViewの後ろにcustomBackgroundViewcustomContentViewを追加する必要があります。

その後、ユーザーが右から左にスワイプすると、customContentViewを左にシフトする必要があります。ビューをシフトすると、customBackgroundViewに表示されます。

Let Code:

まず、UICollectionViewにpanGestureを追加する必要があります

   override func viewDidLoad() {
        super.viewDidLoad()
        self.panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.panThisCell))
        panGesture.delegate = self
        self.collectionView.addGestureRecognizer(panGesture)

    }

セレクタを次のように実装します

  func panThisCell(_ recognizer:UIPanGestureRecognizer){

        if recognizer != panGesture{  return }

        let point = recognizer.location(in: self.collectionView)
        let indexpath = self.collectionView.indexPathForItem(at: point)
        if indexpath == nil{  return }
        guard let cell = self.collectionView.cellForItem(at: indexpath!) as? CustomCollectionViewCell else{

            return

        }
        switch recognizer.state {
        case .began:

            cell.startPoint =  self.collectionView.convert(point, to: cell)
            cell.startingRightLayoutConstraintConstant  = cell.contentViewRightConstraint.constant
            if swipeActiveCell != cell && swipeActiveCell != nil{

                self.resetConstraintToZero(swipeActiveCell!,animate: true, notifyDelegateDidClose: false)
            }
            swipeActiveCell = cell

        case .changed:

            let currentPoint =  self.collectionView.convert(point, to: cell)
            let deltaX = currentPoint.x - cell.startPoint.x
            var panningleft = false

            if currentPoint.x < cell.startPoint.x{

                panningleft = true

            }
            if cell.startingRightLayoutConstraintConstant == 0{

                if !panningleft{

                    let constant = max(-deltaX,0)
                    if constant == 0{

                        self.resetConstraintToZero(cell,animate: true, notifyDelegateDidClose: false)

                    }else{

                        cell.contentViewRightConstraint.constant = constant

                    }
                }else{

                    let constant = min(-deltaX,self.getButtonTotalWidth(cell))
                    if constant == self.getButtonTotalWidth(cell){

                        self.setConstraintsToShowAllButtons(cell,animate: true, notifyDelegateDidOpen: false)

                    }else{

                        cell.contentViewRightConstraint.constant = constant
                        cell.contentViewLeftConstraint.constant = -constant
                    }
                }
            }else{

                let adjustment = cell.startingRightLayoutConstraintConstant - deltaX;
                if (!panningleft) {

                    let constant = max(adjustment, 0);
                    if (constant == 0) {

                        self.resetConstraintToZero(cell,animate: true, notifyDelegateDidClose: false)

                    } else {

                        cell.contentViewRightConstraint.constant = constant;
                    }
                } else {
                    let constant = min(adjustment, self.getButtonTotalWidth(cell));
                    if (constant == self.getButtonTotalWidth(cell)) {

                        self.setConstraintsToShowAllButtons(cell,animate: true, notifyDelegateDidOpen: false)
                    } else {

                        cell.contentViewRightConstraint.constant = constant;
                    }
                }
                cell.contentViewLeftConstraint.constant = -cell.contentViewRightConstraint.constant;

            }
            cell.layoutIfNeeded()
        case .cancelled:

            if (cell.startingRightLayoutConstraintConstant == 0) {

                self.resetConstraintToZero(cell,animate: true, notifyDelegateDidClose: true)

            } else {

                self.setConstraintsToShowAllButtons(cell,animate: true, notifyDelegateDidOpen: true)
            }

        case .ended:

            if (cell.startingRightLayoutConstraintConstant == 0) {
                //Cell was opening
                let halfOfButtonOne = (cell.swipeView.frame).width / 2;
                if (cell.contentViewRightConstraint.constant >= halfOfButtonOne) {
                    //Open all the way
                    self.setConstraintsToShowAllButtons(cell,animate: true, notifyDelegateDidOpen: true)
                } else {
                    //Re-close
                    self.resetConstraintToZero(cell,animate: true, notifyDelegateDidClose: true)
                }
            } else {
                //Cell was closing
                let buttonOnePlusHalfOfButton2 = (cell.swipeView.frame).width
                if (cell.contentViewRightConstraint.constant >= buttonOnePlusHalfOfButton2) {
                    //Re-open all the way
                    self.setConstraintsToShowAllButtons(cell,animate: true, notifyDelegateDidOpen: true)
                } else {
                    //Close
                    self.resetConstraintToZero(cell,animate: true, notifyDelegateDidClose: true)
                }
            }

        default:
            print("default")
        }
    }

制約を更新するヘルパーメソッド

 func getButtonTotalWidth(_ cell:CustomCollectionViewCell)->CGFloat{

        let width = cell.frame.width - cell.swipeView.frame.minX
        return width

    }

    func resetConstraintToZero(_ cell:CustomCollectionViewCell, animate:Bool,notifyDelegateDidClose:Bool){

        if (cell.startingRightLayoutConstraintConstant == 0 &&
            cell.contentViewRightConstraint.constant == 0) {
            //Already all the way closed, no bounce necessary
            return;
        }
        cell.contentViewRightConstraint.constant = -kBounceValue;
        cell.contentViewLeftConstraint.constant = kBounceValue;
        self.updateConstraintsIfNeeded(cell,animated: animate) {
            cell.contentViewRightConstraint.constant = 0;
            cell.contentViewLeftConstraint.constant = 0;

            self.updateConstraintsIfNeeded(cell,animated: animate, completionHandler: {

                cell.startingRightLayoutConstraintConstant = cell.contentViewRightConstraint.constant;
            })
        }
        cell.startPoint = CGPoint()
        swipeActiveCell = nil
    }

    func setConstraintsToShowAllButtons(_ cell:CustomCollectionViewCell, animate:Bool,notifyDelegateDidOpen:Bool){

        if (cell.startingRightLayoutConstraintConstant == self.getButtonTotalWidth(cell) &&
            cell.contentViewRightConstraint.constant == self.getButtonTotalWidth(cell)) {
            return;
        }
        cell.contentViewLeftConstraint.constant = -self.getButtonTotalWidth(cell) - kBounceValue;
        cell.contentViewRightConstraint.constant = self.getButtonTotalWidth(cell) + kBounceValue;

        self.updateConstraintsIfNeeded(cell,animated: animate) {
            cell.contentViewLeftConstraint.constant =  -(self.getButtonTotalWidth(cell))
            cell.contentViewRightConstraint.constant = self.getButtonTotalWidth(cell)

            self.updateConstraintsIfNeeded(cell,animated: animate, completionHandler: {(check) in

                cell.startingRightLayoutConstraintConstant = cell.contentViewRightConstraint.constant;
            })
        }
    }

    func setConstraintsAsSwipe(_ cell:CustomCollectionViewCell, animate:Bool,notifyDelegateDidOpen:Bool){

        if (cell.startingRightLayoutConstraintConstant == self.getButtonTotalWidth(cell) &&
            cell.contentViewRightConstraint.constant == self.getButtonTotalWidth(cell)) {
            return;
        }
        cell.contentViewLeftConstraint.constant = -self.getButtonTotalWidth(cell) - kBounceValue;
        cell.contentViewRightConstraint.constant = self.getButtonTotalWidth(cell) + kBounceValue;

        self.updateConstraintsIfNeeded(cell,animated: animate) {
            cell.contentViewLeftConstraint.constant =  -(self.getButtonTotalWidth(cell))
            cell.contentViewRightConstraint.constant = self.getButtonTotalWidth(cell)

            self.updateConstraintsIfNeeded(cell,animated: animate, completionHandler: {(check) in

                cell.startingRightLayoutConstraintConstant = cell.contentViewRightConstraint.constant;
            })
        }
    }


    func updateConstraintsIfNeeded(_ cell:CustomCollectionViewCell, animated:Bool,completionHandler:@escaping ()->()) {
        var duration:Double = 0
        if animated{

            duration = 0.1

        }
        UIView.animate(withDuration: duration, delay: 0, options: [.curveEaseOut], animations: {

            cell.layoutIfNeeded()

            }, completion:{ value in

                if value{ completionHandler() }
        })
    }

サンプルプロジェクトを作成しました here in Swift 3。

これは チュートリアル の修正版です。

13
LC 웃

ジェスチャーレコグナイザーの使用を回避する問題に対するより簡単な解決策があります。ソリューションは、UIScrollViewUIStackViewの組み合わせに基づいています。

  1. まず、2つのコンテナビューを作成する必要があります。1つはセルの表示部分用で、もう1つは非表示部分用です。これらのビューをUIStackViewに追加します。 stackViewはコンテンツビューとして機能します。ビューの幅がstackView.distribution = .fillEquallyと等しいことを確認してください。

  2. ページングが有効になっているstackView内にUIScrollViewを埋め込みます。 scrollViewはセルの端に制限する必要があります。次に、stackViewの幅をscrollViewの幅の2倍に設定して、各コンテナビューにセルの幅を設定します。

この単純な実装で、表示および非表示のビューを持つ基本セルを作成しました。表示ビューを使用してセルにコンテンツを追加し、非表示ビューで削除ボタンを追加できます。これにより、これを実現できます。

swipe to delete

GitHubのサンプルプロジェクト を設定しました。 このソリューションの詳細についてはこちらをご覧ください
このソリューションの最大の利点は、シンプルさであり、制約やジェスチャー認識機能に対処する必要がないことです。

10
Amer Hukic

@JacekLampartへの同様のアプローチに従いましたが、UICollectionViewCellのawakeFromNib関数にUISwipeGestureRecognizerを追加することを決定したので、一度だけ追加されます。

UICollectionViewCell.m

- (void)awakeFromNib {
    UISwipeGestureRecognizer* swipeGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeToDeleteGesture:)];
    swipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
    [self addGestureRecognizer:swipeGestureRecognizer];
}

- (void)swipeToDeleteGesture:(UISwipeGestureRecognizer *)swipeGestureRecognizer {
    if (swipeGestureRecognizer.state == UIGestureRecognizerStateEnded) {
        // update cell to display delete functionality
    }
}

削除モードの終了については、UIViewのNSArrayを使用してカスタムUIGestureRecognizerを作成しました。私はこの質問から@iMSからアイデアを借りました: ITapGestureRecognizer-タッチアップではなくタッチダウンで機能させるか?

TouchesBeganで、タッチポイントがどのUIViews内にもない場合、ジェスチャーは成功し、削除モードが終了します。

このようにして、セル内の削除ボタン(およびその他のビュー)をUIGestureRecognizerに渡すことができます。タッチポイントがボタンのフレーム内にある場合、削除モードは終了しません。

TouchDownExlusionViewsGestureRecognizer.h

#import <UIKit/UIKit.h>

@interface TouchDownExcludingViewsGestureRecognizer : UIGestureRecognizer

@property (nonatomic) NSArray *excludeViews;

@end

TouchDownExlusionViewsGestureRecognizer.m

#import "TouchDownExcludingViewsGestureRecognizer.h"
#import <UIKit/UIGestureRecognizerSubclass.h>

@implementation TouchDownExcludingViewsGestureRecognizer

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    if (self.state == UIGestureRecognizerStatePossible) {
        BOOL touchHandled = NO;
        for (UIView *view in self.excludeViews) {
            CGPoint touchLocation = [[touches anyObject] locationInView:view];
            if (CGRectContainsPoint(view.bounds, touchLocation)) {
                touchHandled = YES;
                break;
            }
        }

        self.state = (touchHandled ? UIGestureRecognizerStateFailed : UIGestureRecognizerStateRecognized);
    }
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    self.state = UIGestureRecognizerStateFailed;
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    self.state = UIGestureRecognizerStateFailed;
}


@end

実装(UICollectionViewを含むUIViewController内):

#import "TouchDownExcludingViewsGestureRecognizer.h"

TouchDownExcludingViewsGestureRecognizer *touchDownGestureRecognizer = [[TouchDownExcludingViewsGestureRecognizer alloc] initWithTarget:self action:@selector(exitDeleteMode:)];
touchDownGestureRecognizer.excludeViews = @[self.cellInDeleteMode.deleteButton];
[self.view addGestureRecognizer:touchDownGestureRecognizer];

- (void)exitDeleteMode:(TouchDownExcludingViewsGestureRecognizer *)touchDownGestureRecognizer {
    // exit delete mode and disable or remove TouchDownExcludingViewsGestureRecognizer
}
3
Kristian Bauer

次のように、UISwipeGestureRecognizerを各コレクションセルに追加してみてください。

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
             cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CollectionViewCell *cell = ...

    UISwipeGestureRecognizer* gestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(userDidSwipe:)];
    [gestureRecognizer setDirection:UISwipeGestureRecognizerDirectionRight];
    [cell addGestureRecognizer:gestureRecognizer];
}

に続く:

- (void)userDidSwipe:(UIGestureRecognizer *)gestureRecognizer {
    if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
        //handle the gesture appropriately
    }
}
2
Jacek Lampart