Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions CBStoreHouseRefreshControl/ContentViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,12 @@ - (void)viewDidLoad {
self.navigationController.navigationBar.tintColor = [UIColor whiteColor];
self.navigationController.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName : [UIColor whiteColor]};

self.tableView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"bg_pattern"]];
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.backgroundColor = [UIColor colorWithWhite:45.f/255.f alpha:1];

[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
self.tableView.alwaysBounceVertical = YES;

UIView *footer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, self.tableView.bounds.size.height)];
self.tableView.tableFooterView = footer;

// Let the show begins
self.storeHouseRefreshControl = [CBStoreHouseRefreshControl attachToScrollView:self.tableView target:self refreshAction:@selector(refreshTriggered:) plist:@"storehouse" color:[UIColor whiteColor] lineWidth:1.5 dropHeight:80 scale:1 horizontalRandomness:150 reverseLoadingAnimation:YES internalAnimationFactor:0.5];
self.storeHouseRefreshControl = [CBStoreHouseRefreshControl attachToScrollView:self.tableView target:self refreshAction:@selector(refreshTriggered:) plist:@"storehouse" color:[UIColor blackColor] lineWidth:1.5 dropHeight:60 scale:0.8 horizontalRandomness:150 reverseLoadingAnimation:YES internalAnimationFactor:0.5 invert:NO];

//self.storeHouseRefreshControl = [CBStoreHouseRefreshControl attachToScrollView:self.tableView target:self refreshAction:@selector(refreshTriggered:) plist:@"AKTA" color:[UIColor whiteColor] lineWidth:2 dropHeight:80 scale:0.7 horizontalRandomness:300 reverseLoadingAnimation:NO internalAnimationFactor:0.7];
}
Expand All @@ -59,7 +53,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1;
return 2;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
Expand Down
15 changes: 8 additions & 7 deletions Class/BarItem.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,18 @@ - (instancetype)initWithFrame:(CGRect)frame startPoint:(CGPoint)startPoint endPo
{
self = [super initWithFrame:frame];
if (self) {
_startPoint = startPoint;
_endPoint = endPoint;
_lineWidth = lineWidth;
_color = color;
self.startPoint = startPoint;
self.endPoint = endPoint;
self.lineWidth = lineWidth;
self.color = color;
self.backgroundColor = [UIColor clearColor];

CGPoint (^middlePoint)(CGPoint, CGPoint) = ^CGPoint(CGPoint a, CGPoint b) {
CGFloat x = (a.x + b.x)/2.f;
CGFloat y = (a.y + b.y)/2.f;
return CGPointMake(x, y);
};
_middlePoint = middlePoint(startPoint, endPoint);
self.middlePoint = middlePoint(startPoint, endPoint);
}
return self;
}
Expand All @@ -54,10 +55,10 @@ - (void)setHorizontalRandomness:(int)horizontalRandomness dropHeight:(CGFloat)dr

- (void)drawRect:(CGRect)rect {
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
[bezierPath moveToPoint:self.startPoint];
[bezierPath addLineToPoint:self.endPoint];
[self.color setStroke];
bezierPath.lineWidth = self.lineWidth;
[bezierPath moveToPoint:self.startPoint];
[bezierPath addLineToPoint:self.endPoint];
[bezierPath stroke];
}

Expand Down
3 changes: 2 additions & 1 deletion Class/CBStoreHouseRefreshControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
scale:(CGFloat)scale
horizontalRandomness:(CGFloat)horizontalRandomness
reverseLoadingAnimation:(BOOL)reverseLoadingAnimation
internalAnimationFactor:(CGFloat)internalAnimationFactor;
internalAnimationFactor:(CGFloat)internalAnimationFactor
invert:(BOOL)invert;

- (void)scrollViewDidScroll;

Expand Down
120 changes: 87 additions & 33 deletions Class/CBStoreHouseRefreshControl.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
static const CGFloat kloadingIndividualAnimationTiming = 0.8;
static const CGFloat kbarDarkAlpha = 0.4;
static const CGFloat kloadingTimingOffset = 0.1;
static const CGFloat kdisappearDuration = 1.2;
static const CGFloat krelativeHeightFactor = 2.f/5.f;
static const CGFloat kDuration = 0.5f;
static const CGFloat krelativeHeightFactor = 0.5f;

typedef enum {
CBStoreHouseRefreshControlStateIdle = 0,
Expand All @@ -29,27 +29,27 @@
@interface CBStoreHouseRefreshControl () <UIScrollViewDelegate>

@property (nonatomic) CBStoreHouseRefreshControlState state;
@property (nonatomic, weak) UIScrollView *scrollView;
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) NSArray *barItems;
@property (nonatomic, strong) CADisplayLink *displayLink;
@property (nonatomic, assign) id target;
@property (nonatomic) SEL action;

