如何为容器视图配置双向UIPanGestureRecognizer实现左右滑动旋转
实现容器视图双向滑动旋转功能
现状与需求
- 已为容器视图添加
UIPanGestureRecognizer,可实现向右滑动旋转(锚点(x: 1, y: 1)),运行正常,现有响应代码如下:
@objc func respondToPanGesture(recognizer: UIPanGestureRecognizer) { container.setAnchorPoint(anchorPoint: .init(x: 1, y: 1)) let touchLocation = recognizer.location(in: container.superview) let center = container.center switch recognizer.state { case .began : self.deltaAngle = atan2(touchLocation.y - center.y, touchLocation.x - center.x) - atan2(container.transform.b, container.transform.a) case .changed: backgroundView.backgroundColor = UIColor.systemGreen let angle = atan2(touchLocation.y - center.y, touchLocation.x - center.x) let angleDiff = self.deltaAngle - angle if angleDiff <= 0, angleDiff > -5, angleDiff > -0.50 { container.transform = CGAffineTransform(rotationAngle: -angleDiff) } case .ended: UIView.animate(withDuration: 0.6, animations: { [weak self] in guard let this = self else { return } this.container.transform = CGAffineTransform(rotationAngle: 0) }, completion: { [weak self] _ in guard let this = self else { return } this.container.setAnchorPoint(anchorPoint: .init(x: 0.5, y: 0.5)) }) default: break } }
- 需要新增向左滑动旋转效果,对应锚点为
(x: 0, y: 1),但添加新的UIPanGestureRecognizer后,视图仅生效最后添加的手势,需实现双向滑动逻辑。
解决方案:复用单个手势实现双向滑动
无需添加多个手势,在同一个手势响应方法中,根据触摸起始位置判断滑动方向,动态切换锚点和旋转逻辑:
修改后的完整代码
// 新增枚举标记滑动方向 enum PanDirection { case left case right } private var currentDirection: PanDirection? private var deltaAngle: CGFloat = 0 @objc func respondToPanGesture(recognizer: UIPanGestureRecognizer) { let touchLocation = recognizer.location(in: container.superview) let center = container.center switch recognizer.state { case .began: // 根据触摸位置与容器中心的水平偏移判断方向 let isRightSwipe = touchLocation.x > center.x currentDirection = isRightSwipe ? .right : .left // 设置对应锚点 let anchorPoint = isRightSwipe ? CGPoint(x: 1, y: 1) : CGPoint(x: 0, y: 1) container.setAnchorPoint(anchorPoint: anchorPoint) // 计算初始角度差 deltaAngle = atan2(touchLocation.y - center.y, touchLocation.x - center.x) - atan2(container.transform.b, container.transform.a) case .changed: backgroundView.backgroundColor = UIColor.systemGreen guard let direction = currentDirection else { return } let angle = atan2(touchLocation.y - center.y, touchLocation.x - center.x) var angleDiff = deltaAngle - angle // 根据方向调整角度计算逻辑 if direction == .left { // 左滑时反转角度差的符号,确保旋转方向正确 angleDiff = angle - deltaAngle // 限制左滑的角度范围(可根据需求调整) if angleDiff >= 0, angleDiff < 5, angleDiff < 0.50 { container.transform = CGAffineTransform(rotationAngle: angleDiff) } } else { // 右滑保留原有逻辑 if angleDiff <= 0, angleDiff > -5, angleDiff > -0.50 { container.transform = CGAffineTransform(rotationAngle: -angleDiff) } } case .ended, .cancelled: // 动画恢复初始状态 UIView.animate(withDuration: 0.6, animations: { [weak self] in guard let this = self else { return } this.container.transform = CGAffineTransform(rotationAngle: 0) }, completion: { [weak self] _ in guard let this = self else { return } this.container.setAnchorPoint(anchorPoint: .init(x: 0.5, y: 0.5)) this.currentDirection = nil }) default: break } }
关键说明
- 方向判断:在
.began状态时,通过触摸位置与容器中心的x坐标比较,确定是左滑还是右滑 - 动态锚点:根据滑动方向设置对应锚点,右滑用
(1,1),左滑用(0,1) - 角度适配:左滑时反转角度差的符号,保证旋转方向符合预期,同时保留角度范围限制
- 状态重置:手势结束或取消时,统一恢复容器的transform和锚点,并清空当前方向标记
内容的提问来源于stack exchange,提问作者martin wotterman
相关产品推荐
相关产品推荐

