背景
scrollView中的directionalLockEnabled的功能本来就是用来让用户每次只在一个方向上滚动,竖直或者水平,但是如果初始移动方向处于45°左右的时候,这个锁就失效了。苹果官方发现了这个问题,在官方文档里有如下描述,但是没有解决:
"If this property is NO, scrolling is permitted in both horizontal and vertical directions. If this property is YES and the user begins dragging in one general direction (horizontally or vertically), the scroll view disables scrolling in the other direction. If the drag direction is diagonal, then scrolling will not be locked and the user can drag in any direction until the drag completes. The default value is NO"
转载本文需注明:原文链接 + 稀土掘金 RisingSSR
讨论
这是stackoverflow上的讨论,stackoverflow.com/questions/7…
我在网上找到了一个大神的解题思路:
iOS的scrollView属性directionalLockEnabled的问题修正
原文链接:blog.csdn.net/liu_da/arti…
(遵循 CC 4.0 BY-SA 版权协议)
方案
上面的解题思路在于判断 Dragging 等代理,那我们是不是可以把这部分归到内部去执行呢?
在 UIScrollView 中,有一个 panGestureRecognizer 的存在。(以 Swift 为主语言)
open var panGestureRecognizer: UIPanGestureRecognizer { get }
那是不是我自定义继承 UIScrollView 并为 panGestureRecognizer 添加一个 target 就可以了。
代码
这里用 Swift 语言,OC 同理。(相信会 OC 的宝宝都会 Swift)
import UIKit
@objc
open class DirectionalLockScrollView: UIScrollView {
private var scrollViewStartPosPoint: CGPoint = .zero
private var scrollDirection: Int = 0
public override init(frame: CGRect) {
super.init(frame: frame)
_DirectionalLock_init()
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func _DirectionalLock_init() {
self.isDirectionalLockEnabled = false
self.panGestureRecognizer.addTarget(self, action: #selector(_DirectionalLock_handel_pan(_:)))
}
@objc
private func _DirectionalLock_handel_pan(_ pan: UIPanGestureRecognizer) {
if pan.state == .began {
scrollViewStartPosPoint = self.contentOffset
scrollDirection = 0
}
if pan.state == .changed {
if scrollDirection == 0 {
if abs(scrollViewStartPosPoint.x - self.contentOffset.x) <
abs(scrollViewStartPosPoint.y - self.contentOffset.y) {
// Vertical Scrolling
self.scrollDirection = 1
} else {
// Horitonzal Scrolling
self.scrollDirection = 2
}
}
if self.scrollDirection == 1 {
self.contentOffset = CGPoint(x: self.scrollViewStartPosPoint.x, y: self.contentOffset.y)
} else if self.scrollDirection == 2 {
self.contentOffset = CGPoint(x: self.contentOffset.x, y: self.scrollViewStartPosPoint.y)
}
}
if pan.state == .ended {
self.scrollDirection = 0
}
}
}
优化
熟悉 OC 的朋友们都知道,OC 里面有 runtime(方法交换、关联对象) 和 NSProxy 等。
(Swift 也有 rumtime,但用起来确实不如 OC 写得快,且 Swift 没有 NSProxy。)
由于本文只列举了 UIScrollView 这一种情况,其实 UICollectionView 也会出现这种问题。
所以我认为可以使用 OC 的东西写一个通用组件,使得解决这个问题不需要继承。