iOS開発:アリペイインタフェースドラッグボタンアニメーション

15971 ワード

2つの方法は、支付宝の生活インタフェースを真似てカスタマイズされたブロックbuttonをドラッグ&ドロップするアニメーション効果を実現し、ブロックを長く押すと、ブロックを新しい位置にドラッグ&ドロップすることができ、他のブロックは自動的にレイアウトを移動したり、ブロックを追加したり、削除したりすることができます.

プレビュー


構想

  • の2つのアニメーション効果:1つは、ブロックを移動するときにベルが鳴るブロックと位置を交換することであり、もう1つは記録インデックスであり、ブロックが新しい位置に達したときに他のブロックが
  • に順次移動することである.
  • はiosフレームワークのジェスチャー認識
  • を用いる.
  • ブロック内部のデータインデックスがインタフェースレイアウトと一致することを維持する
  • .
    ブロックbuttonクラスを定義する
    //
    //  TileView.h
    //  DragTiles
    //
    //  Created by yxhe on 16/5/26.
    //  Copyright © 2016  yxhe. All rights reserved.
    //
    
    #import <UIKit/UIKit.h>
    
    @class TileButton;
    
    @protocol TileButtonDelegate<NSObject>
    
    @optional
    
    - (void)tileButtonClicked:(TileButton *)tileBtn;
    
    @end
    
    
    @interface TileButton : UIButton
    
    @property (nonatomic, assign) id<TileButtonDelegate> delegate;
    
    @property (nonatomic, assign) NSInteger index; //index in the tile array
    
    - (void)setTileText:(NSString *)text clickText:(NSString *)clickText; //set the tile text outside the class
    
    - (void)tileLongPressed; //tile longpressed and begin to move, called outside
    
    - (void)tileSuspended; //the tile touched pressed but not moved, called outside
    
    - (void)tileSettled; //cancel press or settle the tile to new place, called outside
    
    @end
    
    //
    //  TileView.m
    //  DragTiles
    //
    //  Created by yxhe on 16/5/26.
    //  Copyright © 2016  yxhe. All rights reserved.
    //
    
    #import "TileButton.h"
    
    @interface TileButton ()
    
    @property (nonatomic, strong) UIButton *deleteButton; //the little del button
    
    @end
    
    @implementation TileButton
    
    - (instancetype)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if(self)
        {
            //set the main button style,in the tile button we can add many things
            self.backgroundColor = [UIColor yellowColor];
            [self setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
    //        [self setTitleColor:[UIColor greenColor] forState:UIControlEventTouchDown];
            
            //add the delete button
            _deleteButton = [UIButton buttonWithType:UIButtonTypeCustom];
            
            _deleteButton.frame = CGRectMake(self.bounds.size.width*6/7.0, 0, self.frame.size.width/7.0, self.frame.size.height/7.0); //use the relative coordinates
            _deleteButton.backgroundColor = [UIColor redColor];
            _deleteButton.transform = CGAffineTransformMakeScale(0.1, 0.1); //set the deletebutton small at the beginning
            _deleteButton.hidden = YES; //hide it at the beginning
            [_deleteButton addTarget:self action:@selector(deleteButtonClicked) forControlEvents:UIControlEventTouchUpInside];
            [self addSubview:_deleteButton];
    
            
        }
        
        return self;
    }
    
    
    #pragma mark - called outside
    - (void)setTileText:(NSString *)text clickText:(NSString *)clickText
    {
        [self setTitle:text forState:UIControlStateNormal];
    //    [self setTitle:clickText forState:UIControlEventTouchDown];
        
    }
    
    - (void)tileLongPressed
    {
        //make the tile half transparent and show the deletebutton
        [_deleteButton setHidden:NO]; //show the deletebutton
        [UIView animateWithDuration:0.3 animations:^{
            self.alpha = 0.6;
            self.transform = CGAffineTransformMakeScale(1.1, 1.1);
            _deleteButton.transform = CGAffineTransformMakeScale(1.0, 1.0);
        }];
        
    }
    
    - (void)tileSuspended
    {
        [_deleteButton setHidden:NO];
        [UIView animateWithDuration:0.3 animations:^{
            self.alpha = 0.6;
            self.transform = CGAffineTransformMakeScale(1.0, 1.0);
            _deleteButton.transform = CGAffineTransformMakeScale(1.0, 1.0);
        }];
    }
    
    - (void)tileSettled
    {
        [UIView animateWithDuration:0.3 animations:^{
            self.alpha = 1.0;
            self.transform = CGAffineTransformMakeScale(1.0, 1.0);
            _deleteButton.transform = CGAffineTransformMakeScale(0.1, 0.1);
        }];
        [self performSelector:@selector(delayHide) withObject:nil afterDelay:0.3];
    }
    
    - (void)delayHide
    {
        [_deleteButton setHidden:YES]; //the main button removed then the deletebutton automatically removed
    }
    
    #pragma button callback
    - (void)deleteButtonClicked
    {
        NSLog(@"delete button clicked");
        if([self.delegate respondsToSelector:@selector(tileButtonClicked:)])
            [self.delegate tileButtonClicked:self];
        
    }
    
    @end
    
    このbuttonクラスには、ジェスチャーの長押しとキャンセルの関数を処理したり、削除の依頼イベントのコールバックに応答したり(メッセージを送信したりすることもできます)、buttonが長押しされた後に浮かぶ拡大透明アニメーション、deleteボタンでスケールされたアニメーションなど、いくつかの内容がカプセル化されています.
    メインインタフェースでのジェスチャーの処理
    - (void)onLongGresture:(UILongPressGestureRecognizer *)sender
    {
    #ifndef ALIPAY_ANIMATION
        [self handleFreeMove:sender];
    #else
        [self handleSequenceMove:sender];
    #endif
    }

    アニメーション1
    //method 1: exchange the adjacent tiles, the sequence of the array elements will be disorderd
    - (void)handleFreeMove:(UILongPressGestureRecognizer *)sender
    {
        TileButton *tile_btn = sender.view; //get the dragged tilebutton
        switch(sender.state)
        {
            case UIGestureRecognizerStateBegan:
                startPos = [sender locationInView:sender.view];
                originPos = tile_btn.center;
                [tile_btn tileSuspended];
                touchState = SUSPEND;
                preTouchID = tile_btn.index; //save the ID of pretouched title
                break;
            case UIGestureRecognizerStateChanged:
            {
                [tile_btn tileLongPressed];
                touchState = MOVE; //the tile will move
                CGPoint newPoint = [sender locationInView:sender.view];
                CGFloat offsetX = newPoint.x - startPos.x;
                CGFloat offsetY = newPoint.y - startPos.y;
                
                tile_btn.center = CGPointMake(tile_btn.center.x + offsetX, tile_btn.center.y + offsetY);
                
                //get the intersect tile ID
                int intersectID = -1;
                for(NSInteger i = 0; i < _tileArray.count; i++)
                    if(tile_btn != _tileArray[i] && CGRectContainsPoint([_tileArray[i] frame], tile_btn.center))
                    {
                        intersectID = i;
                        break;
                    }
                
                if(intersectID != -1)
                {
                    //swap every tile, the index remains unchanged
                    __block TileButton *collisionButton = _tileArray[intersectID];
                    __block CGPoint tempOriginPos = collisionButton.center; //the new origin point
                    [UIView animateWithDuration:0.3 animations:^{
                        
                        collisionButton.center = originPos; //move the other title to the moved tile's origin pos
                        originPos = tempOriginPos; //save the temp origin point in case the block shake
                        
                    }];
                    
                    //exchange the tile index of the array
                    [_tileArray exchangeObjectAtIndex:tile_btn.index withObjectAtIndex:intersectID];
                    
                    //tile_btn still point to the moving tile, just swap the index
                    int tempID = collisionButton.index;
                    collisionButton.index = tile_btn.index;
                    tile_btn.index = tempID;
    
                }
                
            }
                break;
            case UIGestureRecognizerStateEnded:
            {
                //   [tile_btn tileSuspended];
                [UIView animateWithDuration:0.3 animations:^{
                    tile_btn.center = originPos;
                }];
                if(touchState == MOVE) //only if the pre state is MOVE, then settle, otherwise leave it suspend
                {
                    touchState = UNTOUCHED;
                    [tile_btn tileSettled]; //settle the tile to the new position(no need to use delay operation here)
                }
            }
                
                break;
            default:
                break;
        }
    }
    
    は、長押しジェスチャーが異なる状態にある場合に状態変換を行う状態列挙を定義し、基本原理は、ブロックをドラッグする過程で座標位置を絶えず記録し、隣接するブロック交換位置を実現することである.
    アニメーション2
    //method 2: move the tiles inorder like Alipay, the order in array remains in sequence always
    - (void)handleSequenceMove:(UILongPressGestureRecognizer *)sender
    {
        TileButton *tile_btn = sender.view; //get the dragged tilebutton
        switch(sender.state)
        {
            case UIGestureRecognizerStateBegan:
                startPos = [sender locationInView:sender.view];
                originPos = tile_btn.center;
                [tile_btn tileSuspended];
                touchState = SUSPEND;
                preTouchID = tile_btn.index; //save the ID of pretouched title
                break;
            case UIGestureRecognizerStateChanged:
            {
                [tile_btn tileLongPressed];
                touchState = MOVE; //the tile will move
                CGPoint newPoint = [sender locationInView:sender.view];
                CGFloat offsetX = newPoint.x - startPos.x;
                CGFloat offsetY = newPoint.y - startPos.y;
                
                tile_btn.center = CGPointMake(tile_btn.center.x + offsetX, tile_btn.center.y + offsetY);
                
                //get the intersect tile ID
                int intersectID = -1;
                for(NSInteger i = 0; i < _tileArray.count; i++)
                    if(tile_btn != _tileArray[i] && CGRectContainsPoint([_tileArray[i] frame], tile_btn.center))
                    {
                        intersectID = i;
                        break;
                    }
                
                if(intersectID != -1)
                {
                    if(abs(intersectID - tile_btn.index) == 1) //if the tiles are adjacent then move directly
                    {
                        __block TileButton *collisionButton = _tileArray[intersectID];
                        __block CGPoint tempOriginPos = collisionButton.center; //the new origin point
                        [UIView animateWithDuration:0.3 animations:^{
                            
                            collisionButton.center = originPos; //move the other title to the moved tile's origin pos
                            originPos = tempOriginPos; //save the temp origin point in case the block shake
                            
                        }];
                        
                        //exchange the tile index of the array
                        [_tileArray exchangeObjectAtIndex:tile_btn.index withObjectAtIndex:intersectID];
                        
                        //tile_btn still point to the moving tile, just swap the index
                        int tempID = collisionButton.index;
                        collisionButton.index = tile_btn.index;
                        tile_btn.index = tempID;
                        
                        
                        NSLog(@"tilebtn index:%d, intersect index:%d", [_tileArray[tile_btn.index] index], [_tileArray[collisionButton.index] index]);
                        
                    }
                    else if(intersectID - tile_btn.index >1) //move the tiles to the left in order
                    {
                        CGPoint preCenter = originPos;
                        CGPoint curCenter;
                        //exchange the pointer in array and swap the index,at last the tile_btn is at the new right place
                        for(int i = tile_btn.index + 1; i <= intersectID; i++)
                        {
                            __block TileButton *movedTileBtn = _tileArray[i];
                            curCenter = movedTileBtn.center;
                            
                            [UIView animateWithDuration:0.3 animations:^{
                                movedTileBtn.center = preCenter;
                            }];
                            preCenter = curCenter; //save the precenter
                            
                            movedTileBtn.index--; //reduce the tile index
                            _tileArray[i-1] = movedTileBtn; //move the pointer one by one
                            
                            
                        }
                        originPos = preCenter;
                        tile_btn.index = intersectID; //exchange the ID
                        _tileArray[intersectID] = tile_btn; //now make the last pointer point to the tile_btn
                        NSLog(@"new tile btn index: %d", [_tileArray[tile_btn.index] index]);
                    }
                    else //move the tile to right in order
                    {
                        CGPoint preCenter = originPos;
                        CGPoint curCenter;
                        //exchange the pointer in array and swap the index,at last the tile_btn is at the new right place
                        for(int i = tile_btn.index - 1; i >= intersectID; i--)
                        {
                            __block TileButton *movedTileBtn = _tileArray[i];
                            curCenter = movedTileBtn.center;
                            
                            [UIView animateWithDuration:0.3 animations:^{
                                movedTileBtn.center = preCenter;
                            }];
                            preCenter = curCenter; //save the precenter
                            
                            movedTileBtn.index++; //reduce the tile index
                            _tileArray[i+1] = movedTileBtn; //move the pointer one by one
                            
                        }
                        originPos = preCenter;
                        tile_btn.index = intersectID; //exchange the ID
                        _tileArray[intersectID] = tile_btn; //now make the last pointer point to the tile_btn
                        NSLog(@"new tile btn index: %d", [_tileArray[tile_btn.index] index]);
                    }
                    
                    
                    //test the display if the array is inorder
                    for(TileButton *tile in _tileArray)
                        NSLog(@"tile text: %@", tile.titleLabel.text);
                    
                }
                
            }
                break;
            case UIGestureRecognizerStateEnded:
            {
                //   [tile_btn tileSuspended];
                [UIView animateWithDuration:0.3 animations:^{
                    tile_btn.center = originPos;
                }];
                if(touchState == MOVE) //only if the pre state is MOVE, then settle, otherwise leave it suspend
                    [tile_btn tileSettled]; //settle the tile to the new position(no need to use delay operation here)
            }
                
                break;
            default:
                break;
        }
    
    }
    このアニメーションの違いは、ブロックが新しい位置に移動し、他のブロックが自動的に順番に前の空の位置を補完することであり、基本原理は、ブロックインデックスを保存し、開始位置と終了位置、ブロックが順番に移動することである.
    アニメーションを削除
    //tile delete button clicked
    - (void)tileButtonClicked:(TileButton *)tileBtn
    {
        //remove the button and adjust the tilearray
        
        NSLog(@"deletebutton delegate responds");
        
        //remember the deleted tile's infomation
        int startIndex = tileBtn.index;
        CGPoint preCenter = tileBtn.center;
        CGPoint curCenter;
        
        //[_tileArray removeObject:tileBtn]; //delete the tile
        //exchange the pointer in array and swap the index,at last the tile_btn is at the new right place
        for(int i = startIndex + 1; i < _tileArray.count; i++)
        {
            __block TileButton *movedTileBtn = _tileArray[i];
            curCenter = movedTileBtn.center;
            
            [UIView animateWithDuration:0.3 animations:^{
                movedTileBtn.center = preCenter;
            }];
            preCenter = curCenter; //save the precenter
            
            movedTileBtn.index--; //reduce the tile index
            _tileArray[i-1] = movedTileBtn; //move the pointer one by one
            
        }
        
        [_tileArray removeLastObject]; //every time remove the last object
        
        //must remove the tileBtn from the view
        [tileBtn removeFromSuperview]; //we can also use performselector so that button disappears with animation
        //test the display if the array is inorder
        for(TileButton *tile in _tileArray)
            NSLog(@"tile text: %@", tile.titleLabel.text);
    
    
    }
    あるブロックを削除すると、後ろのブロックも自動的に補完され、アニメーション効果があります.

    ソースのダウンロード


    csdn:アリペイを真似てブロックをドラッグ
    github: DragTiles