Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

added support for bottom pull to refresh view

  • Loading branch information...
commit 2a33be50f3b7d46f6519d4b187dfde3dc4c6c0c9 1 parent 410f90a
@chrispix chrispix authored
View
5 PullToRefreshView.h
@@ -57,13 +57,16 @@ typedef enum {
@property (nonatomic, retain) UILabel *statusLabel;
@property (nonatomic, retain) CALayer *arrowImage;
@property (nonatomic, retain) UIActivityIndicatorView *activityView;
+@property (assign, readonly) BOOL isBottom;
- (void)refreshLastUpdatedDate;
- (void)finishedLoading;
-- (id)initWithScrollView:(UIScrollView *)scrollView;
+- (id)initWithScrollView:(UIScrollView *)scroll;
- (id)initWithWebView:(UIWebView *)webView;
+- (id)initWithScrollView:(UIScrollView *)scroll atBottom:(BOOL)isBottom;
+- (id)initWithWebView:(UIWebView *)webView atBottom:(BOOL)isBottom;
@end
View
106 PullToRefreshView.m
@@ -38,6 +38,10 @@ @interface PullToRefreshView (Private)
- (void)startTimer;
- (void)dismissView;
+- (BOOL)isScrolledToVisible;
+- (BOOL)isScrolledToLimit;
+- (void)parkVisible;
+- (void)hide;
@end
@@ -46,6 +50,10 @@ @implementation PullToRefreshView
@synthesize scrollView;
@synthesize lastUpdatedLabel, statusLabel, arrowImage, activityView;
@synthesize timeout;
+@synthesize isBottom;
+
+static const CGFloat kViewHeight = 60.0f;
+static const CGFloat kScrollLimit = 65.0f;
- (void)showActivity:(BOOL)shouldShow animated:(BOOL)animated {
if (shouldShow) [self.activityView startAnimating];
@@ -65,16 +73,27 @@ - (void)setImageFlipped:(BOOL)flipped {
}
- (id)initWithScrollView:(UIScrollView *)scroll {
- CGRect frame = CGRectMake(0.0f, 0.0f - scroll.bounds.size.height, scroll.bounds.size.width, scroll.bounds.size.height);
+ return [self initWithScrollView:scroll atBottom:NO];
+}
+
+- (id)initWithWebView:(UIWebView *)webView {
+ return [self initWithWebView:webView atBottom:NO];
+}
+
+- (id)initWithScrollView:(UIScrollView *)scroll atBottom:(BOOL)atBottom {
+ CGFloat offset = atBottom ? scroll.contentSize.height : 0.0f - scroll.bounds.size.height;
+ CGRect frame = CGRectMake(0.0f, offset, scroll.bounds.size.width, scroll.bounds.size.height);
if ((self = [super initWithFrame:frame])) {
+ CGFloat visibleBottom = atBottom ? kViewHeight : self.frame.size.height;
+ isBottom = atBottom;
self.scrollView = scroll;
[scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:NULL];
self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
self.backgroundColor = [UIColor colorWithRed:226.0/255.0 green:231.0/255.0 blue:237.0/255.0 alpha:1.0];
- self.lastUpdatedLabel = [[[UILabel alloc] initWithFrame:CGRectMake(0.0f, frame.size.height - 30.0f, self.frame.size.width, 20.0f)] autorelease];
+ self.lastUpdatedLabel = [[[UILabel alloc] initWithFrame:CGRectMake(0.0f, visibleBottom - 30.0f, self.frame.size.width, 20.0f)] autorelease];
self.lastUpdatedLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
self.lastUpdatedLabel.font = [UIFont systemFontOfSize:12.0f];
self.lastUpdatedLabel.textColor = TEXT_COLOR;
@@ -84,7 +103,7 @@ - (id)initWithScrollView:(UIScrollView *)scroll {
self.lastUpdatedLabel.textAlignment = UITextAlignmentCenter;
[self addSubview:self.lastUpdatedLabel];
- self.statusLabel = [[[UILabel alloc] initWithFrame:CGRectMake(0.0f, frame.size.height - 48.0f, self.frame.size.width, 20.0f)] autorelease];
+ self.statusLabel = [[[UILabel alloc] initWithFrame:CGRectMake(0.0f, visibleBottom - 48.0f, self.frame.size.width, 20.0f)] autorelease];
self.statusLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
self.statusLabel.font = [UIFont boldSystemFontOfSize:13.0f];
self.statusLabel.textColor = TEXT_COLOR;
@@ -95,9 +114,10 @@ - (id)initWithScrollView:(UIScrollView *)scroll {
[self addSubview:self.statusLabel];
self.arrowImage = [[[CALayer alloc] init] autorelease];
- self.arrowImage.frame = CGRectMake(25.0f, frame.size.height - 60.0f, 24.0f, 52.0f);
+ UIImage *arrow = [UIImage imageNamed:@"arrow"];
+ self.arrowImage.contents = (id) arrow.CGImage;
+ self.arrowImage.frame = CGRectMake(25.0f, visibleBottom - kViewHeight + 5.0f, arrow.size.width, arrow.size.height);
self.arrowImage.contentsGravity = kCAGravityResizeAspect;
- self.arrowImage.contents = (id) [UIImage imageNamed:@"arrow"].CGImage;
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
@@ -108,16 +128,16 @@ - (id)initWithScrollView:(UIScrollView *)scroll {
[self.layer addSublayer:self.arrowImage];
self.activityView = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray] autorelease];
- self.activityView.frame = CGRectMake(30.0f, frame.size.height - 38.0f, 20.0f, 20.0f);
+ self.activityView.frame = CGRectMake(30.0f, visibleBottom - 38.0f, 20.0f, 20.0f);
[self addSubview:self.activityView];
[self setState:PullToRefreshViewStateNormal];
}
- return self;
+ return self;
}
-- (id)initWithWebView:(UIWebView *)webView {
+- (id)initWithWebView:(UIWebView *)webView atBottom:(BOOL)atBottom {
UIScrollView *currentScrollView = nil;
for (UIView *subView in webView.subviews) {
if ([subView isKindOfClass:[UIScrollView class]]) {
@@ -125,7 +145,7 @@ - (id)initWithWebView:(UIWebView *)webView {
break;
}
}
- return [self initWithScrollView:currentScrollView];
+ return [self initWithScrollView:currentScrollView atBottom:atBottom];
}
#pragma mark -
@@ -153,22 +173,20 @@ - (void)setState:(PullToRefreshViewState)state_ {
self.statusLabel.text = @"Release to refresh...";
[self showActivity:NO animated:NO];
[self setImageFlipped:YES];
- scrollView.contentInset = UIEdgeInsetsZero;
break;
case PullToRefreshViewStateNormal:
- self.statusLabel.text = @"Pull down to refresh...";
+ self.statusLabel.text = [NSString stringWithFormat:@"Pull %@ to refresh...", isBottom ? @"up" : @"down"];
[self showActivity:NO animated:NO];
[self setImageFlipped:NO];
[self refreshLastUpdatedDate];
- scrollView.contentInset = UIEdgeInsetsZero;
break;
case PullToRefreshViewStateLoading:
self.statusLabel.text = @"Loading...";
[self showActivity:YES animated:YES];
[self setImageFlipped:NO];
- scrollView.contentInset = UIEdgeInsetsMake(60.0f, 0.0f, 0.0f, 0.0f);
+ [self parkVisible];
[self startTimer];
break;
@@ -180,20 +198,69 @@ - (void)setState:(PullToRefreshViewState)state_ {
#pragma mark -
#pragma mark UIScrollView
+- (BOOL)isScrolledToVisible {
+ if (isBottom) {
+ BOOL scrolledBelowContent = scrollView.contentOffset.y > (scrollView.contentSize.height - scrollView.frame.size.height);
+ return scrolledBelowContent && ![self isScrolledToLimit];
+ } else {
+ BOOL scrolledAboveContent = scrollView.contentOffset.y < 0.0f;
+ return scrolledAboveContent && ![self isScrolledToLimit];
+ }
+}
+
+- (BOOL)isScrolledToLimit {
+ if (isBottom) {
+ return scrollView.contentOffset.y >= (scrollView.contentSize.height - scrollView.frame.size.height) + kScrollLimit;
+ } else {
+ return scrollView.contentOffset.y <= -kScrollLimit;
+ }
+}
+
+- (void)parkVisible {
+ if (isBottom) {
+ scrollView.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, kViewHeight, 0.0f);
+ } else {
+ scrollView.contentInset = UIEdgeInsetsMake(kViewHeight, 0.0f, 0.0f, 0.0f);
+ }
+}
+
+- (void)hide {
+ if (isBottom) {
+ scrollView.contentInset = UIEdgeInsetsMake(scrollView.contentInset.top, 0.0f, 0.0f, 0.0f);
+ } else {
+ scrollView.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, scrollView.contentInset.bottom, 0.0f);
+ }
+}
+
+- (void)handleDragWhileLoading {
+ if ([self isScrolledToLimit] || [self isScrolledToVisible]) {
+ // allow scrolled portion of view to display
+ if (isBottom) {
+ CGFloat visiblePortion = scrollView.contentOffset.y - (scrollView.contentSize.height - scrollView.frame.size.height);
+ scrollView.contentInset = UIEdgeInsetsMake(0, 0, MIN(visiblePortion, kViewHeight), 0);
+ } else {
+ scrollView.contentInset = UIEdgeInsetsMake(MIN(-scrollView.contentOffset.y, kViewHeight), 0, 0, 0);
+ }
+ }
+}
+
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:@"contentOffset"]) {
if (scrollView.isDragging) {
if (state == PullToRefreshViewStateReady) {
- if (scrollView.contentOffset.y > -65.0f && scrollView.contentOffset.y < 0.0f)
+ if ([self isScrolledToVisible]) {
+ NSLog(@"scrolled to visible: %@", isBottom ? @"bottom" : @"top");
+ // dragging from "release to refresh" back down (didn't release at top)
[self setState:PullToRefreshViewStateNormal];
+ }
} else if (state == PullToRefreshViewStateNormal) {
- if (scrollView.contentOffset.y < -65.0f)
+ // hit the upper limit, change to "release to refresh"
+ if ([self isScrolledToLimit]) {
+ NSLog(@"scrolled to limit: %@", isBottom ? @"bottom" : @"top");
[self setState:PullToRefreshViewStateReady];
+ }
} else if (state == PullToRefreshViewStateLoading) {
- if (scrollView.contentOffset.y >= 0)
- scrollView.contentInset = UIEdgeInsetsZero;
- else
- scrollView.contentInset = UIEdgeInsetsMake(MIN(-scrollView.contentOffset.y, 60.0f), 0, 0, 0);
+ [self handleDragWhileLoading];
}
} else {
if (state == PullToRefreshViewStateReady) {
@@ -220,6 +287,7 @@ - (void)dismissView {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3f];
[self setState:PullToRefreshViewStateNormal];
+ [self hide];
[UIView commitAnimations];
}
View
2  PullToRefreshView/PullToRefreshView/ViewController.h
@@ -16,5 +16,7 @@
@property(assign)PullToRefreshView *bottomPull;
-(IBAction)didTapFinishedLoading:(id)sender;
+- (IBAction)didTapFinishLoadingTop:(id)sender;
+- (IBAction)didTapFinishLoadingBottom:(id)sender;
@end
View
22 PullToRefreshView/PullToRefreshView/ViewController.m
@@ -15,17 +15,17 @@ @implementation ViewController
-(void)viewDidLoad {
[super viewDidLoad];
+
+ // make area scrollable
+ self.scrollView.contentSize = CGSizeMake(320, 540);
+
self.topPull = [[PullToRefreshView alloc] initWithScrollView:self.scrollView];
[self.topPull setDelegate:self];
[self.scrollView addSubview:self.topPull];
-
- self.bottomPull = [[PullToRefreshView alloc] initWithScrollView:self.scrollView];
- self.bottomPull.isBottom = YES;
+
+ self.bottomPull = [[PullToRefreshView alloc] initWithScrollView:self.scrollView atBottom:YES];
[self.bottomPull setDelegate:self];
- [self.scrollView addSubview:self.bottomPull];
-
- // make area scrollable
- self.scrollView.contentSize = CGSizeMake(320, 481);
+ [self.scrollView addSubview:self.bottomPull];
}
-(void)viewDidUnload {
@@ -38,6 +38,14 @@ -(IBAction)didTapFinishedLoading:(id)sender {
[self.bottomPull finishedLoading];
}
+-(IBAction)didTapFinishLoadingTop:(id)sender {
+ [self.topPull finishedLoading];
+}
+
+-(IBAction)didTapFinishLoadingBottom:(id)sender {
+ [self.bottomPull finishedLoading];
+}
+
#pragma mark -
#pragma mark PullToRefreshViewDelegate
View
99 PullToRefreshView/PullToRefreshView/en.lproj/ViewController.xib
@@ -46,7 +46,7 @@
<int key="NSvFlags">292</int>
<string key="NSFrame">{{84, 35}, {153, 21}}</string>
<reference key="NSSuperview" ref="1016583224"/>
- <reference key="NSNextKeyView" ref="4623693"/>
+ <reference key="NSNextKeyView" ref="928954653"/>
<string key="NSReuseIdentifierKey">_NS:328</string>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClipsSubviews">YES</bool>
@@ -94,17 +94,17 @@
<object class="IBUIButton" id="4623693">
<reference key="NSNextResponder" ref="1016583224"/>
<int key="NSvFlags">292</int>
- <string key="NSFrame">{{75, 211}, {171, 37}}</string>
+ <string key="NSFrame">{{57, 211}, {208, 37}}</string>
<reference key="NSSuperview" ref="1016583224"/>
- <reference key="NSNextKeyView" ref="802845534"/>
+ <reference key="NSNextKeyView" ref="77952817"/>
<string key="NSReuseIdentifierKey">_NS:225</string>
<bool key="IBUIOpaque">NO</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<int key="IBUIContentHorizontalAlignment">0</int>
<int key="IBUIContentVerticalAlignment">0</int>
<int key="IBUIButtonType">1</int>
- <string key="IBUINormalTitle">Tap to finish loading</string>
- <object class="NSColor" key="IBUIHighlightedTitleColor" id="653277802">
+ <string key="IBUINormalTitle">Tap to finish loading both</string>
+ <object class="NSColor" key="IBUIHighlightedTitleColor" id="905454287">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
</object>
@@ -112,26 +112,73 @@
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA</bytes>
</object>
- <object class="NSColor" key="IBUINormalTitleShadowColor">
+ <object class="NSColor" key="IBUINormalTitleShadowColor" id="157889243">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MC41AA</bytes>
</object>
- <object class="IBUIFontDescription" key="IBUIFontDescription">
+ <object class="IBUIFontDescription" key="IBUIFontDescription" id="680852631">
<int key="type">2</int>
<double key="pointSize">15</double>
</object>
- <object class="NSFont" key="IBUIFont">
+ <object class="NSFont" key="IBUIFont" id="227969696">
<string key="NSName">Helvetica-Bold</string>
<double key="NSSize">15</double>
<int key="NSfFlags">16</int>
</object>
</object>
+ <object class="IBUIButton" id="928954653">
+ <reference key="NSNextResponder" ref="1016583224"/>
+ <int key="NSvFlags">292</int>
+ <string key="NSFrame">{{61, 76}, {199, 37}}</string>
+ <reference key="NSSuperview" ref="1016583224"/>
+ <reference key="NSNextKeyView" ref="4623693"/>
+ <string key="NSReuseIdentifierKey">_NS:225</string>
+ <bool key="IBUIOpaque">NO</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <int key="IBUIContentHorizontalAlignment">0</int>
+ <int key="IBUIContentVerticalAlignment">0</int>
+ <int key="IBUIButtonType">1</int>
+ <string key="IBUINormalTitle">Tap to finish loading top</string>
+ <reference key="IBUIHighlightedTitleColor" ref="905454287"/>
+ <object class="NSColor" key="IBUINormalTitleColor">
+ <int key="NSColorSpace">1</int>
+ <bytes key="NSRGB">MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA</bytes>
+ </object>
+ <reference key="IBUINormalTitleShadowColor" ref="157889243"/>
+ <reference key="IBUIFontDescription" ref="680852631"/>
+ <reference key="IBUIFont" ref="227969696"/>
+ </object>
+ <object class="IBUIButton" id="77952817">
+ <reference key="NSNextResponder" ref="1016583224"/>
+ <int key="NSvFlags">292</int>
+ <string key="NSFrame">{{47, 349}, {226, 37}}</string>
+ <reference key="NSSuperview" ref="1016583224"/>
+ <reference key="NSNextKeyView" ref="802845534"/>
+ <string key="NSReuseIdentifierKey">_NS:225</string>
+ <bool key="IBUIOpaque">NO</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <int key="IBUIContentHorizontalAlignment">0</int>
+ <int key="IBUIContentVerticalAlignment">0</int>
+ <int key="IBUIButtonType">1</int>
+ <string key="IBUINormalTitle">Tap to finish loading bottom</string>
+ <reference key="IBUIHighlightedTitleColor" ref="905454287"/>
+ <object class="NSColor" key="IBUINormalTitleColor">
+ <int key="NSColorSpace">1</int>
+ <bytes key="NSRGB">MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA</bytes>
+ </object>
+ <reference key="IBUINormalTitleShadowColor" ref="157889243"/>
+ <reference key="IBUIFontDescription" ref="680852631"/>
+ <reference key="IBUIFont" ref="227969696"/>
+ </object>
</array>
<string key="NSFrameSize">{320, 460}</string>
<reference key="NSSuperview" ref="774585933"/>
<reference key="NSNextKeyView" ref="982200938"/>
<string key="NSReuseIdentifierKey">_NS:190</string>
- <reference key="IBUIBackgroundColor" ref="653277802"/>
+ <object class="NSColor" key="IBUIBackgroundColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MCAwAA</bytes>
+ </object>
<bool key="IBUIClipsSubviews">YES</bool>
<bool key="IBUIMultipleTouchEnabled">YES</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
@@ -179,6 +226,24 @@
</object>
<int key="connectionID">13</int>
</object>
+ <object class="IBConnectionRecord">
+ <object class="IBCocoaTouchEventConnection" key="connection">
+ <string key="label">didTapFinishLoadingTop:</string>
+ <reference key="source" ref="928954653"/>
+ <reference key="destination" ref="372490531"/>
+ <int key="IBEventType">7</int>
+ </object>
+ <int key="connectionID">16</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBCocoaTouchEventConnection" key="connection">
+ <string key="label">didTapFinishLoadingBottom:</string>
+ <reference key="source" ref="77952817"/>
+ <reference key="destination" ref="372490531"/>
+ <int key="IBEventType">7</int>
+ </object>
+ <int key="connectionID">17</int>
+ </object>
</array>
<object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects">
@@ -214,6 +279,8 @@
<reference ref="982200938"/>
<reference ref="802845534"/>
<reference ref="4623693"/>
+ <reference ref="928954653"/>
+ <reference ref="77952817"/>
</array>
<reference key="parent" ref="774585933"/>
</object>
@@ -232,6 +299,16 @@
<reference key="object" ref="4623693"/>
<reference key="parent" ref="1016583224"/>
</object>
+ <object class="IBObjectRecord">
+ <int key="objectID">14</int>
+ <reference key="object" ref="928954653"/>
+ <reference key="parent" ref="1016583224"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">15</int>
+ <reference key="object" ref="77952817"/>
+ <reference key="parent" ref="1016583224"/>
+ </object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
@@ -241,6 +318,8 @@
<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="10.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="12.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string key="14.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string key="15.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="6.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="8.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="9.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
@@ -249,7 +328,7 @@
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
- <int key="maxID">13</int>
+ <int key="maxID">17</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes"/>
<int key="IBDocument.localizationMode">0</int>
Please sign in to comment.
Something went wrong with that request. Please try again.