├── SuspendViewDemo ├── SuspendViewDemo │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── ViewController.h │ ├── AppDelegate.h │ ├── Info.plist │ ├── main.m │ ├── AppDelegate.m │ ├── SuspendView.h │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ ├── ViewController.m │ └── SuspendView.m └── SuspendViewDemo.xcodeproj │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcuserdata │ │ └── lyp.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ ├── xcuserdata │ └── lyp.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist │ ├── xcshareddata │ └── xcschemes │ │ └── SuspendViewDemo.xcscheme │ └── project.pbxproj └── README.md /SuspendViewDemo/SuspendViewDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SuspendViewDemo/SuspendViewDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SuspendViewDemo/SuspendViewDemo/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /SuspendViewDemo/SuspendViewDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // 4 | // 5 | // Created by LYP on 2022/8/4. 6 | // 7 | 8 | #import 9 | 10 | @interface ViewController : UIViewController 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /SuspendViewDemo/SuspendViewDemo.xcodeproj/project.xcworkspace/xcuserdata/lyp.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longypjiangxi/XLUIDragButton/HEAD/SuspendViewDemo/SuspendViewDemo.xcodeproj/project.xcworkspace/xcuserdata/lyp.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /SuspendViewDemo/SuspendViewDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // 4 | // 5 | // Created by LYP on 2022/8/4. 6 | // 7 | 8 | #import 9 | 10 | @interface AppDelegate : UIResponder 11 | 12 | @property (strong, nonatomic) UIWindow * window; 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /SuspendViewDemo/SuspendViewDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SuspendViewDemo 2 | 3 | iOS-OC 全局悬浮球效果实现,悬浮按钮,拖拽拖动,贴边吸附,隐藏和显示,切换横竖屏适配,坐标转换 4 | 5 | 麻烦动动发财的小手点个Star💗 6 | 7 | demo演示,gif图,代码功能介绍,以下为作者原文地址,有问题请私信或评论 8 | 9 | 简书地址:https://www.jianshu.com/p/30aeb1d506d3 10 | 11 | 博客地址:https://www.cnblogs.com/longyongping/p/16601562.html 12 | 13 | CSDN地址:https://blog.csdn.net/qq_37926094/article/details/126467828 14 | -------------------------------------------------------------------------------- /SuspendViewDemo/SuspendViewDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSAppTransportSecurity 6 | 7 | NSAllowsArbitraryLoads 8 | 9 | 10 | UIViewControllerBasedStatusBarAppearance 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /SuspendViewDemo/SuspendViewDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // 4 | // 5 | // Created by LYP on 2022/8/4. 6 | // 7 | 8 | #import 9 | #import "AppDelegate.h" 10 | 11 | int main(int argc, char * argv[]) { 12 | NSString * appDelegateClassName; 13 | @autoreleasepool { 14 | // Setup code that might create autoreleased objects goes here. 15 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 16 | } 17 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 18 | } 19 | -------------------------------------------------------------------------------- /SuspendViewDemo/SuspendViewDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // 4 | // 5 | // Created by LYP on 2022/8/4. 6 | // 7 | 8 | #import "AppDelegate.h" 9 | #import "ViewController.h" 10 | @interface AppDelegate () 11 | 12 | @end 13 | 14 | @implementation AppDelegate 15 | 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 18 | self.window=[[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds]; 19 | self.window.rootViewController = [[ViewController alloc]init]; 20 | [self.window makeKeyAndVisible]; 21 | 22 | return YES; 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /SuspendViewDemo/SuspendViewDemo.xcodeproj/xcuserdata/lyp.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SuspendViewDemo.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 1 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 3406E61D289BACF4006722D7 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /SuspendViewDemo/SuspendViewDemo/SuspendView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SuspendView.h 3 | // 4 | // 5 | // Created by LYP on 2022/8/10. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | @protocol SuspendViewDelegate 12 | //点击事件 13 | - (void)suspendViewButtonClick:(UIButton*)sender; 14 | //显示是否隐藏提示框 15 | - (void)showHideAlertView; 16 | @end 17 | @interface SuspendView : UIView 18 | { 19 | CGPoint lastPoint;/**存储悬浮球最后的移动完位置*/ 20 | BOOL isChangePosition;/**悬浮球是否改变了位置*/ 21 | CGFloat changeHig;//按钮高度位置比例 22 | CGFloat changeWid;//按钮宽度位置比例 23 | 24 | } 25 | @property (nonatomic, retain) UIButton *btn;/**<#name#>*/ 26 | @property (nonatomic, strong) NSTimer *_Nullable timer; 27 | @property (nonatomic, retain) UIImageView *imageView; 28 | @property (nonatomic, assign) UIInterfaceOrientation orientation; 29 | @property (nonatomic, weak) id delegate; 30 | //隐藏&显示 31 | - (void)showSuspendView; 32 | - (void)dismissSuspendView; 33 | @end 34 | 35 | NS_ASSUME_NONNULL_END 36 | -------------------------------------------------------------------------------- /SuspendViewDemo/SuspendViewDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /SuspendViewDemo/SuspendViewDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /SuspendViewDemo/SuspendViewDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /SuspendViewDemo/SuspendViewDemo.xcodeproj/xcshareddata/xcschemes/SuspendViewDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /SuspendViewDemo/SuspendViewDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // 4 | // 5 | // Created by LYP on 2022/8/4. 6 | // 7 | 8 | #import "ViewController.h" 9 | #import "SuspendView.h" 10 | #define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width 11 | #define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height 12 | 13 | @interface ViewController () 14 | 15 | @property (nonatomic, retain) UIButton *loginBtn;/**<#name#>*/ 16 | @property (nonatomic, retain) UIButton *signOutBtn;/**<#name#>*/ 17 | @property (nonatomic, retain) UIButton *menuBtn;/**<#name#>*/ 18 | @property (nonatomic, retain) SuspendView *suspendView;/**<#name#>*/ 19 | @property (nonatomic, strong) NSTimer* timer;/**<#name#>*/ 20 | @end 21 | 22 | @implementation ViewController 23 | 24 | - (void)viewDidLoad { 25 | [super viewDidLoad]; 26 | // Do any additional setup after loading the view. 27 | 28 | self.view.backgroundColor = [UIColor grayColor]; 29 | [self.view addSubview:self.menuBtn]; 30 | 31 | _timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:false block:^(NSTimer * _Nonnull timer) { 32 | [[UIApplication sharedApplication].keyWindow addSubview:self.suspendView]; 33 | [self->_timer invalidate]; 34 | self->_timer = nil; 35 | }]; 36 | 37 | [self configSYSDKNotification]; 38 | 39 | } 40 | 41 | - (void)uploadViewFrame{ 42 | self.menuBtn.frame = CGRectMake((SCREEN_WIDTH - 150)/2, 100, 150, 80); 43 | } 44 | 45 | #pragma mark ========================= SuspendViewDelegate ========================= 46 | - (void)suspendViewButtonClick:(nonnull UIButton *)sender { 47 | NSLog(@"按钮点击事件"); 48 | } 49 | 50 | - (void)showHideAlertView{ 51 | UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"提示" message:@"是否隐藏悬浮窗" preferredStyle:UIAlertControllerStyleAlert]; 52 | // 增加取消按钮; 53 | [alert addAction:[UIAlertAction actionWithTitle:@"否" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 54 | NSLog(@"不隐藏"); 55 | }]]; 56 | // 增加确定按钮; 57 | [alert addAction:[UIAlertAction actionWithTitle:@"是" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 58 | [self.suspendView dismissSuspendView]; 59 | }]]; 60 | [self presentViewController:alert animated:true completion:nil]; 61 | } 62 | 63 | #pragma mark ========================= 通知 ========================= 64 | - (void)configSYSDKNotification{ 65 | 66 | [[NSNotificationCenter defaultCenter] addObserver:self 67 | selector:@selector(didChangeStatusBarOrientation) 68 | name:UIApplicationDidChangeStatusBarOrientationNotification 69 | object:nil]; 70 | } 71 | 72 | 73 | - (void)didChangeStatusBarOrientation { 74 | /** 75 | UIDeviceOrientationUnknown, 76 | UIDeviceOrientationPortrait, // Device oriented vertically, home button on the bottom 77 | UIDeviceOrientationPortraitUpsideDown, // Device oriented vertically, home button on the top 78 | UIDeviceOrientationLandscapeLeft, // Device oriented horizontally, home button on the right 79 | UIDeviceOrientationLandscapeRight, // Device oriented horizontally, home button on the left 80 | UIDeviceOrientationFaceUp, // Device oriented flat, face up 81 | UIDeviceOrientationFaceDown // Device oriented flat, face down 82 | */ 83 | switch ([UIDevice currentDevice].orientation) 84 | { 85 | case UIDeviceOrientationPortraitUpsideDown: 86 | // NSLog(@"faceBar在下面"); 87 | break; 88 | case UIDeviceOrientationLandscapeLeft: 89 | // NSLog(@"faceBar在左边"); 90 | break; 91 | case UIDeviceOrientationLandscapeRight: 92 | // NSLog(@"faceBar在右边"); 93 | break; 94 | case UIDeviceOrientationPortrait: 95 | // NSLog(@"faceBar在上边"); 96 | break; 97 | default: // as UIInterfaceOrientationPortrait 98 | // NSLog(@"faceBar在上边"); 99 | break; 100 | } 101 | 102 | [self uploadViewFrame]; 103 | } 104 | 105 | #pragma mark ========================= Click ========================= 106 | 107 | - (void)menuBtnClick:(UIButton *)button{ 108 | [self.suspendView showSuspendView]; 109 | } 110 | 111 | #pragma mark ========================= 懒加载 ========================= 112 | 113 | - (SuspendView *)suspendView { 114 | if (!_suspendView) { 115 | _suspendView = [[SuspendView alloc] init]; 116 | _suspendView.delegate = self; 117 | } 118 | return _suspendView; 119 | } 120 | 121 | - (UIButton *)menuBtn{ 122 | if(!_menuBtn){ 123 | _menuBtn = [UIButton buttonWithType:UIButtonTypeCustom]; 124 | _menuBtn.frame = CGRectMake((SCREEN_WIDTH - 150)/2, 100, 150, 80); 125 | _menuBtn.layer.cornerRadius = 40; 126 | [_menuBtn setBackgroundColor: UIColor.whiteColor]; 127 | [_menuBtn setTitle:@"显示悬浮窗" forState:UIControlStateNormal]; 128 | [_menuBtn setTitleColor:UIColor.blackColor forState:UIControlStateNormal]; 129 | [_menuBtn addTarget:self action:@selector(menuBtnClick:) forControlEvents:UIControlEventTouchUpInside]; 130 | } 131 | return _menuBtn; 132 | } 133 | 134 | @end 135 | -------------------------------------------------------------------------------- /SuspendViewDemo/SuspendViewDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3406E623289BACF4006722D7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3406E622289BACF4006722D7 /* AppDelegate.m */; }; 11 | 3406E629289BACF4006722D7 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3406E628289BACF4006722D7 /* ViewController.m */; }; 12 | 3406E62C289BACF4006722D7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3406E62A289BACF4006722D7 /* Main.storyboard */; }; 13 | 3406E62E289BACF6006722D7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3406E62D289BACF6006722D7 /* Assets.xcassets */; }; 14 | 3406E631289BACF6006722D7 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3406E62F289BACF6006722D7 /* LaunchScreen.storyboard */; }; 15 | 3406E634289BACF6006722D7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3406E633289BACF6006722D7 /* main.m */; }; 16 | 34977F9528A3561E0045D8EE /* SuspendView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34977F9428A3561E0045D8EE /* SuspendView.m */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXCopyFilesBuildPhase section */ 20 | 3406E641289BB3BE006722D7 /* CopyFiles */ = { 21 | isa = PBXCopyFilesBuildPhase; 22 | buildActionMask = 2147483647; 23 | dstPath = ""; 24 | dstSubfolderSpec = 10; 25 | files = ( 26 | ); 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXCopyFilesBuildPhase section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 3406E61E289BACF4006722D7 /* SuspendViewDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SuspendViewDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 3406E621289BACF4006722D7 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 34 | 3406E622289BACF4006722D7 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 35 | 3406E627289BACF4006722D7 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 36 | 3406E628289BACF4006722D7 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 37 | 3406E62B289BACF4006722D7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 38 | 3406E62D289BACF6006722D7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 39 | 3406E630289BACF6006722D7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 40 | 3406E632289BACF6006722D7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 41 | 3406E633289BACF6006722D7 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 42 | 34977F9328A3561E0045D8EE /* SuspendView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SuspendView.h; sourceTree = ""; }; 43 | 34977F9428A3561E0045D8EE /* SuspendView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SuspendView.m; sourceTree = ""; }; 44 | /* End PBXFileReference section */ 45 | 46 | /* Begin PBXFrameworksBuildPhase section */ 47 | 3406E61B289BACF4006722D7 /* Frameworks */ = { 48 | isa = PBXFrameworksBuildPhase; 49 | buildActionMask = 2147483647; 50 | files = ( 51 | ); 52 | runOnlyForDeploymentPostprocessing = 0; 53 | }; 54 | /* End PBXFrameworksBuildPhase section */ 55 | 56 | /* Begin PBXGroup section */ 57 | 3406E615289BACF4006722D7 = { 58 | isa = PBXGroup; 59 | children = ( 60 | 3406E620289BACF4006722D7 /* SuspendViewDemo */, 61 | 3406E61F289BACF4006722D7 /* Products */, 62 | ); 63 | sourceTree = ""; 64 | }; 65 | 3406E61F289BACF4006722D7 /* Products */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | 3406E61E289BACF4006722D7 /* SuspendViewDemo.app */, 69 | ); 70 | name = Products; 71 | sourceTree = ""; 72 | }; 73 | 3406E620289BACF4006722D7 /* SuspendViewDemo */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | 3406E621289BACF4006722D7 /* AppDelegate.h */, 77 | 3406E622289BACF4006722D7 /* AppDelegate.m */, 78 | 3406E627289BACF4006722D7 /* ViewController.h */, 79 | 3406E628289BACF4006722D7 /* ViewController.m */, 80 | 34977F9328A3561E0045D8EE /* SuspendView.h */, 81 | 34977F9428A3561E0045D8EE /* SuspendView.m */, 82 | 3406E62A289BACF4006722D7 /* Main.storyboard */, 83 | 3406E62D289BACF6006722D7 /* Assets.xcassets */, 84 | 3406E62F289BACF6006722D7 /* LaunchScreen.storyboard */, 85 | 3406E632289BACF6006722D7 /* Info.plist */, 86 | 3406E633289BACF6006722D7 /* main.m */, 87 | ); 88 | path = SuspendViewDemo; 89 | sourceTree = ""; 90 | }; 91 | /* End PBXGroup section */ 92 | 93 | /* Begin PBXNativeTarget section */ 94 | 3406E61D289BACF4006722D7 /* SuspendViewDemo */ = { 95 | isa = PBXNativeTarget; 96 | buildConfigurationList = 3406E637289BACF6006722D7 /* Build configuration list for PBXNativeTarget "SuspendViewDemo" */; 97 | buildPhases = ( 98 | 3406E61A289BACF4006722D7 /* Sources */, 99 | 3406E61B289BACF4006722D7 /* Frameworks */, 100 | 3406E61C289BACF4006722D7 /* Resources */, 101 | 3406E641289BB3BE006722D7 /* CopyFiles */, 102 | ); 103 | buildRules = ( 104 | ); 105 | dependencies = ( 106 | ); 107 | name = SuspendViewDemo; 108 | productName = SuspendViewDemo; 109 | productReference = 3406E61E289BACF4006722D7 /* SuspendViewDemo.app */; 110 | productType = "com.apple.product-type.application"; 111 | }; 112 | /* End PBXNativeTarget section */ 113 | 114 | /* Begin PBXProject section */ 115 | 3406E616289BACF4006722D7 /* Project object */ = { 116 | isa = PBXProject; 117 | attributes = { 118 | BuildIndependentTargetsInParallel = 1; 119 | LastUpgradeCheck = 1340; 120 | TargetAttributes = { 121 | 3406E61D289BACF4006722D7 = { 122 | CreatedOnToolsVersion = 13.4; 123 | }; 124 | }; 125 | }; 126 | buildConfigurationList = 3406E619289BACF4006722D7 /* Build configuration list for PBXProject "SuspendViewDemo" */; 127 | compatibilityVersion = "Xcode 13.0"; 128 | developmentRegion = en; 129 | hasScannedForEncodings = 0; 130 | knownRegions = ( 131 | en, 132 | Base, 133 | ); 134 | mainGroup = 3406E615289BACF4006722D7; 135 | productRefGroup = 3406E61F289BACF4006722D7 /* Products */; 136 | projectDirPath = ""; 137 | projectRoot = ""; 138 | targets = ( 139 | 3406E61D289BACF4006722D7 /* SuspendViewDemo */, 140 | ); 141 | }; 142 | /* End PBXProject section */ 143 | 144 | /* Begin PBXResourcesBuildPhase section */ 145 | 3406E61C289BACF4006722D7 /* Resources */ = { 146 | isa = PBXResourcesBuildPhase; 147 | buildActionMask = 2147483647; 148 | files = ( 149 | 3406E631289BACF6006722D7 /* LaunchScreen.storyboard in Resources */, 150 | 3406E62E289BACF6006722D7 /* Assets.xcassets in Resources */, 151 | 3406E62C289BACF4006722D7 /* Main.storyboard in Resources */, 152 | ); 153 | runOnlyForDeploymentPostprocessing = 0; 154 | }; 155 | /* End PBXResourcesBuildPhase section */ 156 | 157 | /* Begin PBXSourcesBuildPhase section */ 158 | 3406E61A289BACF4006722D7 /* Sources */ = { 159 | isa = PBXSourcesBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 3406E629289BACF4006722D7 /* ViewController.m in Sources */, 163 | 3406E623289BACF4006722D7 /* AppDelegate.m in Sources */, 164 | 3406E634289BACF6006722D7 /* main.m in Sources */, 165 | 34977F9528A3561E0045D8EE /* SuspendView.m in Sources */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXSourcesBuildPhase section */ 170 | 171 | /* Begin PBXVariantGroup section */ 172 | 3406E62A289BACF4006722D7 /* Main.storyboard */ = { 173 | isa = PBXVariantGroup; 174 | children = ( 175 | 3406E62B289BACF4006722D7 /* Base */, 176 | ); 177 | name = Main.storyboard; 178 | sourceTree = ""; 179 | }; 180 | 3406E62F289BACF6006722D7 /* LaunchScreen.storyboard */ = { 181 | isa = PBXVariantGroup; 182 | children = ( 183 | 3406E630289BACF6006722D7 /* Base */, 184 | ); 185 | name = LaunchScreen.storyboard; 186 | sourceTree = ""; 187 | }; 188 | /* End PBXVariantGroup section */ 189 | 190 | /* Begin XCBuildConfiguration section */ 191 | 3406E635289BACF6006722D7 /* Debug */ = { 192 | isa = XCBuildConfiguration; 193 | buildSettings = { 194 | ALWAYS_SEARCH_USER_PATHS = NO; 195 | CLANG_ANALYZER_NONNULL = YES; 196 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 197 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 198 | CLANG_ENABLE_MODULES = YES; 199 | CLANG_ENABLE_OBJC_ARC = YES; 200 | CLANG_ENABLE_OBJC_WEAK = YES; 201 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 202 | CLANG_WARN_BOOL_CONVERSION = YES; 203 | CLANG_WARN_COMMA = YES; 204 | CLANG_WARN_CONSTANT_CONVERSION = YES; 205 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 206 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 207 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 208 | CLANG_WARN_EMPTY_BODY = YES; 209 | CLANG_WARN_ENUM_CONVERSION = YES; 210 | CLANG_WARN_INFINITE_RECURSION = YES; 211 | CLANG_WARN_INT_CONVERSION = YES; 212 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 213 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 214 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 215 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 216 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 217 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 218 | CLANG_WARN_STRICT_PROTOTYPES = YES; 219 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 220 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 221 | CLANG_WARN_UNREACHABLE_CODE = YES; 222 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 223 | COPY_PHASE_STRIP = NO; 224 | DEBUG_INFORMATION_FORMAT = dwarf; 225 | ENABLE_STRICT_OBJC_MSGSEND = YES; 226 | ENABLE_TESTABILITY = YES; 227 | GCC_C_LANGUAGE_STANDARD = gnu11; 228 | GCC_DYNAMIC_NO_PIC = NO; 229 | GCC_NO_COMMON_BLOCKS = YES; 230 | GCC_OPTIMIZATION_LEVEL = 0; 231 | GCC_PREPROCESSOR_DEFINITIONS = ( 232 | "DEBUG=1", 233 | "$(inherited)", 234 | ); 235 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 236 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 237 | GCC_WARN_UNDECLARED_SELECTOR = YES; 238 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 239 | GCC_WARN_UNUSED_FUNCTION = YES; 240 | GCC_WARN_UNUSED_VARIABLE = YES; 241 | IPHONEOS_DEPLOYMENT_TARGET = 15.5; 242 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 243 | MTL_FAST_MATH = YES; 244 | ONLY_ACTIVE_ARCH = YES; 245 | SDKROOT = iphoneos; 246 | }; 247 | name = Debug; 248 | }; 249 | 3406E636289BACF6006722D7 /* Release */ = { 250 | isa = XCBuildConfiguration; 251 | buildSettings = { 252 | ALWAYS_SEARCH_USER_PATHS = NO; 253 | CLANG_ANALYZER_NONNULL = YES; 254 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 255 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 256 | CLANG_ENABLE_MODULES = YES; 257 | CLANG_ENABLE_OBJC_ARC = YES; 258 | CLANG_ENABLE_OBJC_WEAK = YES; 259 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 260 | CLANG_WARN_BOOL_CONVERSION = YES; 261 | CLANG_WARN_COMMA = YES; 262 | CLANG_WARN_CONSTANT_CONVERSION = YES; 263 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 264 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 265 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 266 | CLANG_WARN_EMPTY_BODY = YES; 267 | CLANG_WARN_ENUM_CONVERSION = YES; 268 | CLANG_WARN_INFINITE_RECURSION = YES; 269 | CLANG_WARN_INT_CONVERSION = YES; 270 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 271 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 272 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 273 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 274 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 275 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 276 | CLANG_WARN_STRICT_PROTOTYPES = YES; 277 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 278 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 279 | CLANG_WARN_UNREACHABLE_CODE = YES; 280 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 281 | COPY_PHASE_STRIP = NO; 282 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 283 | ENABLE_NS_ASSERTIONS = NO; 284 | ENABLE_STRICT_OBJC_MSGSEND = YES; 285 | GCC_C_LANGUAGE_STANDARD = gnu11; 286 | GCC_NO_COMMON_BLOCKS = YES; 287 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 288 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 289 | GCC_WARN_UNDECLARED_SELECTOR = YES; 290 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 291 | GCC_WARN_UNUSED_FUNCTION = YES; 292 | GCC_WARN_UNUSED_VARIABLE = YES; 293 | IPHONEOS_DEPLOYMENT_TARGET = 15.5; 294 | MTL_ENABLE_DEBUG_INFO = NO; 295 | MTL_FAST_MATH = YES; 296 | SDKROOT = iphoneos; 297 | VALIDATE_PRODUCT = YES; 298 | }; 299 | name = Release; 300 | }; 301 | 3406E638289BACF6006722D7 /* Debug */ = { 302 | isa = XCBuildConfiguration; 303 | buildSettings = { 304 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 305 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 306 | CODE_SIGN_STYLE = Automatic; 307 | CURRENT_PROJECT_VERSION = 1; 308 | DEVELOPMENT_TEAM = PH5NS9QCQ7; 309 | ENABLE_BITCODE = YES; 310 | EXCLUDED_ARCHS = ""; 311 | FRAMEWORK_SEARCH_PATHS = ( 312 | "$(inherited)", 313 | "$(PROJECT_DIR)/SuspendViewDemo/SDK", 314 | "\"$(SRCROOT)/SuspendViewDemo/SDK/SYSDK.framework\"", 315 | ); 316 | GENERATE_INFOPLIST_FILE = YES; 317 | INFOPLIST_FILE = SuspendViewDemo/Info.plist; 318 | INFOPLIST_KEY_CFBundleDisplayName = "悬浮窗"; 319 | INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "允许应用程序访问您的相册,以便保存图片到相册"; 320 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 321 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 322 | INFOPLIST_KEY_UIMainStoryboardFile = Main; 323 | INFOPLIST_KEY_UIStatusBarHidden = YES; 324 | INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 325 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 326 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 327 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 328 | LD_RUNPATH_SEARCH_PATHS = ( 329 | "$(inherited)", 330 | "@executable_path/Frameworks", 331 | ); 332 | LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/SuspendViewDemo/SDK/SYSDK.framework\""; 333 | MARKETING_VERSION = 1.0; 334 | ONLY_ACTIVE_ARCH = YES; 335 | OTHER_LDFLAGS = "-ObjC"; 336 | PRODUCT_BUNDLE_IDENTIFIER = com.long.SuspendViewDemo; 337 | PRODUCT_NAME = "$(TARGET_NAME)"; 338 | SWIFT_EMIT_LOC_STRINGS = YES; 339 | TARGETED_DEVICE_FAMILY = "1,2"; 340 | VALIDATE_WORKSPACE = YES; 341 | }; 342 | name = Debug; 343 | }; 344 | 3406E639289BACF6006722D7 /* Release */ = { 345 | isa = XCBuildConfiguration; 346 | buildSettings = { 347 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 348 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 349 | CODE_SIGN_STYLE = Automatic; 350 | CURRENT_PROJECT_VERSION = 1; 351 | DEVELOPMENT_TEAM = PH5NS9QCQ7; 352 | ENABLE_BITCODE = YES; 353 | EXCLUDED_ARCHS = ""; 354 | FRAMEWORK_SEARCH_PATHS = ( 355 | "$(inherited)", 356 | "$(PROJECT_DIR)/SuspendViewDemo/SDK", 357 | "\"$(SRCROOT)/SuspendViewDemo/SDK/SYSDK.framework\"", 358 | ); 359 | GENERATE_INFOPLIST_FILE = YES; 360 | INFOPLIST_FILE = SuspendViewDemo/Info.plist; 361 | INFOPLIST_KEY_CFBundleDisplayName = "悬浮窗"; 362 | INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "允许应用程序访问您的相册,以便保存图片到相册"; 363 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 364 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 365 | INFOPLIST_KEY_UIMainStoryboardFile = Main; 366 | INFOPLIST_KEY_UIStatusBarHidden = YES; 367 | INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 368 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 369 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 370 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 371 | LD_RUNPATH_SEARCH_PATHS = ( 372 | "$(inherited)", 373 | "@executable_path/Frameworks", 374 | ); 375 | LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/SuspendViewDemo/SDK/SYSDK.framework\""; 376 | MARKETING_VERSION = 1.0; 377 | ONLY_ACTIVE_ARCH = NO; 378 | OTHER_LDFLAGS = "-ObjC"; 379 | PRODUCT_BUNDLE_IDENTIFIER = com.long.SuspendViewDemo; 380 | PRODUCT_NAME = "$(TARGET_NAME)"; 381 | SWIFT_EMIT_LOC_STRINGS = YES; 382 | TARGETED_DEVICE_FAMILY = "1,2"; 383 | VALIDATE_WORKSPACE = YES; 384 | }; 385 | name = Release; 386 | }; 387 | /* End XCBuildConfiguration section */ 388 | 389 | /* Begin XCConfigurationList section */ 390 | 3406E619289BACF4006722D7 /* Build configuration list for PBXProject "SuspendViewDemo" */ = { 391 | isa = XCConfigurationList; 392 | buildConfigurations = ( 393 | 3406E635289BACF6006722D7 /* Debug */, 394 | 3406E636289BACF6006722D7 /* Release */, 395 | ); 396 | defaultConfigurationIsVisible = 0; 397 | defaultConfigurationName = Release; 398 | }; 399 | 3406E637289BACF6006722D7 /* Build configuration list for PBXNativeTarget "SuspendViewDemo" */ = { 400 | isa = XCConfigurationList; 401 | buildConfigurations = ( 402 | 3406E638289BACF6006722D7 /* Debug */, 403 | 3406E639289BACF6006722D7 /* Release */, 404 | ); 405 | defaultConfigurationIsVisible = 0; 406 | defaultConfigurationName = Release; 407 | }; 408 | /* End XCConfigurationList section */ 409 | }; 410 | rootObject = 3406E616289BACF4006722D7 /* Project object */; 411 | } 412 | -------------------------------------------------------------------------------- /SuspendViewDemo/SuspendViewDemo/SuspendView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SuspendView.m 3 | // 4 | // 5 | // Created by LYP on 2022/8/10. 6 | // 7 | 8 | #import "SuspendView.h" 9 | 10 | #define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width 11 | #define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height 12 | #define ViewSize 50 13 | #define KHeightFit(w) (((w) / 667.0) * SCREEN_HEIGHT) 14 | 15 | #define LRString [NSString stringWithFormat:@"%s", __FILE__].lastPathComponent 16 | #define DLog(...) {\ 17 | NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];\ 18 | [dateFormatter setDateFormat:@"YYYY-MM-dd hh:mm:ss"];\ 19 | NSString *dateString = [dateFormatter stringFromDate:[NSDate date]];\ 20 | printf("%s %s 第%d行:%s\n\n",[dateString UTF8String],[LRString UTF8String] ,__LINE__, [[NSString stringWithFormat:__VA_ARGS__] UTF8String]);} 21 | 22 | @implementation SuspendView 23 | 24 | 25 | - (instancetype)init{ 26 | self = [super init]; 27 | if (self) { 28 | self.backgroundColor = UIColor.redColor; 29 | self.layer.masksToBounds = YES; 30 | self.layer.cornerRadius = ViewSize/2; 31 | self.alpha = 0.5; 32 | 33 | //获取设备方向 34 | self.orientation = [[UIApplication sharedApplication] statusBarOrientation]; 35 | if (self.orientation == UIInterfaceOrientationLandscapeRight){//横向home键在右侧,设备左转,刘海在左边 36 | self.frame = CGRectMake(SCREEN_WIDTH - [self vg_safeDistanceTop] - ViewSize - 20, KHeightFit(80) + ViewSize/2, ViewSize, ViewSize); 37 | }else{ 38 | self.frame = CGRectMake(SCREEN_WIDTH - ViewSize/2, KHeightFit(80) + ViewSize/2, ViewSize, ViewSize); 39 | } 40 | 41 | self.btn = [UIButton buttonWithType:UIButtonTypeCustom]; 42 | self.btn.frame = CGRectMake(5, 5, 40, 40); 43 | self.btn.backgroundColor = UIColor.greenColor; 44 | self.btn.layer.cornerRadius = 20; 45 | [self.btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside]; 46 | [self addSubview:self.btn]; 47 | //获取按钮与屏幕初始宽高比例 48 | [self changeCoordinateScale]; 49 | //是否改变了悬浮窗初始位置 50 | isChangePosition = NO; 51 | 52 | //添加手势 53 | UIPanGestureRecognizer *panRcognize=[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)]; 54 | [panRcognize setMinimumNumberOfTouches:1]; 55 | [panRcognize setEnabled:YES]; 56 | [panRcognize delaysTouchesEnded]; 57 | [panRcognize cancelsTouchesInView]; 58 | [self addGestureRecognizer:panRcognize]; 59 | 60 | //监听屏幕旋转 61 | [[NSNotificationCenter defaultCenter] addObserver:self 62 | selector:@selector(didChangeStatusBarOrientation) 63 | name:UIApplicationDidChangeStatusBarOrientationNotification 64 | object:nil]; 65 | } 66 | return self; 67 | } 68 | 69 | - (void)didChangeStatusBarOrientation { 70 | 71 | self.orientation = [UIApplication sharedApplication].statusBarOrientation; 72 | self.imageView.frame = CGRectMake((SCREEN_WIDTH - 50)/2, (SCREEN_HEIGHT - 50)/2, 50, 50); 73 | // DLog(@"===%zd=====%zd",[[UIDevice currentDevice] orientation],[UIApplication sharedApplication].statusBarOrientation); 74 | //请注意,UIInterfaceOrientationAndScapeLeft等于UIDeviceOrientation AndScapeRight(反之亦然)。 75 | //这是因为向左旋转设备需要向右旋转内容。 76 | /** 77 | UIInterfaceOrientationUnknown = UIDeviceOrientationUnknown, 78 | UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait, 79 | UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown, 80 | UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight, 81 | UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft 82 | */ 83 | 84 | /** 85 | UIDeviceOrientationUnknown, 86 | UIDeviceOrientationPortrait, // Device oriented vertically, home button on the bottom 87 | UIDeviceOrientationPortraitUpsideDown, // Device oriented vertically, home button on the top 88 | UIDeviceOrientationLandscapeLeft, // Device oriented horizontally, home button on the right 89 | UIDeviceOrientationLandscapeRight, // Device oriented horizontally, home button on the left 90 | UIDeviceOrientationFaceUp, // Device oriented flat, face up 91 | UIDeviceOrientationFaceDown // Device oriented flat, face down 92 | */ 93 | switch ([UIDevice currentDevice].orientation) 94 | { 95 | case UIDeviceOrientationPortraitUpsideDown: 96 | // DLog(@"设备倒垂直,home在上") 97 | [self locationChange:@"Down"]; 98 | break; 99 | case UIDeviceOrientationLandscapeLeft:{ 100 | // DLog(@"设备横屏,左转,home在右") 101 | [self locationChange:@"left"]; 102 | } 103 | break; 104 | case UIDeviceOrientationLandscapeRight:{ 105 | // DLog(@"设备横屏,右转,home在左") 106 | [self locationChange:@"right"]; 107 | } 108 | break; 109 | case UIDeviceOrientationPortrait:{ 110 | // DLog(@"设备垂直,home在下"); 111 | [self locationChange:@"Portrait"]; 112 | } 113 | break; 114 | default: 115 | break; 116 | } 117 | } 118 | //根据屏幕宽高改变按钮位置比例 119 | - (void)locationChange:(NSString *)message{ 120 | // NSLog(@"changeHig == %f,changeWid == %f",changeHig,changeWid); 121 | if (SCREEN_HEIGHT > SCREEN_WIDTH) { 122 | //屏幕方向上 123 | if ([message isEqualToString:@"Portrait"]) { 124 | NSLog(@"安全区在上边"); 125 | self.center = CGPointMake(changeWid * SCREEN_WIDTH, changeHig * SCREEN_HEIGHT); 126 | }else{//下 127 | NSLog(@"安全区在下边"); 128 | self.center = CGPointMake(changeWid * SCREEN_WIDTH, changeHig * SCREEN_HEIGHT - [self vg_safeDistanceTop]); 129 | } 130 | }else{ 131 | if ([message isEqualToString:@"left"]) {//左 132 | NSLog(@"安全区在左边"); 133 | self.center = CGPointMake(changeWid * SCREEN_WIDTH + [self vg_safeDistanceTop] + ViewSize, changeHig * SCREEN_HEIGHT); 134 | }else{//右 135 | NSLog(@"安全区在右边"); 136 | self.center = CGPointMake(changeWid * SCREEN_WIDTH - [self vg_safeDistanceTop] - ViewSize, changeHig * SCREEN_HEIGHT); 137 | } 138 | } 139 | // NSLog(@"lastPoint == %@, self.center == %@",NSStringFromCGPoint(lastPoint),NSStringFromCGPoint(self.center)); 140 | 141 | [self changeCoordinateScale]; 142 | 143 | } 144 | //旋转屏幕后修改悬浮窗相对于屏幕的宽高比例以及坐标位置 145 | - (void)changeCoordinateScale{ 146 | changeHig = self.center.y/SCREEN_HEIGHT; 147 | changeWid = self.center.x/SCREEN_WIDTH; 148 | //判断设备旋转方向 149 | if (self.orientation == UIInterfaceOrientationLandscapeRight) {//横向home键在右侧,设备左转,刘海在左边,刘海在左边 150 | //判断悬浮窗坐标x在屏幕的左边还是右边 151 | if (self.center.x > SCREEN_WIDTH/2) {//大于中心x,在右边 152 | //修改悬浮窗的坐标在最右边 153 | self.center = CGPointMake(SCREEN_WIDTH, self.center.y); 154 | }else{ 155 | //修改悬浮窗的坐标在最左边 156 | self.center = CGPointMake([self vg_safeDistanceTop] + ViewSize + 20, self.center.y); 157 | } 158 | }else if(self.orientation == UIInterfaceOrientationLandscapeLeft){//横向home键在左侧,设备右转,刘海在右边 159 | if (self.center.x > SCREEN_WIDTH/2) {//大于中心x,在右边 160 | //修改悬浮窗的坐标在最右边,留出顶部安全距离 161 | self.center = CGPointMake(SCREEN_WIDTH - [self vg_safeDistanceTop] - ViewSize - 20, self.center.y); 162 | }else{ 163 | //修改悬浮窗的坐标在最左边 164 | self.center = CGPointMake(0, self.center.y); 165 | } 166 | }else{ 167 | //大于中心x,在右边 168 | if (self.center.x > SCREEN_WIDTH/2) { 169 | self.center = CGPointMake(SCREEN_WIDTH, self.center.y); 170 | }else{ 171 | self.center = CGPointMake(0, self.center.y); 172 | } 173 | } 174 | // NSLog(@"changeHig == %f,changeWid == %f",changeHig,changeWid); 175 | // NSLog(@"设备宽度 == %f, 设备高度== %f, 按钮坐标==%@",SCREEN_WIDTH,SCREEN_HEIGHT,NSStringFromCGPoint(self.center)); 176 | } 177 | - (void)showSuspendView{ 178 | self.hidden = NO; 179 | NSLog(@"显示悬浮窗"); 180 | } 181 | - (void)dismissSuspendView{ 182 | self.hidden = YES; 183 | NSLog(@"隐藏悬浮窗"); 184 | } 185 | /// 悬浮窗按钮点击放法 186 | /// @param button 点击之后完全显示悬浮窗,改变按钮位置 187 | - (void)btnClick:(UIButton *)button{ 188 | if (self.delegate && [self.delegate respondsToSelector:@selector(suspendViewButtonClick:)]) { 189 | [self.delegate suspendViewButtonClick:button]; 190 | } 191 | // DLog(@"lastPoint == %@",NSStringFromCGPoint(lastPoint)); 192 | //如果没有改变过位置,lastPoint初始值(0,0) 193 | //判断是否移动过悬浮窗 194 | if (!isChangePosition) { 195 | //悬浮窗初始位置在右上角,只有屏幕向右旋转,才需要留出iphone刘海的位置,设备左转刘海在左边,所以不需要做判断 196 | if (self.orientation == UIInterfaceOrientationLandscapeLeft) {//横向home键在左侧,设备右转,刘海在右边 197 | //修改点击后悬浮窗的位置,留出安全距离 198 | [UIView animateWithDuration:0.5 animations:^{ 199 | self.center = CGPointMake(SCREEN_WIDTH - [self vg_safeDistanceTop] - ViewSize - 20 - 20, self.center.y); 200 | }]; 201 | }else{ 202 | [UIView animateWithDuration:0.5 animations:^{ 203 | self.center = CGPointMake(SCREEN_WIDTH - ViewSize, self.center.y); 204 | }]; 205 | } 206 | }else{ 207 | // 判断最后的坐标是靠左还是靠右 208 | if (self.orientation == UIInterfaceOrientationLandscapeRight) {//横向home键在右侧,设备左转,刘海在左边 209 | if (self.center.x > SCREEN_WIDTH/2) {//悬浮窗在屏幕右侧 210 | [UIView animateWithDuration:0.5 animations:^{ 211 | self.center = CGPointMake(SCREEN_WIDTH - ViewSize, self.center.y); 212 | }]; 213 | }else{ 214 | //左转刘海在左边,留出安全距离 215 | [UIView animateWithDuration:0.5 animations:^{ 216 | self.center = CGPointMake([self vg_safeDistanceTop] + ViewSize + 20 + 20, self.center.y); 217 | }]; 218 | } 219 | }else if(self.orientation == UIInterfaceOrientationLandscapeLeft){//横向home键在左侧,设备右转,刘海在右边 220 | if (self.center.x > SCREEN_WIDTH/2) {//悬浮窗在屏幕右侧,留出刘海安全距离 221 | [UIView animateWithDuration:0.5 animations:^{ 222 | self.center = CGPointMake(SCREEN_WIDTH - [self vg_safeDistanceTop] - ViewSize - 20 - 20, self.center.y); 223 | }]; 224 | }else{//左侧显示 225 | [UIView animateWithDuration:0.5 animations:^{ 226 | self.center = CGPointMake(ViewSize, self.center.y); 227 | }]; 228 | } 229 | }else{ 230 | if (self.center.x < SCREEN_WIDTH/2) {//悬浮窗在屏幕右侧 231 | [UIView animateWithDuration:0.5 animations:^{ 232 | self.center = CGPointMake(ViewSize, self.center.y); 233 | }]; 234 | }else{ 235 | [UIView animateWithDuration:0.5 animations:^{ 236 | self.center = CGPointMake(SCREEN_WIDTH - ViewSize, self.center.y); 237 | }]; 238 | } 239 | } 240 | 241 | } 242 | 243 | self.alpha = 1; 244 | //三秒后隐藏悬浮窗,贴边展示一半 245 | self.timer = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(timerAction) userInfo:nil repeats:NO]; 246 | } 247 | 248 | - (void)timerAction{ 249 | //隐藏悬浮球 250 | self.alpha = 0.5; 251 | //判断是否移动过悬浮窗 252 | if (!isChangePosition) { 253 | //悬浮窗初始位置在右上角,只有屏幕向右旋转,才需要留出iphone刘海的位置,设备左转刘海在左边,所以不需要做判断 254 | if (self.orientation == UIInterfaceOrientationLandscapeLeft) {//横向home键在左侧,设备右转,刘海在右边 255 | [UIView animateWithDuration:0.5 animations:^{ 256 | self.center = CGPointMake(SCREEN_WIDTH - [self vg_safeDistanceTop] - ViewSize - 20, self.center.y); 257 | }]; 258 | }else{ 259 | [UIView animateWithDuration:0.5 animations:^{ 260 | self.center = CGPointMake(SCREEN_WIDTH, self.center.y); 261 | }]; 262 | } 263 | 264 | }else{ 265 | if (self.orientation == UIInterfaceOrientationLandscapeRight) {//横向home键在右侧,设备左转,刘海在左边 266 | if (self.center.x > SCREEN_WIDTH/2) {//悬浮窗在屏幕右侧 267 | [UIView animateWithDuration:0.5 animations:^{ 268 | self.center = CGPointMake(SCREEN_WIDTH, self.center.y); 269 | }]; 270 | }else{ 271 | //悬浮窗在屏幕左侧,留出刘海安全距离 272 | [UIView animateWithDuration:0.5 animations:^{ 273 | self.center = CGPointMake([self vg_safeDistanceTop] + ViewSize + 20, self.center.y); 274 | }]; 275 | } 276 | }else if(self.orientation == UIInterfaceOrientationLandscapeLeft){//横向home键在左侧,设备右转,刘海在右边 277 | if (self.center.x > SCREEN_WIDTH/2) {//悬浮窗在屏幕右侧 278 | //悬浮窗在屏幕左侧,留出刘海安全距离 279 | [UIView animateWithDuration:0.5 animations:^{ 280 | self.center = CGPointMake(SCREEN_WIDTH - [self vg_safeDistanceTop] - ViewSize - 20, self.center.y); 281 | }]; 282 | }else{ 283 | [UIView animateWithDuration:0.5 animations:^{ 284 | self.center = CGPointMake(0, self.center.y); 285 | }]; 286 | } 287 | 288 | }else{ 289 | if (self.center.x > SCREEN_WIDTH/2) {//悬浮窗在屏幕右侧 290 | [UIView animateWithDuration:0.5 animations:^{ 291 | self.center = CGPointMake(SCREEN_WIDTH, self.center.y); 292 | }]; 293 | }else{ 294 | [UIView animateWithDuration:0.5 animations:^{ 295 | self.center = CGPointMake(0, self.center.y); 296 | }]; 297 | } 298 | 299 | } 300 | } 301 | //销毁定时器 302 | [self.timer invalidate]; 303 | self.timer = nil; 304 | } 305 | 306 | 307 | /// pan手势 308 | /// @param recognizer recognizer description 309 | - (void)handlePanGesture:(UIPanGestureRecognizer *)recognizer 310 | { 311 | //移动状态 312 | UIGestureRecognizerState recState = recognizer.state; 313 | isChangePosition = YES; 314 | 315 | switch (recState) { 316 | case UIGestureRecognizerStateBegan: 317 | self.alpha = 1; 318 | self.imageView.hidden = NO; 319 | break; 320 | case UIGestureRecognizerStateChanged://移动中 321 | { 322 | self.alpha = 1; 323 | CGPoint translation = [recognizer translationInView:self]; 324 | recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x, recognizer.view.center.y + translation.y); 325 | 326 | CGRect rect = [self convertRect:self.frame toView:self]; 327 | if (CGRectIntersectsRect(self.imageView.frame, rect)) {//在范围内 328 | self.imageView.backgroundColor = UIColor.redColor; 329 | }else{ 330 | self.imageView.backgroundColor = UIColor.blueColor; 331 | } 332 | } 333 | break; 334 | case UIGestureRecognizerStateEnded://移动结束 335 | { 336 | self.alpha = 0.5; 337 | CGPoint stopPoint = CGPointMake(0, SCREEN_HEIGHT / 2); 338 | //判断按钮贴靠在屏幕的左边还是右边 339 | if (recognizer.view.center.x < SCREEN_WIDTH / 2) { 340 | stopPoint = CGPointMake(ViewSize/2, recognizer.view.center.y); 341 | }else{ 342 | //贴靠在右边 343 | stopPoint = CGPointMake(SCREEN_WIDTH - ViewSize/2,recognizer.view.center.y); 344 | } 345 | DLog(@"stopPoint == %@",NSStringFromCGPoint(stopPoint)); 346 | 347 | if (stopPoint.y - ViewSize/2 <= 0) { 348 | DLog(@"上"); 349 | //加上电池栏的高度 350 | if (stopPoint.x - ViewSize/2 <= SCREEN_WIDTH/2) { 351 | stopPoint = CGPointMake(0, stopPoint.y + [self vg_safeDistanceTop] + ViewSize); 352 | DLog(@"左上"); 353 | }else{ 354 | DLog(@"右上"); 355 | stopPoint = CGPointMake(SCREEN_WIDTH, stopPoint.y + [self vg_safeDistanceTop] + ViewSize); 356 | } 357 | } 358 | //如果按钮超出屏幕边缘 359 | if (stopPoint.y + ViewSize + 20 >= SCREEN_HEIGHT) { 360 | DLog(@"下"); 361 | //减去底部状态栏的高度 362 | if (stopPoint.x - ViewSize/2 <= SCREEN_WIDTH/2) { 363 | DLog(@"左下"); 364 | stopPoint = CGPointMake(0, stopPoint.y - [self vg_safeDistanceBottom] - ViewSize/2); 365 | }else{ 366 | DLog(@"右下"); 367 | stopPoint = CGPointMake(SCREEN_WIDTH, stopPoint.y - [self vg_safeDistanceBottom] - ViewSize/2); 368 | } 369 | // DLog(@"超出屏幕下方"); 370 | } 371 | 372 | if (stopPoint.x - ViewSize/2 <= 0) { 373 | DLog(@"左"); 374 | // stopPoint = CGPointMake(ViewSize/2, stopPoint.y); 375 | //缩进去一半 376 | stopPoint = CGPointMake(0, stopPoint.y); 377 | } 378 | if (stopPoint.x + ViewSize/2 >= SCREEN_WIDTH) { 379 | DLog(@"右"); 380 | // stopPoint = CGPointMake(SCREEN_WIDTH - ViewSize/2, stopPoint.y); 381 | stopPoint = CGPointMake(SCREEN_WIDTH, stopPoint.y); 382 | } 383 | 384 | //保存最后的位置 385 | lastPoint = stopPoint; 386 | 387 | //隐藏悬浮球 388 | CGRect rect = [self convertRect:self.frame toView:self]; 389 | if (CGRectIntersectsRect(self.imageView.frame, rect)) {//在范围内 390 | DLog(@"悬浮窗在中心imageview内,提示是否隐藏悬浮窗"); 391 | // [self showAlertView]; 392 | [self.delegate showHideAlertView]; 393 | } 394 | // NSLog(@"self.orientation == %ld",(long)self.orientation); 395 | if (self.orientation == UIInterfaceOrientationLandscapeRight) {//横向home键在右侧,设备左转,刘海在左边 396 | if (stopPoint.x > SCREEN_WIDTH/2) {//悬浮窗在屏幕右侧 397 | [UIView animateWithDuration:0.5 animations:^{ 398 | recognizer.view.center = CGPointMake(SCREEN_WIDTH, stopPoint.y); 399 | }]; 400 | }else{ 401 | //悬浮窗在屏幕左侧,留出刘海安全距离 402 | [UIView animateWithDuration:0.5 animations:^{ 403 | recognizer.view.center = CGPointMake([self vg_safeDistanceTop] + ViewSize + 20, stopPoint.y); 404 | }]; 405 | } 406 | }else if(self.orientation == UIInterfaceOrientationLandscapeLeft){//横向home键在左侧,设备右转,刘海在右边 407 | if (stopPoint.x > SCREEN_WIDTH/2) {//悬浮窗在屏幕右侧 408 | //悬浮窗在屏幕左侧,留出刘海安全距离 409 | [UIView animateWithDuration:0.5 animations:^{ 410 | recognizer.view.center = CGPointMake(SCREEN_WIDTH - [self vg_safeDistanceTop] - ViewSize - 20, stopPoint.y); 411 | }]; 412 | }else{ 413 | [UIView animateWithDuration:0.5 animations:^{ 414 | recognizer.view.center = CGPointMake(0, stopPoint.y); 415 | }]; 416 | } 417 | 418 | }else{ 419 | [UIView animateWithDuration:0.5 animations:^{ 420 | recognizer.view.center = stopPoint; 421 | }]; 422 | } 423 | [self changeCoordinateScale]; 424 | 425 | self.imageView.hidden = YES; 426 | 427 | } 428 | break; 429 | 430 | default: 431 | break; 432 | } 433 | 434 | [recognizer setTranslation:CGPointMake(0, 0) inView:self]; 435 | } 436 | 437 | //获取头部安全区高度 438 | - (CGFloat)vg_safeDistanceTop { 439 | if (@available(iOS 13.0, *)) { 440 | NSSet *set = [UIApplication sharedApplication].connectedScenes; 441 | UIWindowScene *windowScene = [set anyObject]; 442 | UIWindow *window = windowScene.windows.firstObject; 443 | return window.safeAreaInsets.top; 444 | } else if (@available(iOS 11.0, *)) { 445 | UIWindow *window = [UIApplication sharedApplication].windows.firstObject; 446 | return window.safeAreaInsets.top; 447 | } 448 | return 0; 449 | } 450 | 451 | //获取设备底部安全区高度 452 | - (CGFloat)vg_safeDistanceBottom { 453 | if (@available(iOS 13.0, *)) { 454 | NSSet *set = [UIApplication sharedApplication].connectedScenes; 455 | UIWindowScene *windowScene = [set anyObject]; 456 | UIWindow *window = windowScene.windows.firstObject; 457 | return window.safeAreaInsets.bottom; 458 | } else if (@available(iOS 11.0, *)) { 459 | UIWindow *window = [UIApplication sharedApplication].windows.firstObject; 460 | return window.safeAreaInsets.bottom; 461 | } 462 | return 0; 463 | } 464 | 465 | - (UIImageView *)imageView{ 466 | if (!_imageView) { 467 | _imageView = [[UIImageView alloc]initWithFrame:CGRectMake((SCREEN_WIDTH - 50)/2, (SCREEN_HEIGHT - 50)/2, 50, 50)]; 468 | _imageView.backgroundColor = UIColor.blueColor; 469 | _imageView.hidden = YES; 470 | [[UIApplication sharedApplication].keyWindow addSubview:_imageView]; 471 | } 472 | return _imageView; 473 | } 474 | 475 | @end 476 | --------------------------------------------------------------------------------