效果图:

1.实现原理

将所有按钮放在viewcontroller的_buttonArray集合中,同时赋值给按钮中

增加长按手势的响应

当手势坐标进入其他按钮的frame时,调整集合中按钮位置;

当长按手势开始,放大按钮; 结束时还原按钮

2.附源码及注释[按钮调控已实现动画]

@interface UIDragButton : UIButton { 
    CGPoint _prePoint;                  // 移动过程中的上一个点 
    BOOL    _isPress;                   // 是否按下:实现过程未用到 
    CGPoint _framePoint;                // 未放大情况下frame的左上角坐标 
    CGRect  _frameRect;                 // 未放大情况下frame值 
} 
 
@property (nonatomic, assign) NSMutableArray *buttonArray;  // button集合 
@property (nonatomic, assign) NSInteger      indexOfArray;  // 当前按钮在集合中的下标 
 
// 移动动画,实现过程未用到,暂不能用 
- (void)moveTo:(CGRect)rect Animation:(BOOL)flag; 
// 初始化一些样式,即长按手势 
- (void)initStyle; 
 
@end

.m

@implementation UIDragButton 
 
/* 
 * 样式初始化,即手势初始化 
 **/ 
- (void)initStyle { 
    self.backgroundColor = [UIColor whiteColor]; 
    self.layer.borderWidth = 0.3; 
    self.layer.borderColor = [UIColor grayColor].CGColor; 
    UILongPressGestureRecognizer * longPressGr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressToDo:)]; 
    longPressGr.minimumPressDuration = 1.0; 
    [self addGestureRecognizer:longPressGr]; 
} 
 
#pragma mark - 按钮尺寸更改 
// 放大 
- (void)changeBig { 
    self.transform = CGAffineTransformScale(self.transform,1.25,1.25); 
    self.backgroundColor = [UIColor colorWithHexString:@"E0E0E0" alpha:1]; 
} 
 
// 还原 
- (void)changeBack { 
    self.transform = CGAffineTransformScale(self.transform,0.8,0.8); 
    // 按钮放大还原后会有偏移,所以要设置回正常位置,80和100是按钮尺寸 
    self.frame = CGRectMake(_framePoint.x, _framePoint.y, 80, 100); 
} 
 
#pragma mark - 手势管理 
/* 
 * 手势响应,并判断状态 
 **/ 
- (void)longPressToDo:(UILongPressGestureRecognizer *)press { 
    if ([press state] == UIGestureRecognizerStateBegan) { 
        [self beganTouch:press]; 
        [self changeBig]; // 放大 
    } 
    else if ([press state] == UIGestureRecognizerStateChanged) { 
        [self movedTouch:press]; 
    } 
    else if ([press state] == UIGestureRecognizerStateEnded){ 
        [self endedTouch:press]; 
    } 
} 
 
/* 
 * 手势长按开始 
 **/ 
- (void)beganTouch:(UILongPressGestureRecognizer *)press { 
    _prePoint = [press locationInView:self]; 
    _frameRect = self.frame; 
    _framePoint = self.frame.origin; 
    // 该通知用于告诉controller点击的是第几个按钮,用于将该按钮放在最上层 
    [[NSNotificationCenter defaultCenter] postNotificationName:@"buttonChanged" object:[NSNumber numberWithInteger:_indexOfArray]]; 
} 
 
/* 
 * 手势长按结束 
 **/ 
- (void)endedTouch:(UILongPressGestureRecognizer *)press { 
    _prePoint = [press locationInView:self]; 
    [self changeBack]; 
    self.backgroundColor = [UIColor whiteColor]; 
} 
 
/* 
 * 长按过程中移动 
 **/ 
- (void)movedTouch:(UILongPressGestureRecognizer *)press { 
    CGPoint now = [press locationInView:self]; 
     
    NSInteger x = now.x - _prePoint.x; 
    NSInteger y = now.y - _prePoint.y; 
    self.frame = CGRectMake(self.frame.origin.x+x, self.frame.origin.y+y, self.frame.size.width, self.frame.size.height); 
    [self placeIsChanged:CGPointMake(self.frame.origin.x + _prePoint.x, self.frame.origin.y + _prePoint.y)]; 
} 
 
#pragma mark - 按钮调整 
/* 
 * 判断当前按钮位置是否变化,并进行调整 
 **/ 
- (void)placeIsChanged:(CGPoint)point { 
    for (NSInteger i = 0; i < [self.buttonArray count]; i++) { 
        if ([self pointIn:point rect:((UIButton *)[_buttonArray objectAtIndex:i]).frame] 
            && _indexOfArray != i) { 
            [self adjustButtons:i]; 
        } 
    } 
} 
 
