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
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/502688.html
標籤:iOS
