【Demo】iOS可吸附拖动的悬浮窗按钮插件
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
电脑知识学习@更新2017.1.14:
欢迎⼀起完善优化这个插件,⽬前很⽐较精简,但可以满⾜⼀般情况的需求;
之前的demo已经删除停⽌更新;
中秋节的祝福语句---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@废话在前
悬浮窗⽤于在整个程序过程中始终显⽰在屏幕最上⽅供⽤户进⾏全局操作,可拖动,可点击,IOS中悬浮窗的实现主要有两种思路,⼀种是使⽤UIWindow,另⼀种是使⽤UIButton。前者通过下⽂的探索已经⽐较完美的实现所需功能,后者仍然存在问题待解决,下⾯⼀起来从简单⼊⼿披荆斩棘来实现这个奇妙的悬浮窗组件吧!
⼀. UIWindow实现可吸附拖动的悬浮按钮(按钮拖动和点击事件冲突解决)
开始思路是直接改写UIButton,使⽤touch代理事件来监听按钮开始触摸,移动,触摸结束等事件获取触点坐标,改变UIWindow的位置实现悬浮窗整体的移动,但发现按钮触摸事件和按钮的点击事件冲突了,点击按钮不响应。⽹上有解决办法是⾃定义tap和pan⼿势事件解决,我没有成功,想到另⼀个简单的办法:
在改写的可拖动的UIButton中加代理事件,只使⽤touch事件,touch began时记录下开始触点的位置,touchended的时候⽐较开始的触点位置和结束的触点位置的距离,如果距离⾜够⼩就认为是点击事件,否则只认为是拖动了(当然如果拖动回到起点还认为是点击事件不好,这个可以忽略,或者通过计算时间长度来优化)。
实现代码如下,重写⼀个UIDragButton类,⽤于实现悬浮uiwindow的移动和点击事件的代理监听,定义⼀个FloatingViewController作为初始化的控制器,设置悬浮uiwindow和悬浮按钮以及按钮点击事件的处理等。
改写的UIButton:
//
// UIDragButton.h
/
/ JXHDemo
//
// Created by Xinhou Jiang on 6/14/16.
// Copyright © 2016 Jiangxh. All rights reserved.
//
#import <UIKit/UIKit.h>
/**
* 代理按钮的点击事件
*/
@protocol UIDragButtonDelegate <NSObject>
- (void)dragButtonClicked:(UIButton *)sender;
@end
@interface UIDragButton : UIButton
/**
* 悬浮窗所依赖的根视图
*/
@property (nonatomic, strong)UIView *rootView;
/**
* UIDragButton的点击事件代理
*/
@property (nonatomic, weak)id<UIDragButtonDelegate>btnDelegate;
@end
/
/
// UIDragButton.m
// JXHDemo
//
// Created by Xinhou Jiang on 6/14/16.
// Copyright © 2016 Jiangxh. All rights reserved.
小飞棍来喽什么时候的梗//
// 屏幕⾼度
#define ScreenH [UIScreen mainScreen].bounds.size.height
// 屏幕宽度
#define ScreenW [UIScreen mainScreen].bounds.size.width
#import "UIDragButton.h"
@interface UIDragButton()
/**
* 开始按下的触点坐标
*/
@property (nonatomic, assign)CGPoint startPos;
@end
@implementation UIDragButton
// 枚举四个吸附⽅向
typedef enum {
LEFT,
LEFT,
RIGHT,
TOP,
BOTTOM
}Dir;
/**
* 开始触摸,记录触点位置⽤于判断是拖动还是点击
*/
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
// 获得触摸在根视图中的坐标
UITouch *touch = [touches anyObject];
_startPos = [touch locationInView:_rootView];
}
/**
* ⼿指按住移动过程,通过悬浮按钮的拖动事件来拖动整个悬浮窗⼝
*/
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// 获得触摸在根视图中的坐标
UITouch *touch = [touches anyObject];
CGPoint curPoint = [touch locationInView:_rootView];
// 移动按钮到当前触摸位置
= curPoint;
}
/**
* 拖动结束后使悬浮窗⼝吸附在最近的屏幕边缘
*/
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 获得触摸在根视图中的坐标
UITouch *touch = [touches anyObject];
CGPoint curPoint = [touch locationInView:_rootView];
// 通知代理,如果结束触点和起始触点极近则认为是点击事件
if (pow((_startPos.x - curPoint.x),2) + pow((_startPos.y - curPoint.y),2) < 1) {
[self.btnDelegate dragButtonClicked:self];
return;//点击后不吸附
}
// 与四个屏幕边界距离
CGFloat left = curPoint.x;
CGFloat right = ScreenW - curPoint.x;
CGFloat top = curPoint.y;
CGFloat bottom = ScreenH - curPoint.y;
// 计算四个距离最⼩的吸附⽅向
Dir minDir = LEFT;
CGFloat minDistance = left;
if (right < minDistance) {
minDistance = right;
minDir = RIGHT;
}
if (top < minDistance) {
minDistance = top;
minDir = TOP;
}
if (bottom < minDistance) {
minDir = BOTTOM;
}
拼多多领钱/
/ 开始吸附
switch (minDir) {
case LEFT:
= CGPointMake(self.superview.frame.size.width/2, y);
break;
case RIGHT:
= CGPointMake(ScreenW - self.superview.frame.size.width/2, y); break;
break;
case TOP:
= CGPointMake(x, self.superview.frame.size.height/2);
break;
case BOTTOM:
= CGPointMake(x, ScreenH - self.superview.frame.size.height/2); break;
default:
break;
}
}
@end
悬浮窗控制器:
//
// FloatingViewController.h
// Unity-iPhone
//
// Created by Xinhou Jiang on 6/13/16.
//
//
#import <UIKit/UIKit.h>
@interface FloatingViewController : UIViewController
@end
//
// FloatingViewController.m
// Unity-iPhone
//
/
/ Created by Xinhou Jiang on 6/13/16.
//
//
// 屏幕⾼度
#define ScreenH [UIScreen mainScreen].bounds.size.height
// 屏幕宽度
#define ScreenW [UIScreen mainScreen].bounds.size.width
// 悬浮按钮的尺⼨
#define floatSize 50
#import "FloatingViewController.h"
#import "UIDragButton.h"
@interface FloatingViewController ()<UIDragButtonDelegate>
/**
* 悬浮的window
*/
@property(strong,nonatomic)UIWindow *window;
/**
* 悬浮的按钮
*/
@property(strong,nonatomic)UIDragButton *button;
@end
@implementation FloatingViewController
-
(void)viewDidLoad {
[super viewDidLoad];
// 将视图尺⼨设置为0,防⽌阻碍其他视图元素的交互
self.view.frame = CGRectZero;
// 延时显⽰悬浮窗⼝
[self performSelector:@selector(createButton) withObject:nil afterDelay:1];
}
/**
* 创建悬浮窗⼝
*/
- (void)createButton
{
// 悬浮按钮
_button = [UIDragButton buttonWithType:UIButtonTypeCustom];
[_button setImage:[UIImage imageNamed:@"add_button"] forState:UIControlStateNormal];
// 按钮图⽚伸缩充满整个按钮
_tMode = UIViewContentModeScaleToFill;
_button.frame = CGRectMake(0, 0, floatSize, floatSize);
// 按钮点击事件
//[_button addTarget:self action:@selector(floatBtnClicked:) forControlEvents:UIControlEventTouchUpInside]; // 按钮点击事件代理
_button.btnDelegate = self;
// 初始选中状态
_button.selected = NO;
// 禁⽌⾼亮
楚乔传剧情_button.adjustsImageWhenHighlighted = NO;
_View = self.view.superview;
// 悬浮窗
_window = [[UIWindow alloc]initWithFrame:CGRectMake(ScreenW-floatSize, ScreenH/2, floatSize, floatSize)]; _window.windowLevel = UIWindowLevelAlert+1;
_window.backgroundColor = [UIColor orangeColor];
_Radius = floatSize/2;
_window.layer.masksToBounds = YES;
// 将按钮添加到悬浮按钮上
[_window addSubview:_button];
//显⽰window
[_window makeKeyAndVisible];
}
/**
* 悬浮按钮点击
*/
- (void)dragButtonClicked:(UIButton *)sender {
// 按钮选中关闭切换
sender.selected = !sender.selected;
if (sender.selected) {
[sender setImage:[UIImage imageNamed:@"add_rotate"] forState:UIControlStateNormal];
}else{
音箱排名[sender setImage:[UIImage imageNamed:@"add_button"] forState:UIControlStateNormal];
}
// 关闭悬浮窗
//[_window resignKeyWindow];
//_window = nil;
}
@end
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论