Initial commit
This commit is contained in:
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// SVPullToRefresh.h
|
||||
//
|
||||
// Created by Sam Vermette on 23.04.12.
|
||||
// Copyright (c) 2012 samvermette.com. All rights reserved.
|
||||
//
|
||||
// https://github.com/samvermette/SVPullToRefresh
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface SVPullToRefresh : UIView
|
||||
|
||||
@property (nonatomic, strong) UIColor *arrowColor;
|
||||
@property (nonatomic, strong) UIColor *textColor;
|
||||
@property (nonatomic, readwrite) UIActivityIndicatorViewStyle activityIndicatorViewStyle;
|
||||
@property (nonatomic, strong) NSDate *lastUpdatedDate;
|
||||
|
||||
- (void)triggerRefresh;
|
||||
- (void)stopAnimating;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
// extends UIScrollView
|
||||
|
||||
@interface UIScrollView (SVPullToRefresh)
|
||||
|
||||
- (void)addPullToRefreshWithActionHandler:(void (^)(void))actionHandler;
|
||||
|
||||
@property (nonatomic, strong) SVPullToRefresh *pullToRefreshView;
|
||||
|
||||
@end
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
//
|
||||
// SVPullToRefresh.m
|
||||
//
|
||||
// Created by Sam Vermette on 23.04.12.
|
||||
// Copyright (c) 2012 samvermette.com. All rights reserved.
|
||||
//
|
||||
// https://github.com/samvermette/SVPullToRefresh
|
||||
//
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import "SVPullToRefresh.h"
|
||||
|
||||
enum {
|
||||
SVPullToRefreshStateHidden = 1,
|
||||
SVPullToRefreshStateVisible,
|
||||
SVPullToRefreshStateTriggered,
|
||||
SVPullToRefreshStateLoading
|
||||
};
|
||||
|
||||
typedef NSUInteger SVPullToRefreshState;
|
||||
|
||||
|
||||
@interface SVPullToRefresh ()
|
||||
|
||||
- (id)initWithScrollView:(UIScrollView*)scrollView;
|
||||
- (void)rotateArrow:(float)degrees hide:(BOOL)hide;
|
||||
- (void)setScrollViewContentInset:(UIEdgeInsets)contentInset;
|
||||
- (void)scrollViewDidScroll:(CGPoint)contentOffset;
|
||||
|
||||
@property (nonatomic, copy) void (^actionHandler)(void);
|
||||
@property (nonatomic, readwrite) SVPullToRefreshState state;
|
||||
|
||||
@property (nonatomic, strong) UIImageView *arrow;
|
||||
@property (nonatomic, strong, readonly) UIImage *arrowImage;
|
||||
@property (nonatomic, strong) UIActivityIndicatorView *activityIndicatorView;
|
||||
@property (nonatomic, strong) UILabel *titleLabel;
|
||||
|
||||
@property (nonatomic, strong, readonly) UILabel *dateLabel;
|
||||
@property (nonatomic, strong, readonly) NSDateFormatter *dateFormatter;
|
||||
|
||||
@property (nonatomic, weak) UIScrollView *scrollView;
|
||||
@property (nonatomic, readwrite) UIEdgeInsets originalScrollViewContentInset;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@implementation SVPullToRefresh
|
||||
|
||||
// public properties
|
||||
@synthesize actionHandler, arrowColor, textColor, activityIndicatorViewStyle, lastUpdatedDate;
|
||||
|
||||
@synthesize state;
|
||||
@synthesize scrollView = _scrollView;
|
||||
@synthesize arrow, arrowImage, activityIndicatorView, titleLabel, dateLabel, dateFormatter, originalScrollViewContentInset;
|
||||
|
||||
- (void)dealloc {
|
||||
[self.scrollView removeObserver:self forKeyPath:@"contentOffset"];
|
||||
}
|
||||
|
||||
- (id)initWithScrollView:(UIScrollView *)scrollView {
|
||||
self = [super initWithFrame:CGRectZero];
|
||||
self.scrollView = scrollView;
|
||||
[_scrollView addSubview:self];
|
||||
|
||||
// default styling values
|
||||
self.arrowColor = [UIColor grayColor];
|
||||
self.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
|
||||
self.textColor = [UIColor darkGrayColor];
|
||||
|
||||
self.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 20, 150, 20)];
|
||||
titleLabel.text = NSLocalizedString(@"Pull to refresh...",);
|
||||
|
||||
titleLabel.font = [UIFont boldSystemFontOfSize:14];
|
||||
titleLabel.backgroundColor = [UIColor clearColor];
|
||||
titleLabel.textColor = textColor;
|
||||
|
||||
[self addSubview:titleLabel];
|
||||
|
||||
[self addSubview:self.arrow];
|
||||
|
||||
[scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
|
||||
[scrollView addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil];
|
||||
|
||||
self.originalScrollViewContentInset = scrollView.contentInset;
|
||||
|
||||
self.state = SVPullToRefreshStateHidden;
|
||||
self.frame = CGRectMake(0, -60, scrollView.bounds.size.width, 60);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
CGFloat remainingWidth = self.superview.bounds.size.width-200;
|
||||
float position = 0.50;
|
||||
|
||||
CGRect titleFrame = titleLabel.frame;
|
||||
titleFrame.origin.x = ceil(remainingWidth*position+44);
|
||||
titleLabel.frame = titleFrame;
|
||||
|
||||
CGRect dateFrame = dateLabel.frame;
|
||||
dateFrame.origin.x = titleFrame.origin.x;
|
||||
dateLabel.frame = dateFrame;
|
||||
|
||||
CGRect arrowFrame = arrow.frame;
|
||||
arrowFrame.origin.x = ceil(remainingWidth*position);
|
||||
arrow.frame = arrowFrame;
|
||||
|
||||
self.activityIndicatorView.center = self.arrow.center;
|
||||
}
|
||||
|
||||
#pragma mark - Getters
|
||||
|
||||
- (UIImageView *)arrow {
|
||||
if(!arrow) {
|
||||
arrow = [[UIImageView alloc] initWithImage:self.arrowImage];
|
||||
arrow.frame = CGRectMake(0, 6, 22, 48);
|
||||
arrow.backgroundColor = [UIColor clearColor];
|
||||
}
|
||||
return arrow;
|
||||
}
|
||||
|
||||
- (UIImage *)arrowImage {
|
||||
CGRect rect = CGRectMake(0, 0, 22, 48);
|
||||
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
|
||||
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
[[UIColor clearColor] set];
|
||||
CGContextFillRect(context, rect);
|
||||
|
||||
[self.arrowColor set];
|
||||
CGContextTranslateCTM(context, 0, rect.size.height);
|
||||
CGContextScaleCTM(context, 1.0, -1.0);
|
||||
CGContextClipToMask(context, rect, [[UIImage imageNamed:@"SVPullToRefresh.bundle/arrow"] CGImage]);
|
||||
CGContextFillRect(context, rect);
|
||||
|
||||
UIImage *output = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return output;
|
||||
}
|
||||
|
||||
- (UIActivityIndicatorView *)activityIndicatorView {
|
||||
if(!activityIndicatorView) {
|
||||
activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
|
||||
activityIndicatorView.hidesWhenStopped = YES;
|
||||
[self addSubview:activityIndicatorView];
|
||||
}
|
||||
return activityIndicatorView;
|
||||
}
|
||||
|
||||
- (UILabel *)dateLabel {
|
||||
if(!dateLabel) {
|
||||
dateLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 28, 180, 20)];
|
||||
dateLabel.font = [UIFont systemFontOfSize:12];
|
||||
dateLabel.backgroundColor = [UIColor clearColor];
|
||||
dateLabel.textColor = textColor;
|
||||
[self addSubview:dateLabel];
|
||||
|
||||
CGRect titleFrame = titleLabel.frame;
|
||||
titleFrame.origin.y = 12;
|
||||
titleLabel.frame = titleFrame;
|
||||
}
|
||||
return dateLabel;
|
||||
}
|
||||
|
||||
- (NSDateFormatter *)dateFormatter {
|
||||
if(!dateFormatter) {
|
||||
dateFormatter = [[NSDateFormatter alloc] init];
|
||||
[dateFormatter setDateStyle:NSDateFormatterShortStyle];
|
||||
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
|
||||
dateFormatter.locale = [NSLocale currentLocale];
|
||||
}
|
||||
return dateFormatter;
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
- (void)setArrowColor:(UIColor *)newArrowColor {
|
||||
arrowColor = newArrowColor;
|
||||
self.arrow.image = self.arrowImage;
|
||||
}
|
||||
|
||||
- (void)setTextColor:(UIColor *)newTextColor {
|
||||
textColor = newTextColor;
|
||||
titleLabel.textColor = newTextColor;
|
||||
dateLabel.textColor = newTextColor;
|
||||
}
|
||||
|
||||
- (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)viewStyle {
|
||||
self.activityIndicatorView.activityIndicatorViewStyle = viewStyle;
|
||||
}
|
||||
|
||||
- (void)setScrollViewContentInset:(UIEdgeInsets)contentInset {
|
||||
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionAllowUserInteraction|UIViewAnimationOptionBeginFromCurrentState animations:^{
|
||||
self.scrollView.contentInset = contentInset;
|
||||
} completion:^(BOOL finished) {
|
||||
if(self.state == SVPullToRefreshStateHidden && contentInset.top == self.originalScrollViewContentInset.top)
|
||||
[UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
|
||||
arrow.alpha = 0;
|
||||
} completion:NULL];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setLastUpdatedDate:(NSDate *)newLastUpdatedDate {
|
||||
self.dateLabel.text = [NSString stringWithFormat:NSLocalizedString(@"Last Updated: %@",), newLastUpdatedDate?[self.dateFormatter stringFromDate:newLastUpdatedDate]:NSLocalizedString(@"Never",)];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
|
||||
if([keyPath isEqualToString:@"contentOffset"] && self.state != SVPullToRefreshStateLoading)
|
||||
[self scrollViewDidScroll:[[change valueForKey:NSKeyValueChangeNewKey] CGPointValue]];
|
||||
else if([keyPath isEqualToString:@"frame"])
|
||||
[self layoutSubviews];
|
||||
}
|
||||
|
||||
- (void)scrollViewDidScroll:(CGPoint)contentOffset {
|
||||
CGFloat scrollOffsetThreshold = self.frame.origin.y-self.originalScrollViewContentInset.top;
|
||||
|
||||
if(!self.scrollView.isDragging && self.state == SVPullToRefreshStateTriggered)
|
||||
self.state = SVPullToRefreshStateLoading;
|
||||
else if(contentOffset.y > scrollOffsetThreshold && contentOffset.y < -self.originalScrollViewContentInset.top && self.scrollView.isDragging && self.state != SVPullToRefreshStateLoading)
|
||||
self.state = SVPullToRefreshStateVisible;
|
||||
else if(contentOffset.y < scrollOffsetThreshold && self.scrollView.isDragging && self.state == SVPullToRefreshStateVisible)
|
||||
self.state = SVPullToRefreshStateTriggered;
|
||||
else if(contentOffset.y >= -self.originalScrollViewContentInset.top && self.state != SVPullToRefreshStateHidden)
|
||||
self.state = SVPullToRefreshStateHidden;
|
||||
}
|
||||
|
||||
- (void)triggerRefresh {
|
||||
self.state = SVPullToRefreshStateLoading;
|
||||
}
|
||||
|
||||
- (void)stopAnimating {
|
||||
self.state = SVPullToRefreshStateHidden;
|
||||
}
|
||||
|
||||
- (void)setState:(SVPullToRefreshState)newState {
|
||||
state = newState;
|
||||
|
||||
switch (newState) {
|
||||
case SVPullToRefreshStateHidden:
|
||||
titleLabel.text = NSLocalizedString(@"cube_ptr_pull_down_to_refresh",);
|
||||
[self.activityIndicatorView stopAnimating];
|
||||
[self setScrollViewContentInset:self.originalScrollViewContentInset];
|
||||
[self rotateArrow:0 hide:NO];
|
||||
break;
|
||||
|
||||
case SVPullToRefreshStateVisible:
|
||||
titleLabel.text = NSLocalizedString(@"cube_ptr_pull_down_to_refresh",);
|
||||
arrow.alpha = 1;
|
||||
[self.activityIndicatorView stopAnimating];
|
||||
[self setScrollViewContentInset:self.originalScrollViewContentInset];
|
||||
[self rotateArrow:0 hide:NO];
|
||||
break;
|
||||
|
||||
case SVPullToRefreshStateTriggered:
|
||||
titleLabel.text = NSLocalizedString(@"cube_ptr_release_to_refresh",);
|
||||
[self rotateArrow:M_PI hide:NO];
|
||||
break;
|
||||
|
||||
case SVPullToRefreshStateLoading:
|
||||
titleLabel.text = NSLocalizedString(@"loading",);
|
||||
[self.activityIndicatorView startAnimating];
|
||||
[self setScrollViewContentInset:UIEdgeInsetsMake(self.frame.origin.y*-1+self.originalScrollViewContentInset.top, 0, 0, 0)];
|
||||
[self rotateArrow:0 hide:YES];
|
||||
if(actionHandler)
|
||||
actionHandler();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)rotateArrow:(float)degrees hide:(BOOL)hide {
|
||||
[UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
|
||||
self.arrow.layer.transform = CATransform3DMakeRotation(degrees, 0, 0, 1);
|
||||
self.arrow.layer.opacity = !hide;
|
||||
} completion:NULL];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark - UIScrollView (SVPullToRefresh)
|
||||
#import <objc/runtime.h>
|
||||
|
||||
static char UIScrollViewPullToRefreshView;
|
||||
|
||||
@implementation UIScrollView (SVPullToRefresh)
|
||||
|
||||
@dynamic pullToRefreshView;
|
||||
|
||||
- (void)addPullToRefreshWithActionHandler:(void (^)(void))actionHandler {
|
||||
SVPullToRefresh *pullToRefreshView = [[SVPullToRefresh alloc] initWithScrollView:self];
|
||||
pullToRefreshView.actionHandler = actionHandler;
|
||||
self.pullToRefreshView = pullToRefreshView;
|
||||
}
|
||||
|
||||
- (void)setPullToRefreshView:(SVPullToRefresh *)pullToRefreshView {
|
||||
[self willChangeValueForKey:@"pullToRefreshView"];
|
||||
objc_setAssociatedObject(self, &UIScrollViewPullToRefreshView,
|
||||
pullToRefreshView,
|
||||
OBJC_ASSOCIATION_ASSIGN);
|
||||
[self didChangeValueForKey:@"pullToRefreshView"];
|
||||
}
|
||||
|
||||
- (SVPullToRefresh *)pullToRefreshView {
|
||||
return objc_getAssociatedObject(self, &UIScrollViewPullToRefreshView);
|
||||
}
|
||||
|
||||
@end
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// UIScrollView+SVInfiniteScrolling.h
|
||||
//
|
||||
// Created by Sam Vermette on 23.04.12.
|
||||
// Copyright (c) 2012 samvermette.com. All rights reserved.
|
||||
//
|
||||
// https://github.com/samvermette/SVPullToRefresh
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class SVInfiniteScrollingView;
|
||||
|
||||
@interface UIScrollView (SVInfiniteScrolling)
|
||||
|
||||
- (void)addInfiniteScrollingWithActionHandler:(void (^)(void))actionHandler;
|
||||
- (void)triggerInfiniteScrolling;
|
||||
|
||||
@property (nonatomic, strong, readonly) SVInfiniteScrollingView *infiniteScrollingView;
|
||||
@property (nonatomic, assign) BOOL showsInfiniteScrolling;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
enum {
|
||||
SVInfiniteScrollingStateStopped = 0,
|
||||
SVInfiniteScrollingStateTriggered,
|
||||
SVInfiniteScrollingStateLoading,
|
||||
SVInfiniteScrollingStateAll = 10
|
||||
};
|
||||
|
||||
typedef NSUInteger SVInfiniteScrollingState;
|
||||
|
||||
@interface SVInfiniteScrollingView : UIView
|
||||
|
||||
@property (nonatomic, readwrite) UIActivityIndicatorViewStyle activityIndicatorViewStyle;
|
||||
@property (nonatomic, readonly) SVInfiniteScrollingState state;
|
||||
@property (nonatomic, readwrite) BOOL enabled;
|
||||
|
||||
- (void)setCustomView:(UIView *)view forState:(SVInfiniteScrollingState)state;
|
||||
|
||||
- (void)startAnimating;
|
||||
- (void)stopAnimating;
|
||||
|
||||
@end
|
||||
+304
@@ -0,0 +1,304 @@
|
||||
//
|
||||
// UIScrollView+SVInfiniteScrolling.m
|
||||
//
|
||||
// Created by Sam Vermette on 23.04.12.
|
||||
// Copyright (c) 2012 samvermette.com. All rights reserved.
|
||||
//
|
||||
// https://github.com/samvermette/SVPullToRefresh
|
||||
//
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import "UIScrollView+SVInfiniteScrolling.h"
|
||||
|
||||
|
||||
static CGFloat const SVInfiniteScrollingViewHeight = 60;
|
||||
|
||||
@interface SVInfiniteScrollingDotView : UIView
|
||||
|
||||
@property (nonatomic, strong) UIColor *arrowColor;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@interface SVInfiniteScrollingView ()
|
||||
|
||||
@property (nonatomic, copy) void (^infiniteScrollingHandler)(void);
|
||||
|
||||
@property (nonatomic, strong) UIActivityIndicatorView *activityIndicatorView;
|
||||
@property (nonatomic, readwrite) SVInfiniteScrollingState state;
|
||||
@property (nonatomic, strong) NSMutableArray *viewForState;
|
||||
@property (nonatomic, weak) UIScrollView *scrollView;
|
||||
@property (nonatomic, readwrite) CGFloat originalBottomInset;
|
||||
@property (nonatomic, assign) BOOL wasTriggeredByUser;
|
||||
@property (nonatomic, assign) BOOL isObserving;
|
||||
|
||||
- (void)resetScrollViewContentInset;
|
||||
- (void)setScrollViewContentInsetForInfiniteScrolling;
|
||||
- (void)setScrollViewContentInset:(UIEdgeInsets)insets;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
#pragma mark - UIScrollView (SVInfiniteScrollingView)
|
||||
#import <objc/runtime.h>
|
||||
|
||||
static char UIScrollViewInfiniteScrollingView;
|
||||
UIEdgeInsets scrollViewOriginalContentInsets;
|
||||
|
||||
@implementation UIScrollView (SVInfiniteScrolling)
|
||||
|
||||
@dynamic infiniteScrollingView;
|
||||
|
||||
- (void)addInfiniteScrollingWithActionHandler:(void (^)(void))actionHandler {
|
||||
|
||||
if(!self.infiniteScrollingView) {
|
||||
SVInfiniteScrollingView *view = [[SVInfiniteScrollingView alloc] initWithFrame:CGRectMake(0, self.contentSize.height, self.bounds.size.width, SVInfiniteScrollingViewHeight)];
|
||||
view.infiniteScrollingHandler = actionHandler;
|
||||
view.scrollView = self;
|
||||
[self addSubview:view];
|
||||
|
||||
view.originalBottomInset = self.contentInset.bottom;
|
||||
self.infiniteScrollingView = view;
|
||||
self.showsInfiniteScrolling = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)triggerInfiniteScrolling {
|
||||
self.infiniteScrollingView.state = SVInfiniteScrollingStateTriggered;
|
||||
[self.infiniteScrollingView startAnimating];
|
||||
}
|
||||
|
||||
- (void)setInfiniteScrollingView:(SVInfiniteScrollingView *)infiniteScrollingView {
|
||||
[self willChangeValueForKey:@"UIScrollViewInfiniteScrollingView"];
|
||||
objc_setAssociatedObject(self, &UIScrollViewInfiniteScrollingView,
|
||||
infiniteScrollingView,
|
||||
OBJC_ASSOCIATION_ASSIGN);
|
||||
[self didChangeValueForKey:@"UIScrollViewInfiniteScrollingView"];
|
||||
}
|
||||
|
||||
- (SVInfiniteScrollingView *)infiniteScrollingView {
|
||||
return objc_getAssociatedObject(self, &UIScrollViewInfiniteScrollingView);
|
||||
}
|
||||
|
||||
- (void)setShowsInfiniteScrolling:(BOOL)showsInfiniteScrolling {
|
||||
self.infiniteScrollingView.hidden = !showsInfiniteScrolling;
|
||||
|
||||
if(!showsInfiniteScrolling) {
|
||||
if (self.infiniteScrollingView.isObserving) {
|
||||
[self removeObserver:self.infiniteScrollingView forKeyPath:@"contentOffset"];
|
||||
[self removeObserver:self.infiniteScrollingView forKeyPath:@"contentSize"];
|
||||
[self.infiniteScrollingView resetScrollViewContentInset];
|
||||
self.infiniteScrollingView.isObserving = NO;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!self.infiniteScrollingView.isObserving) {
|
||||
[self addObserver:self.infiniteScrollingView forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
|
||||
[self addObserver:self.infiniteScrollingView forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil];
|
||||
[self.infiniteScrollingView setScrollViewContentInsetForInfiniteScrolling];
|
||||
self.infiniteScrollingView.isObserving = YES;
|
||||
|
||||
[self.infiniteScrollingView setNeedsLayout];
|
||||
self.infiniteScrollingView.frame = CGRectMake(0, self.contentSize.height, self.infiniteScrollingView.bounds.size.width, SVInfiniteScrollingViewHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)showsInfiniteScrolling {
|
||||
return !self.infiniteScrollingView.hidden;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark - SVInfiniteScrollingView
|
||||
@implementation SVInfiniteScrollingView
|
||||
|
||||
// public properties
|
||||
@synthesize infiniteScrollingHandler, activityIndicatorViewStyle;
|
||||
|
||||
@synthesize state = _state;
|
||||
@synthesize scrollView = _scrollView;
|
||||
@synthesize activityIndicatorView = _activityIndicatorView;
|
||||
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame {
|
||||
if(self = [super initWithFrame:frame]) {
|
||||
|
||||
// default styling values
|
||||
self.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
|
||||
self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
||||
self.state = SVInfiniteScrollingStateStopped;
|
||||
self.enabled = YES;
|
||||
|
||||
self.viewForState = [NSMutableArray arrayWithObjects:@"", @"", @"", @"", nil];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)willMoveToSuperview:(UIView *)newSuperview {
|
||||
if (self.superview && newSuperview == nil) {
|
||||
UIScrollView *scrollView = (UIScrollView *)self.superview;
|
||||
if (scrollView.showsInfiniteScrolling) {
|
||||
if (self.isObserving) {
|
||||
[scrollView removeObserver:self forKeyPath:@"contentOffset"];
|
||||
[scrollView removeObserver:self forKeyPath:@"contentSize"];
|
||||
self.isObserving = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
self.activityIndicatorView.center = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
|
||||
}
|
||||
|
||||
#pragma mark - Scroll View
|
||||
|
||||
- (void)resetScrollViewContentInset {
|
||||
UIEdgeInsets currentInsets = self.scrollView.contentInset;
|
||||
currentInsets.bottom = self.originalBottomInset;
|
||||
[self setScrollViewContentInset:currentInsets];
|
||||
}
|
||||
|
||||
- (void)setScrollViewContentInsetForInfiniteScrolling {
|
||||
UIEdgeInsets currentInsets = self.scrollView.contentInset;
|
||||
currentInsets.bottom = self.originalBottomInset + SVInfiniteScrollingViewHeight;
|
||||
[self setScrollViewContentInset:currentInsets];
|
||||
}
|
||||
|
||||
- (void)setScrollViewContentInset:(UIEdgeInsets)contentInset {
|
||||
[UIView animateWithDuration:0.3
|
||||
delay:0
|
||||
options:UIViewAnimationOptionAllowUserInteraction|UIViewAnimationOptionBeginFromCurrentState
|
||||
animations:^{
|
||||
self.scrollView.contentInset = contentInset;
|
||||
}
|
||||
completion:NULL];
|
||||
}
|
||||
|
||||
#pragma mark - Observing
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
|
||||
if([keyPath isEqualToString:@"contentOffset"])
|
||||
[self scrollViewDidScroll:[[change valueForKey:NSKeyValueChangeNewKey] CGPointValue]];
|
||||
else if([keyPath isEqualToString:@"contentSize"]) {
|
||||
[self layoutSubviews];
|
||||
self.frame = CGRectMake(0, self.scrollView.contentSize.height, self.bounds.size.width, SVInfiniteScrollingViewHeight);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollViewDidScroll:(CGPoint)contentOffset {
|
||||
if(self.state != SVInfiniteScrollingStateLoading && self.enabled) {
|
||||
CGFloat scrollViewContentHeight = self.scrollView.contentSize.height;
|
||||
CGFloat scrollOffsetThreshold = scrollViewContentHeight-self.scrollView.bounds.size.height;
|
||||
|
||||
if(!self.scrollView.isDragging && self.state == SVInfiniteScrollingStateTriggered)
|
||||
self.state = SVInfiniteScrollingStateLoading;
|
||||
else if(contentOffset.y > scrollOffsetThreshold && self.state == SVInfiniteScrollingStateStopped && self.scrollView.isDragging)
|
||||
self.state = SVInfiniteScrollingStateTriggered;
|
||||
else if(contentOffset.y < scrollOffsetThreshold && self.state != SVInfiniteScrollingStateStopped)
|
||||
self.state = SVInfiniteScrollingStateStopped;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Getters
|
||||
|
||||
- (UIActivityIndicatorView *)activityIndicatorView {
|
||||
if(!_activityIndicatorView) {
|
||||
_activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
|
||||
_activityIndicatorView.hidesWhenStopped = YES;
|
||||
[self addSubview:_activityIndicatorView];
|
||||
}
|
||||
return _activityIndicatorView;
|
||||
}
|
||||
|
||||
- (UIActivityIndicatorViewStyle)activityIndicatorViewStyle {
|
||||
return self.activityIndicatorView.activityIndicatorViewStyle;
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
- (void)setCustomView:(UIView *)view forState:(SVInfiniteScrollingState)state {
|
||||
id viewPlaceholder = view;
|
||||
|
||||
if(!viewPlaceholder)
|
||||
viewPlaceholder = @"";
|
||||
|
||||
if(state == SVInfiniteScrollingStateAll)
|
||||
[self.viewForState replaceObjectsInRange:NSMakeRange(0, 3) withObjectsFromArray:@[viewPlaceholder, viewPlaceholder, viewPlaceholder]];
|
||||
else
|
||||
[self.viewForState replaceObjectAtIndex:state withObject:viewPlaceholder];
|
||||
|
||||
self.state = self.state;
|
||||
}
|
||||
|
||||
- (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)viewStyle {
|
||||
self.activityIndicatorView.activityIndicatorViewStyle = viewStyle;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)triggerRefresh {
|
||||
self.state = SVInfiniteScrollingStateTriggered;
|
||||
self.state = SVInfiniteScrollingStateLoading;
|
||||
}
|
||||
|
||||
- (void)startAnimating{
|
||||
self.state = SVInfiniteScrollingStateLoading;
|
||||
}
|
||||
|
||||
- (void)stopAnimating {
|
||||
self.state = SVInfiniteScrollingStateStopped;
|
||||
}
|
||||
|
||||
- (void)setState:(SVInfiniteScrollingState)newState {
|
||||
|
||||
if(_state == newState)
|
||||
return;
|
||||
|
||||
SVInfiniteScrollingState previousState = _state;
|
||||
_state = newState;
|
||||
|
||||
for(id otherView in self.viewForState) {
|
||||
if([otherView isKindOfClass:[UIView class]])
|
||||
[otherView removeFromSuperview];
|
||||
}
|
||||
|
||||
id customView = [self.viewForState objectAtIndex:newState];
|
||||
BOOL hasCustomView = [customView isKindOfClass:[UIView class]];
|
||||
|
||||
if(hasCustomView) {
|
||||
[self addSubview:customView];
|
||||
CGRect viewBounds = [customView bounds];
|
||||
CGPoint origin = CGPointMake(roundf((self.bounds.size.width-viewBounds.size.width)/2), roundf((self.bounds.size.height-viewBounds.size.height)/2));
|
||||
[customView setFrame:CGRectMake(origin.x, origin.y, viewBounds.size.width, viewBounds.size.height)];
|
||||
}
|
||||
else {
|
||||
CGRect viewBounds = [self.activityIndicatorView bounds];
|
||||
CGPoint origin = CGPointMake(roundf((self.bounds.size.width-viewBounds.size.width)/2), roundf((self.bounds.size.height-viewBounds.size.height)/2));
|
||||
[self.activityIndicatorView setFrame:CGRectMake(origin.x, origin.y, viewBounds.size.width, viewBounds.size.height)];
|
||||
|
||||
switch (newState) {
|
||||
case SVInfiniteScrollingStateStopped:
|
||||
[self.activityIndicatorView stopAnimating];
|
||||
break;
|
||||
|
||||
case SVInfiniteScrollingStateTriggered:
|
||||
[self.activityIndicatorView startAnimating];
|
||||
break;
|
||||
|
||||
case SVInfiniteScrollingStateLoading:
|
||||
[self.activityIndicatorView startAnimating];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(previousState == SVInfiniteScrollingStateTriggered && newState == SVInfiniteScrollingStateLoading && self.infiniteScrollingHandler && self.enabled)
|
||||
self.infiniteScrollingHandler();
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user