I am trying to implement a similar photo selection method to the stock iOS photos app. This works using a UIPanGestureRecognizer
which disables the scroll on the UICollectionView
. In the stock photos app, it seems like when the pan reaches the top or bottom of the screen the scroll view starts scrolling at a given speed and the closer to the edge of the screen, the quicker the scroll.
It doesn't seem as if there's an API to scroll at a given speed in a UIScrollView
or UICollectionView
. Is there any clever way I could do this with the existing methods such as scrollToVisibleRect
or setting the content offset within an animation block? I'm worried that whilst these may work, they will be jerky in their movement!
I ended up creating a SpeedScrollableCollectionViewController
for this and controlling it using a UIPanGestureRecognizer
(not relevant to the question
class SpeedScrollableCollectionViewController: UICollectionViewController {
private var lastFrameTime: CFTimeInterval?
private var displayLink: CADisplayLink?
override init(collectionViewLayout layout: UICollectionViewLayout) {
super.init(collectionViewLayout: layout)
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
var scrollSpeed: CGPoint = .zero {
didSet {
guard scrollSpeed != .zero else {
displayLink?.isPaused = true
return
}
guard displayLink == nil else {
lastFrameTime = CACurrentMediaTime()
displayLink?.isPaused = false
return
}
lastFrameTime = CACurrentMediaTime()
displayLink = CADisplayLink(target: self, selector: #selector(updateContentOfffset))
displayLink?.add(to: .main, forMode: .common)
}
}
@objc func updateContentOfffset() {
defer {
lastFrameTime = CACurrentMediaTime()
}
guard let lastFrameTime = lastFrameTime else { return }
let dt = CACurrentMediaTime() - lastFrameTime
let dx = scrollSpeed.x * CGFloat(dt)
let dy = scrollSpeed.y * CGFloat(dt)
var currentOffset = collectionView.contentOffset
currentOffset.x += dx
currentOffset.y += dy
collectionView.setContentOffset(currentOffset, animated: false)
}
}