iOS开发:仿支付宝界面拖拽按钮动画

来源:互联网 发布:网络教育哪个机构好 编辑:程序博客网 时间:2024/06/09 15:03

两种方式实现模仿支付宝生活界面可拖拽定制方块button的动画效果,当长按方块,可以拖拽方块到新的位置,其他的方块自动移动布局,也可以添加、删除方块。


预览



思路

  • 两种动画效果:一种是移动方块时与响铃方块交换位置,另一种是记录索引,方块到达新位置时其他方块依次迁移
  • 用到了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}

动画一
//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;    }}
定义一个状态枚举,当长按手势处于不同的状态时进行状态转换,基本原理是,拖动方块过程中不断记录坐标位置,实现相邻方块交换位置。

动画二
//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





0 0
原创粉丝点击