前言
現(xiàn)在的直播app都具有懸浮窗功能,懸浮窗可以拖拽,并且具有回彈動(dòng)畫暮屡,可以設(shè)計(jì)一個(gè)UIView的分類實(shí)現(xiàn)驻龟,減少侵入性温眉。
主要代碼及思路
思路
- 給view添加一個(gè)pan的手勢,在gesture的狀態(tài)中進(jìn)行判斷(例如邊界的回彈)翁狐,最終拖拽手勢結(jié)束時(shí)类溢,通過block把手勢回調(diào)出去。
代碼
#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關(guān)聯(lián)一下需要回調(diào)的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);
// 拖拽狀態(tài)結(jié)束
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
有個(gè)狀態(tài)是UIGestureRecognizerStateEnded
闯冷,當(dāng)拖拽結(jié)束時(shí),需要判斷拖拽視圖的位置是否超出屏幕邊界隐锭,如果超出邊界窃躲,需要回彈到邊界內(nèi)。 - 回彈動(dòng)畫結(jié)束后钦睡,用runtime取出關(guān)聯(lián)的block蒂窒,把最終的視圖的frame回調(diào),供外部使用荞怒。