[英]Custom UIRefreshControl with Lottie
My goal is to replace the default spinner of the UIRefreshControl
with a Lottie animation.我的目标是用 Lottie 动画替换
UIRefreshControl
的默认微调器。
My issue is that the animation does not play immediately when I pull down my UICollectionView
whose subview is the UIRefreshControl
.我的问题是当我拉下我的
UICollectionView
时动画不会立即播放,其子视图是UIRefreshControl
。 The animation only plays when I scroll down slightly and pause my finger.动画仅在我稍微向下滚动并暂停手指时播放。 The moment I again start moving the scrolling position it immediately goes back to its initial starting state not playing.
我再次开始移动滚动位置的那一刻,它立即回到不播放的初始状态。
Any guidance would be appreciated, below is relevant code..任何指导将不胜感激,以下是相关代码..
func setupRefreshControl() {
guard let refreshContents = Bundle.main.loadNibNamed("RefreshControlView", owner: self, options: nil), let refreshView = refreshContents[0] as? RefreshControlView else { return }
refreshView.frame = refreshControl.frame
refreshControl.addSubview(refreshView)
refreshControl.tintColor = UIColor.clear
refreshControl.backgroundColor = UIColor.clear
refreshControl.addTarget(self, action: #selector(exampleFunction), for: .valueChanged)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
for subview in refreshControl.subviews {
if let refreshControlView = subview as? RefreshControlView {
refreshControlView.activityIndicatorControl.animationView.setAnimation(named: "lottieJson")
refreshControlView.activityIndicatorControl.animationView.loopAnimation = true
refreshControlView.activityIndicatorControl.animationView.play()
break
} else {
continue
}
}
}
You don't see the animation playing while you are scrolling because every time scrollViewDidScroll
is called you restart the animation by calling play
.您在滚动时看不到动画播放,因为每次调用
scrollViewDidScroll
时,您都会通过调用play
重新启动动画。 And because that function is called almost constantly while you scroll the animation does not have a chance to play more than its first frame.并且因为在滚动动画时几乎不断调用该函数,所以没有机会播放超过其第一帧的内容。 So it looks paused.
所以它看起来暂停了。
When implementing a custom Refresh Control you have to implement 3 phases:实现自定义刷新控件时,您必须实现 3 个阶段:
1. User scrolls but the refresh has not been triggered yet 1.用户滚动但尚未触发刷新
In this phase you probably want to show a progress in your animation depending on how far the user has scrolled.在此阶段,您可能希望根据用户滚动的距离显示动画进度。 To do this you calculate the progress in
scrollViewDidScroll
and pass it to the LOTAnimationView
's animationProgress
property.为此,您在
scrollViewDidScroll
计算进度并将其传递给LOTAnimationView
的animationProgress
属性。
To calculate this progress you have to know how far the user has to scroll down until the refresh is triggered.要计算此进度,您必须知道用户必须向下滚动多远才能触发刷新。 In my experience this happens at a
contentOffset.y
of approximately 150
.根据我的经验,这发生在
contentOffset.y
大约150
。
2. Refresh is triggered 2.刷新被触发
When the user has scrolled down enough a .valueChanged
ControlEvent is triggered.当用户向下滚动足够
.valueChanged
会触发.valueChanged
ControlEvent。 When this happens you start the looping animation by calling play()
on the LOTAnimationView
发生这种情况时,您可以通过在
LOTAnimationView
上调用play()
来启动循环动画
3. Refresh is done 3.刷新完成
When the refreshing is completed you call endRefreshing()
on your custom Refresh Control.刷新完成后,您可以在自定义刷新控件上调用
endRefreshing()
。 When this happens you stop the animation by calling stop()
on the LOTAnimationView
发生这种情况时,您可以通过在
LOTAnimationView
上调用stop()
来停止动画
Check out this small example of a LottieRefreshControl that I use in one of my projects:查看我在我的一个项目中使用的 LottieRefreshControl 的这个小例子:
import UIKit
import Lottie
class LottieRefreshControl: UIRefreshControl {
fileprivate let animationView = Lottie.AnimationView(name: "searchAnimation")
fileprivate var isAnimating = false
fileprivate let maxPullDistance: CGFloat = 150
override init() {
super.init(frame: .zero)
setupView()
setupLayout()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func updateProgress(with offsetY: CGFloat) {
guard !isAnimating else { return }
let progress = min(abs(offsetY / maxPullDistance), 1)
animationView.currentProgress = progress
}
override func beginRefreshing() {
super.beginRefreshing()
isAnimating = true
animationView.currentProgress = 0
animationView.play()
}
override func endRefreshing() {
super.endRefreshing()
animationView.stop()
isAnimating = false
}
}
private extension LottieRefreshControl {
func setupView() {
// hide default indicator view
tintColor = .clear
animationView.loopMode = .loop
addSubview(animationView)
addTarget(self, action: #selector(beginRefreshing), for: .valueChanged)
}
func setupLayout() {
animationView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
animationView.centerXAnchor.constraint(equalTo: centerXAnchor),
animationView.centerYAnchor.constraint(equalTo: centerYAnchor),
animationView.widthAnchor.constraint(equalToConstant: 50),
animationView.heightAnchor.constraint(equalToConstant: 32)
])
}
}
Now you only have to call updateProgress
on the Refresh Control from scrollViewDidScroll
:现在,您只需从
scrollViewDidScroll
调用 Refresh Control 上的updateProgress
:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
refreshControl.updateProgress(with: scrollView.contentOffset.y)
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.