页面中经常遇到需要悬浮展示并且可以随手势移动的按钮,简单实现了一个。
思路就是继承 UIButton
类,然后重写拖拽方法 .touchDragInside
addTarget(self, action: #selector(dragMoving(btn: event:)), for: .touchDragInside)
addTarget(self, action: #selector(dragEnd(btn: event:)), for: .touchUpInside)
因为 .touchDragInside
是在手势移动过程中会不断的触发,在移动结束的手指抬起时才会触发 .touchUpInside
需要每次移动触发后获取到当前的 point
guard let touch = event.allTouches?.first else {
print("⚠️ 无法获取 Touch ⚠️ ")
return
}
var point = touch.location(in: superview)
获取到后把 btn.center
设置为 point
所在点即可
一般会设置视图悬浮显示的区域为贴边显示,所以在滑动结束时需要重新设置下 point
的位置
不足屏幕的一半就贴在左边,否则贴在右边
let btnx = btn.frame.size.width / 2
if x <= btnx {
point.x = btnx
}
if x >= (superview?.bounds.size.width)! - btnx {
point.x = (superview?.bounds.size.width)! - btnx
}
由于 .touchUpInside
事件需要同时处理拖拽的结束和点击事件,所以需要每次在拖动结束后判断用户的意图是拖动视图还是点击视图,因为用户在点击的时候手指也可能在屏幕上滑动,在这里根据视图的位移来判断,定为上下左右位移超过 5px 则认为用户在进行拖动操作,否则是点击。
var p_start: CGPoint? //获取开始点击的初始位置
var isDrag: Bool?
@objc func dragMoving(btn: UIButton, event: UIEvent) -> Void {
var point = touch.location(in: superview)
if !isDrag! {
p_start = point
isDrag = true
}
}
上面也提及到了,.touchDragInside
会随手势移动而不断的触发,我们只需要记录开始的 point
就好
在拖动结束时判断位置距离
@objc func dragEnd(btn: UIButton, event: UIEvent) -> Void {
isDrag = false
var point = touch.location(in: superview)
let p_end = point
if abs(p_start!.x - p_end.x) < 5 && abs(p_start!.y - p_end.y) < 5 {
if (clicked != nil) {
clicked!()
}
}
}
一般不需要视图紧贴屏幕边缘,会设置一个 margin
来控制离屏幕边际距离。
let kEdgeMargin: CGFloat = 5.0 //视图与屏幕距离最小距离
最终效果:
模拟器效果
完整代码:FlyBtn
网友评论