/* 
 * 判断点是否在rect内 
 **/ 
- (BOOL)pointIn:(CGPoint)point rect:(CGRect)rect { 
    if (point.x > rect.origin.x && point.y > rect.origin.y && point.x < rect.origin.x+rect.size.width && point.y < rect.origin.y+rect.size.height) { 
        return YES; 
    } 
    return NO; 
} 
 
/* 
 * 调整按钮位置 
 **/ 
- (void)adjustButtons:(NSInteger)index { 
    CGRect rect = ((UIButton *)[_buttonArray objectAtIndex:index]).frame; 
     
    __block NSInteger i = 0; 
    __block NSInteger num = index; 
    // 将靠前的按钮移动到靠后的位置 
    if (_indexOfArray < index) { 
        // 将上一个按钮的位置赋值给当前按钮 
        [UIView animateWithDuration:0.5 animations:^{ 
            for (i = num; i > _indexOfArray+1; i--) { 
                ((UIDragButton *)[_buttonArray objectAtIndex:i]).frame = ((UIDragButton *)[_buttonArray objectAtIndex:i-1]).frame; 
            } 
            ((UIDragButton *)[_buttonArray objectAtIndex:i]).frame = _frameRect; 
        }]; 
        _frameRect = rect; 
         
        // 调整顺序  保证数组中按钮的frame按顺序排列 
        for (i = _indexOfArray; i < index; i++) { 
            [_buttonArray exchangeObjectAtIndex:i withObjectAtIndex:i+1]; 
            ((UIDragButton *)[_buttonArray objectAtIndex:i]).indexOfArray = i; 
        } 
        ((UIDragButton *)[_buttonArray objectAtIndex:index]).indexOfArray = index; 
    } 
    else { // 将靠后的按钮移动到前边 
        // 将上一个按钮的位置赋值给当前按钮 
        [UIView animateWithDuration:0.5 animations:^{ 
            for (i = num; i < _indexOfArray-1; i++) { 
                ((UIDragButton *)[_buttonArray objectAtIndex:i]).frame = ((UIDragButton *)[_buttonArray objectAtIndex:i+1]).frame; 
            } 
            ((UIDragButton *)[_buttonArray objectAtIndex:i]).frame = _frameRect; 
        }]; 
        _frameRect = rect; 
         
        // 调整顺序  保证数组中按钮的frame按顺序排列 
        for (i = _indexOfArray; i > index; i--) { 
            [_buttonArray exchangeObjectAtIndex:i withObjectAtIndex:i-1]; 
            ((UIDragButton *)[_buttonArray objectAtIndex:i]).indexOfArray = i; 
        } 
        ((UIDragButton *)[_buttonArray objectAtIndex:index]).indexOfArray = index; 
    } 
    _framePoint = _frameRect.origin; 
} 
 
#pragma mark - 按钮移动动画 
- (void)moveTo:(CGRect)rect Animation:(BOOL)flag { 
    if (flag) { 
        // 计算偏移并移动 
        float x = rect.origin.x - self.frame.origin.x; 
        float y = rect.origin.y - self.frame.origin.y; 
        [self.layer addAnimation:[self moveX:0.1 X:[NSNumber numberWithFloat:x]] forKey:nil]; 
        [self.layer addAnimation:[self moveY:0.1 Y:[NSNumber numberWithFloat:y]] forKey:nil]; 
    } 
    else { 
        self.frame = rect; 
    } 
} 
 
- (CABasicAnimation *)moveX:(float)time X:(NSNumber *)x  // 横向移动 
{ 
    CABasicAnimation *animation=[CABasicAnimation animationWithKeyPath:@"transform.translation.x"]; 
    animation.toValue=x; 
    animation.duration=time;                    // 动画持续时间 
    animation.removedOnCompletion=NO; 
    animation.fillMode=kCAFillModeForwards; 
    return animation; 
} 
 
- (CABasicAnimation *)moveY:(float)time Y:(NSNumber *)y  // 纵向移动 
{ 
    CABasicAnimation *animation=[CABasicAnimation animationWithKeyPath:@"transform.translation.y"]; 
    animation.toValue=y; 
    animation.duration=time; 
    animation.removedOnCompletion=NO; 
    animation.fillMode=kCAFillModeForwards; 
    return animation; 
} 
 
@end


发布评论

分享到:

IT虾米网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!

iOS 移动动画简单实现详解
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。