前言
现在的直播app都具有悬浮窗功能,悬浮窗可以拖拽,并且具有回弹动画,可以设计一个UIView的分类实现,减少侵入性。
主要代码及思路
思路
- 给view添加一个pan的手势,在gesture的状态中进行判断(例如边界的回弹),最终拖拽手势结束时,通过block把手势回调出去。
代码
#import "UIView+dragable.h"
#import <objc/runtime.h>
#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回调,供外部使用。
网友评论