|
|
@@ -42,12 +42,12 @@ - (void)willMoveToSuperview:(UIView *)newSuperview
|
|
|
[newSuperview addObserver:self forKeyPath:MJRefreshContentSize options:NSKeyValueObservingOptionNew context:nil];
|
|
|
|
|
|
// 重新调整frame
|
|
|
- [self adjustFrame];
|
|
|
+ [self adjustFrameWithContentSize];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#pragma mark 重写调整frame
|
|
|
-- (void)adjustFrame
|
|
|
+- (void)adjustFrameWithContentSize
|
|
|
{
|
|
|
// 内容的高度
|
|
|
CGFloat contentHeight = self.scrollView.contentSizeHeight;
|
|
|
@@ -60,92 +60,121 @@ - (void)adjustFrame
|
|
|
#pragma mark 监听UIScrollView的属性
|
|
|
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
|
|
{
|
|
|
- [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
|
|
-
|
|
|
- // 不是contentSize,直接返回
|
|
|
- if (![MJRefreshContentSize isEqualToString:keyPath]) return;
|
|
|
-
|
|
|
// 不能跟用户交互,直接返回
|
|
|
if (!self.userInteractionEnabled || self.alpha <= 0.01 || self.hidden) return;
|
|
|
|
|
|
- // 如果正在刷新,直接返回
|
|
|
- if (self.state == MJRefreshStateRefreshing) return;
|
|
|
+ if ([MJRefreshContentSize isEqualToString:keyPath]) {
|
|
|
+ // 调整frame
|
|
|
+ [self adjustFrameWithContentSize];
|
|
|
+ } else if ([MJRefreshContentOffset isEqualToString:keyPath]) {
|
|
|
+#warning 这个返回一定要放这个位置
|
|
|
+ // 如果正在刷新,直接返回
|
|
|
+ if (self.state == MJRefreshStateRefreshing) return;
|
|
|
+
|
|
|
+ // 调整状态
|
|
|
+ [self adjustStateWithContentOffset];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 调整状态
|
|
|
+ */
|
|
|
+- (void)adjustStateWithContentOffset
|
|
|
+{
|
|
|
+ // 当前的contentOffset
|
|
|
+ CGFloat currentOffsetY = self.scrollView.contentOffsetY;
|
|
|
+ // 尾部控件刚好出现的offsetY
|
|
|
+ CGFloat happenOffsetY = [self happenOffsetY];
|
|
|
+
|
|
|
+ // 如果是向下滚动到看不见尾部控件,直接返回
|
|
|
+ if (currentOffsetY <= happenOffsetY) return;
|
|
|
|
|
|
- // 调整frame
|
|
|
- [self adjustFrame];
|
|
|
+ if (self.scrollView.isDragging) {
|
|
|
+ // 普通 和 即将刷新 的临界点
|
|
|
+ CGFloat normal2pullingOffsetY = happenOffsetY + self.height;
|
|
|
+
|
|
|
+ if (self.state == MJRefreshStateNormal && currentOffsetY > normal2pullingOffsetY) {
|
|
|
+ // 转为即将刷新状态
|
|
|
+ self.state = MJRefreshStatePulling;
|
|
|
+ } else if (self.state == MJRefreshStatePulling && currentOffsetY <= normal2pullingOffsetY) {
|
|
|
+ // 转为普通状态
|
|
|
+ self.state = MJRefreshStateNormal;
|
|
|
+ }
|
|
|
+ } else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开
|
|
|
+ // 开始刷新
|
|
|
+ self.state = MJRefreshStateRefreshing;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
#pragma mark - 状态相关
|
|
|
#pragma mark 设置状态
|
|
|
- (void)setState:(MJRefreshState)state
|
|
|
{
|
|
|
+ // 1.一样的就直接返回
|
|
|
if (self.state == state) return;
|
|
|
+
|
|
|
+ // 2.保存旧状态
|
|
|
MJRefreshState oldState = self.state;
|
|
|
|
|
|
+ // 3.调用父类方法
|
|
|
[super setState:state];
|
|
|
|
|
|
+ // 4.根据状态来设置属性
|
|
|
switch (state)
|
|
|
{
|
|
|
- case MJRefreshStatePulling:
|
|
|
- {
|
|
|
- self.statusLabel.text = MJRefreshFooterReleaseToRefresh;
|
|
|
-
|
|
|
- [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
|
|
|
- self.arrowImage.transform = CGAffineTransformIdentity;
|
|
|
- UIEdgeInsets inset = self.scrollView.contentInset;
|
|
|
- inset.bottom = self.scrollViewOriginalInset.bottom;
|
|
|
- self.scrollView.contentInset = inset;
|
|
|
- }];
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
case MJRefreshStateNormal:
|
|
|
{
|
|
|
+ // 设置文字
|
|
|
self.statusLabel.text = MJRefreshFooterPullToRefresh;
|
|
|
|
|
|
- // 刚刷新完毕
|
|
|
- CGFloat animDuration = MJRefreshSlowAnimationDuration;
|
|
|
- CGFloat deltaH = [self contentBreakView];
|
|
|
- CGPoint tempOffset;
|
|
|
+ // 刷新完毕
|
|
|
+ if (MJRefreshStateRefreshing == oldState) {
|
|
|
+ self.arrowImage.transform = CGAffineTransformMakeRotation(M_PI);
|
|
|
+ [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{
|
|
|
+ self.scrollView.contentInsetBottom = self.scrollViewOriginalInset.bottom;
|
|
|
+ }];
|
|
|
+ } else {
|
|
|
+ // 执行动画
|
|
|
+ [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
|
|
|
+ self.arrowImage.transform = CGAffineTransformMakeRotation(M_PI);
|
|
|
+ }];
|
|
|
+ }
|
|
|
|
|
|
+ CGFloat deltaH = [self heightForContentBreakView];
|
|
|
int currentCount = [self totalDataCountInScrollView];
|
|
|
+ // 刚刷新完毕
|
|
|
if (MJRefreshStateRefreshing == oldState && deltaH > 0 && currentCount != self.lastRefreshCount) {
|
|
|
- tempOffset = self.scrollView.contentOffset;
|
|
|
- animDuration = 0;
|
|
|
+ self.scrollView.contentOffsetY = self.scrollView.contentOffsetY;
|
|
|
}
|
|
|
+ break;
|
|
|
+ }
|
|
|
|
|
|
- [UIView animateWithDuration:animDuration animations:^{
|
|
|
- UIEdgeInsets inset = self.scrollView.contentInset;
|
|
|
- inset.bottom = self.scrollViewOriginalInset.bottom;
|
|
|
- self.scrollView.contentInset = inset;
|
|
|
- }];
|
|
|
+ case MJRefreshStatePulling:
|
|
|
+ {
|
|
|
+ // 设置文字
|
|
|
+ self.statusLabel.text = MJRefreshFooterReleaseToRefresh;
|
|
|
|
|
|
- [UIView animateWithDuration:animDuration ? MJRefreshFastAnimationDuration : animDuration animations:^{
|
|
|
- self.arrowImage.transform = CGAffineTransformMakeRotation(M_PI);
|
|
|
+ [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
|
|
|
+ self.arrowImage.transform = CGAffineTransformIdentity;
|
|
|
}];
|
|
|
-
|
|
|
- if (animDuration == 0) {
|
|
|
- self.scrollView.contentOffset = tempOffset;
|
|
|
- }
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
case MJRefreshStateRefreshing:
|
|
|
{
|
|
|
+ // 设置文字
|
|
|
+ self.statusLabel.text = MJRefreshFooterRefreshing;
|
|
|
+
|
|
|
// 记录刷新前的数量
|
|
|
self.lastRefreshCount = [self totalDataCountInScrollView];
|
|
|
- self.statusLabel.text = MJRefreshFooterRefreshing;
|
|
|
|
|
|
[UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
|
|
|
- self.arrowImage.transform = CGAffineTransformMakeRotation(M_PI);
|
|
|
- UIEdgeInsets inset = self.scrollView.contentInset;
|
|
|
- CGFloat bottom = self.frame.size.height + self.scrollViewOriginalInset.bottom;
|
|
|
- CGFloat deltaH = [self contentBreakView];
|
|
|
+ CGFloat bottom = self.height + self.scrollViewOriginalInset.bottom;
|
|
|
+ CGFloat deltaH = [self heightForContentBreakView];
|
|
|
if (deltaH < 0) { // 如果内容高度小于view的高度
|
|
|
bottom -= deltaH;
|
|
|
}
|
|
|
- inset.bottom = bottom;
|
|
|
- self.scrollView.contentInset = inset;
|
|
|
+ self.scrollView.contentInsetBottom = bottom;
|
|
|
}];
|
|
|
break;
|
|
|
}
|
|
|
@@ -155,7 +184,6 @@ - (void)setState:(MJRefreshState)state
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- (int)totalDataCountInScrollView
|
|
|
{
|
|
|
int totalCount = 0;
|
|
|
@@ -176,27 +204,23 @@ - (int)totalDataCountInScrollView
|
|
|
}
|
|
|
|
|
|
#pragma mark 获得scrollView的内容 超出 view 的高度
|
|
|
-- (CGFloat)contentBreakView
|
|
|
+- (CGFloat)heightForContentBreakView
|
|
|
{
|
|
|
CGFloat h = self.scrollView.frame.size.height - self.scrollViewOriginalInset.bottom - self.scrollViewOriginalInset.top;
|
|
|
return self.scrollView.contentSize.height - h;
|
|
|
}
|
|
|
|
|
|
#pragma mark - 在父类中用得上
|
|
|
-// 合理的Y值(刚好看到上拉刷新控件时的contentOffset.y,取相反数)
|
|
|
-- (CGFloat)validY
|
|
|
+/**
|
|
|
+ * 刚好看到上拉刷新控件时的contentOffset.y
|
|
|
+ */
|
|
|
+- (CGFloat)happenOffsetY
|
|
|
{
|
|
|
- CGFloat deltaH = [self contentBreakView];
|
|
|
+ CGFloat deltaH = [self heightForContentBreakView];
|
|
|
if (deltaH > 0) {
|
|
|
return deltaH - self.scrollViewOriginalInset.top;
|
|
|
} else {
|
|
|
return - self.scrollViewOriginalInset.top;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-// view的类型
|
|
|
-- (int)viewType
|
|
|
-{
|
|
|
- return MJRefreshViewTypeFooter;
|
|
|
-}
|
|
|
@end
|