@property (nonatomic) CGFloat dropHeight;
@property (nonatomic) CGFloat originalTopContentInset;
@property (nonatomic) CGFloat disappearProgress;
@property (nonatomic) CGFloat internalAnimationFactor;
@property (nonatomic) int horizontalRandomness;
@property (nonatomic) BOOL reverseLoadingAnimation;
@property (nonatomic) BOOL invert;

@end

@implementation CBStoreHouseRefreshControl

+ (CBStoreHouseRefreshControl*)attachToScrollView:(UIScrollView *)scrollView
target:(id)target
refreshAction:(SEL)refreshAction
plist:(NSString *)plist
target:(id)target
refreshAction:(SEL)refreshAction
plist:(NSString *)plist
{
return [CBStoreHouseRefreshControl attachToScrollView:scrollView
target:target
Expand All @@ -61,7 +61,8 @@ + (CBStoreHouseRefreshControl*)attachToScrollView:(UIScrollView *)scrollView
scale:1
horizontalRandomness:150
reverseLoadingAnimation:NO
internalAnimationFactor:0.7];
internalAnimationFactor:0.7
invert:NO];
}

+ (CBStoreHouseRefreshControl*)attachToScrollView:(UIScrollView *)scrollView
Expand All @@ -75,6 +76,7 @@ + (CBStoreHouseRefreshControl*)attachToScrollView:(UIScrollView *)scrollView
horizontalRandomness:(CGFloat)horizontalRandomness
reverseLoadingAnimation:(BOOL)reverseLoadingAnimation
internalAnimationFactor:(CGFloat)internalAnimationFactor
invert:(BOOL)invert
{
CBStoreHouseRefreshControl *refreshControl = [[CBStoreHouseRefreshControl alloc] init];
refreshControl.dropHeight = dropHeight;
Expand All @@ -86,6 +88,9 @@ + (CBStoreHouseRefreshControl*)attachToScrollView:(UIScrollView *)scrollView
refreshControl.internalAnimationFactor = internalAnimationFactor;
[scrollView addSubview:refreshControl];

refreshControl.invert = invert;


// Calculate frame according to points max width and height
CGFloat width = 0;
CGFloat height = 0;
Expand All @@ -103,17 +108,16 @@ + (CBStoreHouseRefreshControl*)attachToScrollView:(UIScrollView *)scrollView
if (endPoint.y > height) height = endPoint.y;
}
refreshControl.frame = CGRectMake(0, 0, width, height);

// Create bar items
NSMutableArray *mutableBarItems = [[NSMutableArray alloc] init];
for (int i=0; i<startPoints.count; i++) {

CGPoint startPoint = CGPointFromString(startPoints[i]);
CGPoint endPoint = CGPointFromString(endPoints[i]);

BarItem *barItem = [[BarItem alloc] initWithFrame:refreshControl.frame startPoint:startPoint endPoint:endPoint color:color lineWidth:lineWidth];
barItem.tag = i;
barItem.backgroundColor=[UIColor clearColor];
barItem.alpha = 0;
[mutableBarItems addObject:barItem];
[refreshControl addSubview:barItem];
Expand All @@ -123,49 +127,86 @@ + (CBStoreHouseRefreshControl*)attachToScrollView:(UIScrollView *)scrollView

refreshControl.barItems = [NSArray arrayWithArray:mutableBarItems];
refreshControl.frame = CGRectMake(0, 0, width, height);
refreshControl.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2, 0);
if (refreshControl.invert) {
refreshControl.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2, refreshControl.scrollView.contentSize.height+refreshControl.dropHeight*krelativeHeightFactor);
} else {
refreshControl.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2, refreshControl.realContentOffsetY*krelativeHeightFactor);
}
for (BarItem *barItem in refreshControl.barItems) {
[barItem setupWithFrame:refreshControl.frame];
}

refreshControl.transform = CGAffineTransformMakeScale(scale, scale);

if(refreshControl.invert) {
refreshControl.transform = CGAffineTransformMake(1, 0, 0, -1, 0, 0);
}
refreshControl.transform = CGAffineTransformScale(refreshControl.transform, scale, scale);
return refreshControl;
}

#pragma mark UIScrollViewDelegate

- (void)scrollViewDidScroll
{
if (self.originalTopContentInset == 0) self.originalTopContentInset = self.scrollView.contentInset.top;
self.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2, self.realContentOffsetY*krelativeHeightFactor);
// printf("%f\n",[self bottomDrop]);
if (self.state == CBStoreHouseRefreshControlStateRefreshing) {
if (self.invert) {
self.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2, self.scrollView.contentSize.height+[self bottomDrop]*krelativeHeightFactor);
} else {
self.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2, (self.realContentOffsetY-self.dropHeight)*krelativeHeightFactor);
}
} else {
if (self.invert) {
self.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2, self.scrollView.contentSize.height+[self bottomDrop]*krelativeHeightFactor);
} else {
self.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2, self.realContentOffsetY*krelativeHeightFactor);
}

}
if (self.state == CBStoreHouseRefreshControlStateIdle)
[self updateBarItemsWithProgress:self.animationProgress];
}

