一个抽象基类,易于创建自定义下拉刷新控件
上述所有内容都使用了不到150行代码。
现在有很多PTR库,但没有一个完全适合我的需求。下拉刷新控件完成时很棒,但制作起来很繁琐。我创建这个库,这样我就可以直接跳入自定义动画,而不必担心自定义。对于我的实现,你只需要重写一些抽象方法,详细说明高度、动画持续时间和每个动画周期中运行的内容。
我的实现提供了以下功能:
在初始化后,您只需一行代码即可将刷新控件添加到滚动视图。当滚动视图被下拉超过其高度时,将调用刷新块中的代码。
重要:您必须调用endRefreshing来停止刷新控件。一个很好的地方是在网络请求的回调中做这件事。
-(void) viewDidLoad {
[super viewDidLoad];
...
self.myRefreshControl = [[MyRefreshControl alloc] init];
[self.myRefreshControl addToScrollView:self.tableView withRefreshBlock:^{
[self tableViewWasPulledToRefresh];
}];
...
}
-(void)tableViewWasPulledToRefresh {
[self someBigNetworkRequestWithCallback:^{
[self.myRefreshControl endRefreshing];
}];
}
JHRefreshControl是一个抽象基类,这意味着您需要覆盖一些函数。以下方法必须实现,否则您的应用程序会崩溃。您可以在AbstractFunctionsCopyandPaste.txt中找到一个代码片段,包含.m文件中所有方法的代码,可以直接复制和粘贴。
在ColorChangeRefreshControl.m中可以找到一个很好的例子。
// sets the height of the refresh control
+(CGFloat)height;
//sets the animation duration of each animation cycle for the refresh control.
+(NSTimeInterval)animationDuration;
// use this to setup the refresh control.
// put setup code here instead of init
-(void)setup;
// Used to control UI elements during scrolling
// only called while scroll view is scrolling
// not during PTR animation
-(void)handleScrollingOnAnimationView:(UIView *)animationView
withPullDistance:(CGFloat)pullDistance
pullRatio:(CGFloat)pullRatio
pullVelocity:(CGFloat)pullVelocity;
// Set refresh animation to correct state before a new cycle begins
// Called before each animation
-(void)setupRefreshControlForAnimationView:(UIView *)animationView;
// UI changes to be animated continuously
// until endRefreshing is called.
-(void)animationCycleForAnimationView:(UIView *)animationView;
// manual refresh
// call in view controller
-(void)forceRefresh;
// called to end the animation
// call in view controller
-(void)endRefreshing;
// should reset UI elements here
// called after refresh control finishes and is hidden
// override in subclass
-(void)resetAnimationView:(UIView *)animationView;
// called to add a subview to the animation view
// important to only add subviews in this manner
// call in subclass to setup refresh control
-(void)addSubviewToRefreshAnimationView:(UIView *)subview;
// sets the animation delay
// override in subclass
+(NSTimeInterval)animationDelay;
//getters
@property (atomic, readonly, getter=isRefreshing) BOOL refreshing;
@property (nonatomic, readonly) CGFloat height;
@property (nonatomic, readonly) NSTimeInterval animationDuration;
@property (nonatomic, readonly) NSTimeInterval animationDelay;
你说的这个“动画周期”是什么意思??
动画周期是在动画块内部重复执行的UI代码。现实生活中的例子包括Yik Yak应用中 yak 头部的一次旋转,或者在Snapchat应用中一次颜色的改变。
那么真正发生了什么??
刷新控制有一个运行动画的子视图。当刷新控制被拉动时,它会为周期设置动画视图(确保yak的头部处于正确的起始角度),然后执行动画(yak的头部旋转一次。)然后检查是否调用了 endRefreshing。如果已经调用,就退出。如果没有调用,则再次设置并运行动画(并继续这种模式,直到调用 endRefreshing)。
以下过程会在从拉动滚动视图刷新到调用 endRefreshing 期间持续运行。
setupRefreshControlForAnimation
-> animate [ animation cycle ]
-> completion [animation cycle finished]
-> if refreshing:
recurse (another cycle)
-> else:
refreshing ended
-> [completion animation]
-> reset animation view
如果您想调试上述过程,请将 JHPTR_DEBUG_FLAG 设置为 YES 在 JHPullToRefreshKit.h 中。这将提供详细输出,您可以使用这些信息查看在使用 JHRefreshControl 或 JHLayerAnimationRefreshControl 子类时调用哪些函数。
这是在滚动视图向下滚动时(偏移量 < 高度)刷新控制显示的方式。必须在 initWithType: 中设置。如果调用简单 init,则默认为 SlideDown。
JHRefreshControlTypeSlideDown: 刷新控制将随着滚动视图的滚动而向下滑动。
JHRefreshControlTypeBackground: 刷新控制位于滚动视图后面,并且在滚动视图向下滚动时被揭露。
MyRefreshControl *refreshControl = [[MyRefreshControl alloc] initWithType:JHRefreshControlTypeSlideDown];
这是在滚动视图已经滚动超过其高度时动画视图的锚定方式。(偏移量 > 高度)。默认为 Top。
JHRefreshControlAnchorPositionTop: 动画视图将固定在顶部。
JHRefreshControlAnchorPositionMiddle: 动画视图将随着滚动视图偏移量的增加而拉伸,并固定在中间。
JHRefreshControlAnchorPositionBottom: 动画视图将固定在底部。
self.anchorPosition = JHRefreshControlAnchorPositionTop;
这决定了动画周期在哪种类型的动画块中运行。
self.animationType = JHRefreshControlAnimationTypeDefault;
您还可以为这些动画添加 UIViewAnimationOptions。
self->animationOptions = UIViewAnimationOptionCurveEaseInOut;
要添加例如我提供的 Yahoo 新闻摘要示例中的退出动画,请覆盖以下函数。退出动画在调用 endRefreshing 后以及注销刷新控件之前调用。完成时一定要调用 completion(),以便刷新控件知道要注销!
-(void)exitAnimationForRefreshView:(UIView *)animationView withCompletion:(JHCompletionBlock)completion;
以下是 淡出至黑色 退出动画的示例
-(void) exitAnimationForRefreshView:(UIView *)animationView withCompletion:(JHCompletionBlock)completion {
[UIView animateWithDuration:1.0 animations:^{
self.backgroundColor = [UIColor blackColor];
self.mySubview.alpha = 0.0;
} completion:^(BOOL finished) {
[self.mySubview removeFromSuperview];
completion();
}];
}
有些动画 лучше выполнять с помощью CABasicAnimations。这一点的一个很好的例子是旋转 ≥ 360 度。如果你想让一个视图旋转 450 度,UIView 动画块只能让它旋转 90 度。如果你想有一个执行 CABasicAnimations 的刷新控件,就必须继承 JHLayerAnimationRefreshControl 并覆盖另一個抽象方法。
-(CALayer *) targetLayer; 设置你想要动画的层。
要向这个层添加动画,只需调用
-(void)addCABasicAnimationWithKeyPath:(NSString *)keyPath fromValue:(CGFloat)fromValue toValue:(CGFloat)toValue;
我提供的 Yahoo 新闻摘要示例就是一个很好的例子。为了使点旋转,我在 setup 中调用上述函数。
[self addCABasicAnimationWithKeyPath:@"transform.rotation.z" fromValue:0.0 toValue:2*M_PI];
这确保了我在 targetLayer 中返回的层将在每个动画周期旋转 360 度。
请注意,当继承 JHLayerAnimationRefreshControl 时,setupRefreshControlForAnimationView: 和 animationCycleForAnimationView: 不需要被覆盖。然而,如果你想要执行其他 UIView 动画,你可以覆盖这些函数。
你的视图控制器或滚动视图可以符合一些代理方法。
self.myRefreshControl.delegate = self;
@protocol JHRefreshControlDelegate <NSObject>
-(void)refreshControlDidStart:(JHRefreshControl *)refreshControl;
-(void)refreshControlDidEnd:(JHRefreshControl *)refreshControl;
@optional
-(void)refreshControlDidStartAnimationCycle:(JHRefreshControl *)refreshControl;
-(void)refreshControlDidEndAnimationCycle:(JHRefreshControl *)refreshControl;
@end
请随时通过电子邮件发给我 [email protected]。我很乐意听听你的看法,或者看到使用此技术的示例。