185 lines
6.7 KiB
Swift
185 lines
6.7 KiB
Swift
//
|
||
// DDUIPanGestureRecognizer.swift
|
||
// DDUIGestureRecognizer
|
||
// Created by DDIsFriend on 2023/12/19.
|
||
|
||
|
||
import Foundation
|
||
|
||
open class DDUIPanGestureRecognizer : UIPanGestureRecognizer {
|
||
public enum PanGestureFromType {
|
||
case bottom,top
|
||
}
|
||
|
||
public enum ExpandLevel {
|
||
case min,`default`,max
|
||
}
|
||
|
||
public struct PanGesValue {
|
||
/// 从什么位置弹出
|
||
public var from : PanGestureFromType = .bottom
|
||
/// 最小显示的高度
|
||
public var minDisplayHeight : CGFloat = 0
|
||
/// 默认弹出的高度
|
||
public var defaultDisplayHeight : CGFloat = 0
|
||
/// 最大显示的高度
|
||
public var maxDisplayHeight : CGFloat = 0
|
||
/// 标记当前需要完全展开时的拖动比例(0,1]
|
||
public var dragScale : CGFloat = 0.5
|
||
/// 标记当前是否是展开的状态
|
||
public var expandLevel : ExpandLevel = .min
|
||
|
||
internal var translationY : CGFloat = 0
|
||
internal var currentY : CGFloat = 0
|
||
}
|
||
|
||
public var panGesValue = PanGesValue.init()
|
||
|
||
public var expandLevelChangedHandler : ((_ oldExpandLevel: ExpandLevel, _ newExpandLevel: ExpandLevel) -> Void)?
|
||
|
||
/// 会执行默认方法
|
||
/// - Parameter target: target
|
||
public init() {
|
||
super.init(target: nil, action: nil)
|
||
self.addTarget(self, action: #selector(panGesAction(ges: )))
|
||
}
|
||
|
||
/// 需要自己添加target和action
|
||
public override init(target: Any?, action: Selector?) {
|
||
super.init(target: target, action: action)
|
||
}
|
||
|
||
@objc fileprivate func panGesAction(ges: DDUIPanGestureRecognizer) {
|
||
baseAction(duration: 0.1) { viewFrame, minY, defaultY, maxY, currentY in
|
||
switch ges.state {
|
||
case .began:
|
||
currentY = viewFrame.origin.y
|
||
break
|
||
case .changed:
|
||
/// 拖动时的移动距离是当前changed与上一次changed的差值,即deltaY
|
||
let translationPoint = ges.translation(in: ges.view)
|
||
let deltaY = translationPoint.y - ges.panGesValue.translationY
|
||
ges.panGesValue.translationY = translationPoint.y
|
||
|
||
currentY = (viewFrame.origin.y) + deltaY
|
||
break
|
||
case .ended:
|
||
/// 当currentY在maxY和defaultY之间时的临界值
|
||
let criticalYBetweenOfMaxYAndDefaultY = maxY - (maxY - defaultY) * ges.panGesValue.dragScale
|
||
if ges.panGesValue.currentY <= maxY && ges.panGesValue.currentY > criticalYBetweenOfMaxYAndDefaultY {
|
||
currentY = maxY
|
||
}else if ges.panGesValue.currentY > defaultY && ges.panGesValue.currentY <= criticalYBetweenOfMaxYAndDefaultY {
|
||
currentY = defaultY
|
||
}
|
||
|
||
/// 当currentY在minY和defaultY之间时
|
||
let criticalYBetweenOfMinYAndDefaultY = defaultY - (defaultY - minY) * ges.panGesValue.dragScale
|
||
if ges.panGesValue.currentY <= defaultY && ges.panGesValue.currentY > criticalYBetweenOfMinYAndDefaultY {
|
||
currentY = defaultY
|
||
}else if ges.panGesValue.currentY > minY && ges.panGesValue.currentY <= criticalYBetweenOfMinYAndDefaultY {
|
||
currentY = minY
|
||
}
|
||
|
||
ges.panGesValue.translationY = 0
|
||
break
|
||
default:
|
||
currentY = viewFrame.origin.y
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
func baseAction(duration: TimeInterval,baseActionHandler: (_ viewFrame: CGRect, _ minY: CGFloat, _ defaultY: CGFloat, _ maxY: CGFloat,_ currentY: inout CGFloat) -> Void) {
|
||
guard let superView = view?.superview else {
|
||
assert(false,"父类都没有,拖什么拖")
|
||
return
|
||
}
|
||
|
||
let superViewFrame = superView.frame
|
||
let viewFrame = view?.frame ?? .zero
|
||
let superViewH = superViewFrame.size.height
|
||
let viewH = viewFrame.size.height
|
||
|
||
switch panGesValue.from {
|
||
case .bottom:
|
||
/// 拖动的view的最小y值,这个值和view的高度有关,view的最大y值要和它的superView的最大y值相等,所以minY可能等于deltaH
|
||
var minY = superViewH - panGesValue.maxDisplayHeight
|
||
let deltaH = superViewH - viewH
|
||
if deltaH > minY {
|
||
minY = deltaH
|
||
}
|
||
|
||
/// 拖动的view的最大y值
|
||
var maxY = superViewH - panGesValue.minDisplayHeight
|
||
if deltaH > maxY {
|
||
maxY = deltaH
|
||
}
|
||
|
||
/// 默认弹出的y值
|
||
var defaultY = superViewH - panGesValue.defaultDisplayHeight
|
||
if deltaH > defaultY {
|
||
defaultY = deltaH
|
||
}
|
||
|
||
var currentY : CGFloat = 0.0
|
||
|
||
/// 计算值
|
||
baseActionHandler(viewFrame,minY,defaultY,maxY,¤tY)
|
||
|
||
/// 当到达最小y值时返回
|
||
if currentY < minY {
|
||
return
|
||
}
|
||
|
||
/// 当到达最大y值时返回
|
||
if currentY > maxY {
|
||
return
|
||
}
|
||
|
||
/// 记录当前的currentY
|
||
panGesValue.currentY = currentY
|
||
|
||
/// 上一次expandLevel
|
||
let oldExpandLevel = panGesValue.expandLevel
|
||
|
||
/// 记录当前的expandLevel
|
||
if panGesValue.currentY == minY {
|
||
panGesValue.expandLevel = .max
|
||
}else if panGesValue.currentY == maxY {
|
||
panGesValue.expandLevel = .min
|
||
}else if panGesValue.currentY == defaultY {
|
||
panGesValue.expandLevel = .default
|
||
}
|
||
|
||
/// 新的expandLevel
|
||
let newExpandLevel = panGesValue.expandLevel
|
||
|
||
/// expandLevel改变时回调
|
||
if oldExpandLevel != newExpandLevel {
|
||
if let expandLevelChangedHandler = expandLevelChangedHandler {
|
||
expandLevelChangedHandler(oldExpandLevel,newExpandLevel)
|
||
}
|
||
}
|
||
|
||
UIView.animate(withDuration: duration, animations: {[weak self] in
|
||
self?.view?.frame = CGRectMake(viewFrame.origin.x, currentY, viewFrame.size.width, viewFrame.size.height)
|
||
})
|
||
break
|
||
default:
|
||
break
|
||
}
|
||
}
|
||
|
||
public func expand(_ expand: ExpandLevel) {
|
||
baseAction(duration: 0.25) { viewFrame, minY, defaultY, maxY, currentY in
|
||
if expand == .max {
|
||
currentY = minY
|
||
}else if expand == .min {
|
||
currentY = maxY
|
||
}else if expand == .default {
|
||
currentY = defaultY
|
||
}
|
||
}
|
||
}
|
||
}
|