- (CGFloat)bottomDrop {
return (self.realContentOffsetY + self.scrollView.frame.size.height - self.scrollView.contentSize.height-self.scrollView.contentInset.top);
}

- (void)scrollViewDidEndDragging
{
if (self.state == CBStoreHouseRefreshControlStateIdle && self.realContentOffsetY < -self.dropHeight) {

if (self.animationProgress == 1) self.state = CBStoreHouseRefreshControlStateRefreshing;
if (self.state == CBStoreHouseRefreshControlStateIdle) {
if(self.invert == NO && self.realContentOffsetY < -self.dropHeight) {
if (self.animationProgress == 1) self.state = CBStoreHouseRefreshControlStateRefreshing;
}
if(self.invert == YES && [self bottomDrop] > self.dropHeight) {
if (self.animationProgress == 1) self.state = CBStoreHouseRefreshControlStateRefreshing;
}

if (self.state == CBStoreHouseRefreshControlStateRefreshing) {

UIEdgeInsets newInsets = self.scrollView.contentInset;
newInsets.top = self.originalTopContentInset + self.dropHeight;
CGPoint contentOffset = self.scrollView.contentOffset;
UIEdgeInsets curInset = self.scrollView.contentInset;
if(self.invert) {
newInsets.bottom += self.dropHeight;
curInset.bottom += [self bottomDrop];
} else {
newInsets.top += self.dropHeight;
}

[UIView animateWithDuration:0 animations:^(void) {
self.scrollView.contentInset = curInset;
[self.scrollView setNeedsLayout];
[UIView animateWithDuration:kDuration animations:^{
self.scrollView.bounces = NO;
self.scrollView.contentInset = newInsets;
self.scrollView.contentOffset = contentOffset;
} completion:^(BOOL finished) {
self.scrollView.bounces = YES;
}];

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"

if ([self.target respondsToSelector:self.action])
[self.target performSelector:self.action withObject:self];

#pragma clang diagnostic pop
#pragma clang diagnostic pop

[self startLoadingAnimation];
}
Expand All @@ -176,12 +217,15 @@ - (void)scrollViewDidEndDragging

- (CGFloat)animationProgress
{
if(self.invert) {
return MIN(1.f, MAX(0, fabsf(self.scrollView.contentSize.height - (self.realContentOffsetY - self.scrollView.contentInset.top) - self.scrollView.frame.size.height)/self.dropHeight));
}
return MIN(1.f, MAX(0, fabsf(self.realContentOffsetY)/self.dropHeight));
}

- (CGFloat)realContentOffsetY
{
return self.scrollView.contentOffset.y + self.originalTopContentInset;
return self.scrollView.contentOffset.y + self.scrollView.contentInset.top;
}

- (void)updateBarItemsWithProgress:(CGFloat)progress
Expand Down Expand Up @@ -245,7 +289,7 @@ - (void)barItemAnimation:(BarItem*)barItem
isLastOne = barItem.tag == 0;
else
isLastOne = barItem.tag == self.barItems.count-1;

if (isLastOne && self.state == CBStoreHouseRefreshControlStateRefreshing) {
[self startLoadingAnimation];
}
Expand All @@ -255,7 +299,7 @@ - (void)barItemAnimation:(BarItem*)barItem
- (void)updateDisappearAnimation
{
if (self.disappearProgress >= 0 && self.disappearProgress <= 1) {
self.disappearProgress -= 1/60.f/kdisappearDuration;
self.disappearProgress -= 1/60.f/kDuration;
//60.f means this method get called 60 times per second
[self updateBarItemsWithProgress:self.disappearProgress];
}
Expand All @@ -265,17 +309,27 @@ - (void)updateDisappearAnimation

- (void)finishingLoading
{
if (self.state != CBStoreHouseRefreshControlStateRefreshing) {
return;
}
self.state = CBStoreHouseRefreshControlStateDisappearing;
UIEdgeInsets newInsets = self.scrollView.contentInset;
newInsets.top = self.originalTopContentInset;
[UIView animateWithDuration:kdisappearDuration animations:^(void) {
[UIView animateWithDuration:kDuration animations:^(void) {
UIEdgeInsets newInsets = self.scrollView.contentInset;
if(self.invert) {
newInsets.bottom -= self.dropHeight;
} else {
newInsets.top -= self.dropHeight;
}

self.scrollView.bounces = NO;
self.scrollView.contentInset = newInsets;
} completion:^(BOOL finished) {
self.scrollView.bounces = YES;
self.state = CBStoreHouseRefreshControlStateIdle;
[self.displayLink invalidate];
self.disappearProgress = 1;
}];

for (BarItem *barItem in self.barItems) {
[barItem.layer removeAllAnimations];
barItem.alpha = kbarDarkAlpha;
Expand Down