1.懸浮球與設備劉海的安全距離無論是橫屏還是豎屏,懸浮球距離有劉海的一邊會留出安全距離設備方向的上下兩邊,也有安全距離 2.貼邊吸附方向和距離懸浮球只能貼設備方向的左右兩邊,需要貼上下兩邊自行調整距離邊緣的數值自行調整 3.切換橫豎屏,懸浮球自適應懸浮球位置切換橫豎屏後,等比例轉換的。 4.隱藏和顯 ...
1.懸浮球與設備劉海的安全距離
無論是橫屏還是豎屏,懸浮球距離有劉海的一邊會留出安全距離
設備方向的上下兩邊,也有安全距離
2.貼邊吸附方向和距離
懸浮球只能貼設備方向的左右兩邊,需要貼上下兩邊自行調整
距離邊緣的數值自行調整
3.切換橫豎屏,懸浮球自適應
懸浮球位置切換橫豎屏後,等比例轉換的。
4.隱藏和顯示
拖到屏幕中間ImageView範圍內可以隱藏懸浮窗,在範圍內會由藍色變紅色,可自定義圖片或者大小
使用說明:
懸浮球點擊事件代理方法- (void)suspendViewButtonClick:(UIButton*)sender;
懸浮球在ImageView範圍內提示是否隱藏懸浮窗- (void)showHideAlertView;
顯示懸浮窗- (void)showSuspendView;
隱藏懸浮窗- (void)dismissSuspendView;
demo下載地址:
https://github.com/longypjiangxi/XLUIDragButton
簡書地址:
https://www.jianshu.com/p/30aeb1d506d3
#import <UIKit/UIKit.h> NS_ASSUME_NONNULL_BEGIN @protocol SuspendViewDelegate <NSObject> - (void)suspendViewButtonClick:(UIButton*)sender; - (void)showHideAlertView; @end @interface SuspendView : UIView { CGPoint lastPoint;/**存儲懸浮球最後的移動完位置*/ BOOL isChangePosition;/**懸浮球是否改變了位置*/ CGFloat changeHig;//按鈕高度位置比例 CGFloat changeWid;//按鈕寬度位置比例 } @property (nonatomic, retain) UIButton *btn;/**<#name#>*/ @property (nonatomic, strong) NSTimer *_Nullable timer; @property (nonatomic, retain) UIImageView *imageView; @property (nonatomic, assign) UIInterfaceOrientation orientation; @property (nonatomic, weak) id<SuspendViewDelegate> delegate; - (void)showSuspendView; - (void)dismissSuspendView; @end NS_ASSUME_NONNULL_END #import "SuspendView.h" #define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width #define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height #define ViewSize 50 #define KHeightFit(w) (((w) / 667.0) * SCREEN_HEIGHT) #define LRString [NSString stringWithFormat:@"%s", __FILE__].lastPathComponent #define DLog(...) {\ NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];\ [dateFormatter setDateFormat:@"YYYY-MM-dd hh:mm:ss"];\ NSString *dateString = [dateFormatter stringFromDate:[NSDate date]];\ printf("%s %s 第%d行:%s\n\n",[dateString UTF8String],[LRString UTF8String] ,__LINE__, [[NSString stringWithFormat:__VA_ARGS__] UTF8String]);} @implementation SuspendView - (instancetype)init{ self = [super init]; if (self) { self.backgroundColor = UIColor.redColor; self.layer.masksToBounds = YES; self.layer.cornerRadius = ViewSize/2; self.alpha = 0.5; //獲取設備方向 self.orientation = [[UIApplication sharedApplication] statusBarOrientation]; if (self.orientation == UIInterfaceOrientationLandscapeRight){//橫向home鍵在右側,設備左轉,劉海在左邊 self.frame = CGRectMake(SCREEN_WIDTH - [self vg_safeDistanceTop] - ViewSize - 20, KHeightFit(80) + ViewSize/2, ViewSize, ViewSize); }else{ self.frame = CGRectMake(SCREEN_WIDTH - ViewSize/2, KHeightFit(80) + ViewSize/2, ViewSize, ViewSize); } self.btn = [UIButton buttonWithType:UIButtonTypeCustom]; self.btn.frame = CGRectMake(5, 5, 40, 40); self.btn.backgroundColor = UIColor.greenColor; self.btn.layer.cornerRadius = 20; [self.btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside]; [self addSubview:self.btn]; //獲取按鈕與屏幕初始寬高比例 [self changeCoordinateScale]; //是否改變了懸浮窗初始位置 isChangePosition = NO; //添加手勢 UIPanGestureRecognizer *panRcognize=[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)]; [panRcognize setMinimumNumberOfTouches:1]; [panRcognize setEnabled:YES]; [panRcognize delaysTouchesEnded]; [panRcognize cancelsTouchesInView]; [self addGestureRecognizer:panRcognize]; //監聽屏幕旋轉 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didChangeStatusBarOrientation) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; } return self; } - (void)didChangeStatusBarOrientation { self.orientation = [UIApplication sharedApplication].statusBarOrientation; self.imageView.frame = CGRectMake((SCREEN_WIDTH - 50)/2, (SCREEN_HEIGHT - 50)/2, 50, 50); // DLog(@"===%zd=====%zd",[[UIDevice currentDevice] orientation],[UIApplication sharedApplication].statusBarOrientation); //請註意,UIInterfaceOrientationAndScapeLeft等於UIDeviceOrientation AndScapeRight(反之亦然)。 //這是因為向左旋轉設備需要向右旋轉內容。 /** UIInterfaceOrientationUnknown = UIDeviceOrientationUnknown, UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait, UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown, UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight, UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft */ /** UIDeviceOrientationUnknown, UIDeviceOrientationPortrait, // Device oriented vertically, home button on the bottom UIDeviceOrientationPortraitUpsideDown, // Device oriented vertically, home button on the top UIDeviceOrientationLandscapeLeft, // Device oriented horizontally, home button on the right UIDeviceOrientationLandscapeRight, // Device oriented horizontally, home button on the left UIDeviceOrientationFaceUp, // Device oriented flat, face up UIDeviceOrientationFaceDown // Device oriented flat, face down */ switch ([UIDevice currentDevice].orientation) { case UIDeviceOrientationPortraitUpsideDown: // DLog(@"設備倒垂直,home在上") [self locationChange:@"Down"]; break; case UIDeviceOrientationLandscapeLeft:{ // DLog(@"設備橫屏,左轉,home在右") [self locationChange:@"left"]; } break; case UIDeviceOrientationLandscapeRight:{ // DLog(@"設備橫屏,右轉,home在左") [self locationChange:@"right"]; } break; case UIDeviceOrientationPortrait:{ // DLog(@"設備垂直,home在下"); [self locationChange:@"Portrait"]; } break; default: break; } } //根據屏幕寬高改變按鈕位置比例 - (void)locationChange:(NSString *)message{ // NSLog(@"changeHig == %f,changeWid == %f",changeHig,changeWid); if (SCREEN_HEIGHT > SCREEN_WIDTH) { //屏幕方向上 if ([message isEqualToString:@"Portrait"]) { NSLog(@"安全區在上邊"); self.center = CGPointMake(changeWid * SCREEN_WIDTH, changeHig * SCREEN_HEIGHT); }else{//下 NSLog(@"安全區在下邊"); self.center = CGPointMake(changeWid * SCREEN_WIDTH, changeHig * SCREEN_HEIGHT - [self vg_safeDistanceTop]); } }else{ if ([message isEqualToString:@"left"]) {//左 NSLog(@"安全區在左邊"); self.center = CGPointMake(changeWid * SCREEN_WIDTH + [self vg_safeDistanceTop] + ViewSize, changeHig * SCREEN_HEIGHT); }else{//右 NSLog(@"安全區在右邊"); self.center = CGPointMake(changeWid * SCREEN_WIDTH - [self vg_safeDistanceTop] - ViewSize, changeHig * SCREEN_HEIGHT); } } // NSLog(@"lastPoint == %@, self.center == %@",NSStringFromCGPoint(lastPoint),NSStringFromCGPoint(self.center)); [self changeCoordinateScale]; } //旋轉屏幕後修改懸浮窗相對於屏幕的寬高比例以及坐標位置 - (void)changeCoordinateScale{ changeHig = self.center.y/SCREEN_HEIGHT; changeWid = self.center.x/SCREEN_WIDTH; //判斷設備旋轉方向 if (self.orientation == UIInterfaceOrientationLandscapeRight) {//橫向home鍵在右側,設備左轉,劉海在左邊,劉海在左邊 //判斷懸浮窗坐標x在屏幕的左邊還是右邊 if (self.center.x > SCREEN_WIDTH/2) {//大於中心x,在右邊 //修改懸浮窗的坐標在最右邊 self.center = CGPointMake(SCREEN_WIDTH, self.center.y); }else{ //修改懸浮窗的坐標在最左邊 self.center = CGPointMake([self vg_safeDistanceTop] + ViewSize + 20, self.center.y); } }else if(self.orientation == UIInterfaceOrientationLandscapeLeft){//橫向home鍵在左側,設備右轉,劉海在右邊 if (self.center.x > SCREEN_WIDTH/2) {//大於中心x,在右邊 //修改懸浮窗的坐標在最右邊,留出頂部安全距離 self.center = CGPointMake(SCREEN_WIDTH - [self vg_safeDistanceTop] - ViewSize - 20, self.center.y); }else{ //修改懸浮窗的坐標在最左邊 self.center = CGPointMake(0, self.center.y); } }else{ //大於中心x,在右邊 if (self.center.x > SCREEN_WIDTH/2) { self.center = CGPointMake(SCREEN_WIDTH, self.center.y); }else{ self.center = CGPointMake(0, self.center.y); } } // NSLog(@"changeHig == %f,changeWid == %f",changeHig,changeWid); // NSLog(@"設備寬度 == %f, 設備高度== %f, 按鈕坐標==%@",SCREEN_WIDTH,SCREEN_HEIGHT,NSStringFromCGPoint(self.center)); } - (void)showSuspendView{ self.hidden = NO; NSLog(@"顯示懸浮窗"); } - (void)dismissSuspendView{ self.hidden = YES; NSLog(@"隱藏懸浮窗"); } /// 懸浮窗按鈕點擊放法 /// @param button 點擊之後完全顯示懸浮窗,改變按鈕位置 - (void)btnClick:(UIButton *)button{ if (self.delegate && [self.delegate respondsToSelector:@selector(suspendViewButtonClick:)]) { [self.delegate suspendViewButtonClick:button]; } // DLog(@"lastPoint == %@",NSStringFromCGPoint(lastPoint)); //如果沒有改變過位置,lastPoint初始值(0,0) //判斷是否移動過懸浮窗 if (!isChangePosition) { //懸浮窗初始位置在右上角,只有屏幕向右旋轉,才需要留出iphone劉海的位置,設備左轉劉海在左邊,所以不需要做判斷 if (self.orientation == UIInterfaceOrientationLandscapeLeft) {//橫向home鍵在左側,設備右轉,劉海在右邊 //修改點擊後懸浮窗的位置,留出安全距離 [UIView animateWithDuration:0.5 animations:^{ self.center = CGPointMake(SCREEN_WIDTH - [self vg_safeDistanceTop] - ViewSize - 20 - 20, self.center.y); }]; }else{ [UIView animateWithDuration:0.5 animations:^{ self.center = CGPointMake(SCREEN_WIDTH - ViewSize, self.center.y); }]; } }else{ // 判斷最後的坐標是靠左還是靠右 if (self.orientation == UIInterfaceOrientationLandscapeRight) {//橫向home鍵在右側,設備左轉,劉海在左邊 if (self.center.x > SCREEN_WIDTH/2) {//懸浮窗在屏幕右側 [UIView animateWithDuration:0.5 animations:^{ self.center = CGPointMake(SCREEN_WIDTH - ViewSize, self.center.y); }]; }else{ //左轉劉海在左邊,留出安全距離 [UIView animateWithDuration:0.5 animations:^{ self.center = CGPointMake([self vg_safeDistanceTop] + ViewSize + 20 + 20, self.center.y); }]; } }else if(self.orientation == UIInterfaceOrientationLandscapeLeft){//橫向home鍵在左側,設備右轉,劉海在右邊 if (self.center.x > SCREEN_WIDTH/2) {//懸浮窗在屏幕右側,留出劉海安全距離 [UIView animateWithDuration:0.5 animations:^{ self.center = CGPointMake(SCREEN_WIDTH - [self vg_safeDistanceTop] - ViewSize - 20 - 20, self.center.y); }]; }else{//左側顯示 [UIView animateWithDuration:0.5 animations:^{ self.center = CGPointMake(ViewSize, self.center.y); }]; } }else{ if (self.center.x < SCREEN_WIDTH/2) {//懸浮窗在屏幕右側 [UIView animateWithDuration:0.5 animations:^{ self.center = CGPointMake(ViewSize, self.center.y); }]; }else{ [UIView animateWithDuration:0.5 animations:^{ self.center = CGPointMake(SCREEN_WIDTH - ViewSize, self.center.y); }]; } } } self.alpha = 1; //三秒後隱藏懸浮窗,貼邊展示一半 self.timer = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(timerAction) userInfo:nil repeats:NO]; } - (void)timerAction{ //隱藏懸浮球 self.alpha = 0.5; //判斷是否移動過懸浮窗 if (!isChangePosition) { //懸浮窗初始位置在右上角,只有屏幕向右旋轉,才需要留出iphone劉海的位置,設備左轉劉海在左邊,所以不需要做判斷 if (self.orientation == UIInterfaceOrientationLandscapeLeft) {//橫向home鍵在左側,設備右轉,劉海在右邊 [UIView animateWithDuration:0.5 animations:^{ self.center = CGPointMake(SCREEN_WIDTH - [self vg_safeDistanceTop] - ViewSize - 20, self.center.y); }]; }else{ [UIView animateWithDuration:0.5 animations:^{ self.center = CGPointMake(SCREEN_WIDTH, self.center.y); }]; } }else{ if (self.orientation == UIInterfaceOrientationLandscapeRight) {//橫向home鍵在右側,設備左轉,劉海在左邊 if (self.center.x > SCREEN_WIDTH/2) {//懸浮窗在屏幕右側 [UIView animateWithDuration:0.5 animations:^{ self.center = CGPointMake(SCREEN_WIDTH, self.center.y); }]; }else{ //懸浮窗在屏幕左側,留出劉海安全距離 [UIView animateWithDuration:0.5 animations:^{ self.center = CGPointMake([self vg_safeDistanceTop] + ViewSize + 20, self.center.y); }]; } }else if(self.orientation == UIInterfaceOrientationLandscapeLeft){//橫向home鍵在左側,設備右轉,劉海在右邊 if (self.center.x > SCREEN_WIDTH/2) {//懸浮窗在屏幕右側 //懸浮窗在屏幕左側,留出劉海安全距離 [UIView animateWithDuration:0.5 animations:^{ self.center = CGPointMake(SCREEN_WIDTH - [self vg_safeDistanceTop] - ViewSize - 20, self.center.y); }]; }else{ [UIView animateWithDuration:0.5 animations:^{ self.center = CGPointMake(0, self.center.y); }]; } }else{ if (self.center.x > SCREEN_WIDTH/2) {//懸浮窗在屏幕右側 [UIView animateWithDuration:0.5 animations:^{ self.center = CGPointMake(SCREEN_WIDTH, self.center.y); }]; }else{ [UIView animateWithDuration:0.5 animations:^{ self.center = CGPointMake(0, self.center.y); }]; } } } //銷毀定時器 [self.timer invalidate]; self.timer = nil; } /// pan手勢 /// @param recognizer recognizer description - (void)handlePanGesture:(UIPanGestureRecognizer *)recognizer { //移動狀態 UIGestureRecognizerState recState = recognizer.state; isChangePosition = YES; switch (recState) { case UIGestureRecognizerStateBegan: self.alpha = 1; self.imageView.hidden = NO; break; case UIGestureRecognizerStateChanged://移動中 { self.alpha = 1; CGPoint translation = [recognizer translationInView:self]; recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x, recognizer.view.center.y + translation.y); CGRect rect = [self convertRect:self.frame toView:self]; if (CGRectIntersectsRect(self.imageView.frame, rect)) {//在範圍內 self.imageView.backgroundColor = UIColor.redColor; }else{ self.imageView.backgroundColor = UIColor.blueColor; } } break; case UIGestureRecognizerStateEnded://移動結束 { self.alpha = 0.5; CGPoint stopPoint = CGPointMake(0, SCREEN_HEIGHT / 2); //判斷按鈕貼靠在屏幕的左邊還是右邊 if (recognizer.view.center.x < SCREEN_WIDTH / 2) { stopPoint = CGPointMake(ViewSize/2, recognizer.view.center.y); }else{ //貼靠在右邊 stopPoint = CGPointMake(SCREEN_WIDTH - ViewSize/2,recognizer.view.center.y); } DLog(@"stopPoint == %@",NSStringFromCGPoint(stopPoint)); if (stopPoint.y - ViewSize/2 <= 0) { DLog(@"上"); //加上電池欄的高度 if (stopPoint.x - ViewSize/2 <= SCREEN_WIDTH/2) { stopPoint = CGPointMake(0, stopPoint.y + [self vg_safeDistanceTop] + ViewSize); DLog(@"左上"); }else{ DLog(@"右上"); stopPoint = CGPointMake(SCREEN_WIDTH, stopPoint.y + [self vg_safeDistanceTop] + ViewSize); } } //如果按鈕超出屏幕邊緣 if (stopPoint.y + ViewSize + 20 >= SCREEN_HEIGHT) { DLog(@"下"); //減去底部狀態欄的高度 if (stopPoint.x - ViewSize/2 <= SCREEN_WIDTH/2) { DLog(@"左下"); stopPoint = CGPointMake(0, stopPoint.y - [self vg_safeDistanceBottom] - ViewSize/2); }else{ DLog(@"右下"); stopPoint = CGPointMake(SCREEN_WIDTH, stopPoint.y - [self vg_safeDistanceBottom] - ViewSize/2); } // DLog(@"超出屏幕下方"); } if (stopPoint.x - ViewSize/2 <= 0) { DLog(@"左"); // stopPoint = CGPointMake(ViewSize/2, stopPoint.y); //縮進去一半 stopPoint = CGPointMake(0, stopPoint.y); } if (stopPoint.x + ViewSize/2 >= SCREEN_WIDTH) { DLog(@"右"); // stopPoint = CGPointMake(SCREEN_WIDTH - ViewSize/2, stopPoint.y); stopPoint = CGPointMake(SCREEN_WIDTH, stopPoint.y); } //保存最後的位置 lastPoint = stopPoint; //隱藏懸浮球 CGRect rect = [self convertRect:self.frame toView:self]; if (CGRectIntersectsRect(self.imageView.frame, rect)) {//在範圍內 DLog(@"懸浮窗在中心imageview內,提示是否隱藏懸浮窗"); // [self showAlertView]; [self.delegate showHideAlertView]; } // NSLog(@"self.orientation == %ld",(long)self.orientation); if (self.orientation == UIInterfaceOrientationLandscapeRight) {//橫向home鍵在右側,設備左轉,劉海在左邊 if (stopPoint.x > SCREEN_WIDTH/2) {//懸浮窗在屏幕右側 [UIView animateWithDuration:0.5 animations:^{ recognizer.view.center = CGPointMake(SCREEN_WIDTH, stopPoint.y); }]; }else{ //懸浮窗在屏幕左側,留出劉海安全距離 [UIView animateWithDuration:0.5 animations:^{ recognizer.view.center = CGPointMake([self vg_safeDistanceTop] + ViewSize + 20, stopPoint.y); }]; } }else if(self.orientation == UIInterfaceOrientationLandscapeLeft){//橫向home鍵在左側,設備右轉,劉海在右邊 if (stopPoint.x > SCREEN_WIDTH/2) {//懸浮窗在屏幕右側 //懸浮窗在屏幕左側,留出劉海安全距離 [UIView animateWithDuration:0.5 animations:^{ recognizer.view.center = CGPointMake(SCREEN_WIDTH - [self vg_safeDistanceTop] - ViewSize - 20, stopPoint.y); }]; }else{ [UIView animateWithDuration:0.5 animations:^{ recognizer.view.center = CGPointMake(0, stopPoint.y); }]; } }else{ [UIView animateWithDuration:0.5 animations:^{ recognizer.view.center = stopPoint; }]; } [self changeCoordinateScale]; self.imageView.hidden = YES; } break; default: break; } [recognizer setTranslation:CGPointMake(0, 0) inView:self]; } //獲取頭部安全區高度 - (CGFloat)vg_safeDistanceTop { if (@available(iOS 13.0, *)) { NSSet *set = [UIApplication sharedApplication].connectedScenes; UIWindowScene *windowScene = [set anyObject]; UIWindow *window = windowScene.windows.firstObject; return window.safeAreaInsets.top; } else if (@available(iOS 11.0, *)) { UIWindow *window = [UIApplication sharedApplication].windows.firstObject; return window.safeAreaInsets.top; } return 0; } //獲取設備底部安全區高度 - (CGFloat)vg_safeDistanceBottom { if (@available(iOS 13.0, *)) { NSSet *set = [UIApplication sharedApplication].connectedScenes; UIWindowScene *windowScene = [set anyObject]; UIWindow *window = windowScene.windows.firstObject; return window.safeAreaInsets.bottom; } else if (@available(iOS 11.0, *)) { UIWindow *window = [UIApplication sharedApplication].windows.firstObject; return window.safeAreaInsets.bottom; } return 0; } - (UIImageView *)imageView{ if (!_imageView) { _imageView = [[UIImageView alloc]initWithFrame:CGRectMake((SCREEN_WIDTH - 50)/2, (SCREEN_HEIGHT - 50)/2, 50, 50)]; _imageView.backgroundColor = UIColor.blueColor; _imageView.hidden = YES; [[UIApplication sharedApplication].keyWindow addSubview:_imageView]; } return _imageView; } @end