iOS 给view加一个拖拽功能

前言

现在的直播app都具有悬浮窗功能,悬浮窗可以拖拽,并且具有回弹动画,可以设计一个UIView的分类实现,减少侵入性。

主要代码及思路

思路

  • 给view添加一个pan的手势,在gesture的状态中进行判断(例如边界的回弹),最终拖拽手势结束时,通过block把手势回调出去。

代码

#import "UIView+dragable.h"
#import 

#define ScreenWidth                         [[UIScreen mainScreen] bounds].size.width
#define ScreenHeight                        [[UIScreen mainScreen] bounds].size.height

static const char *ActionHandlerPanGestureKey;

@implementation UIView (dragable)

- (void)addDragableActionWithEnd:(void (^)(CGRect endFrame))endBlock; {
    // 添加拖拽手势
    UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanAction:)];
    [self addGestureRecognizer:panGestureRecognizer];
    
    // 记录block
    objc_setAssociatedObject(self, ActionHandlerPanGestureKey, endBlock, OBJC_ASSOCIATION_COPY);
}

@end

  • 由于在uiview的分类中,所以使用了runtime关联一下需要回调的block
- (void)handlePanAction:(UIPanGestureRecognizer *)sender {
    CGPoint point = [sender translationInView:[sender.view superview]];
    
    CGFloat senderHalfViewWidth = sender.view.frame.size.width / 2;
    CGFloat senderHalfViewHeight = sender.view.frame.size.height / 2;
    
    __block CGPoint viewCenter = CGPointMake(sender.view.center.x + point.x, sender.view.center.y + point.y);
    // 拖拽状态结束
    if (sender.state == UIGestureRecognizerStateEnded) {
        [UIView animateWithDuration:0.4 animations:^{
            if ((sender.view.center.x + point.x - senderHalfViewWidth) <= 12) {
                viewCenter.x = senderHalfViewWidth + 12;
            }
            if ((sender.view.center.x + point.x + senderHalfViewWidth) >= (ScreenWidth - 12)) {
                viewCenter.x = ScreenWidth - senderHalfViewWidth - 12;
            }
            if ((sender.view.center.y + point.y - senderHalfViewHeight) <= 12) {
                viewCenter.y = senderHalfViewHeight + 12;
            }
            if ((sender.view.center.y + point.y + senderHalfViewHeight) >= (ScreenHeight - 12)) {
                viewCenter.y = ScreenHeight - senderHalfViewHeight - 12;
            }
            sender.view.center = viewCenter;
        } completion:^(BOOL finished) {
            void (^endBlock)(CGRect endFrame) = objc_getAssociatedObject(self, ActionHandlerPanGestureKey);
            if (endBlock) {
                endBlock(sender.view.frame);
            }
        }];
        [sender setTranslation:CGPointMake(0, 0) inView:[sender.view superview]];
    } else {
        // UIGestureRecognizerStateBegan || UIGestureRecognizerStateChanged
        viewCenter.x = sender.view.center.x + point.x;
        viewCenter.y = sender.view.center.y + point.y;
        sender.view.center = viewCenter;
        [sender setTranslation:CGPointMake(0, 0) inView:[sender.view superview]];
    }
}
  • UIGestureRecognizerState有个状态是UIGestureRecognizerStateEnded,当拖拽结束时,需要判断拖拽视图的位置是否超出屏幕边界,如果超出边界,需要回弹到边界内。
  • 回弹动画结束后,用runtime取出关联的block,把最终的视图的frame回调,供外部使用。

你可能感兴趣的:(iOS 给view加一个拖拽功能)