// // PullRefreshTableViewController.m // Plancast // // Created by Leah Culver on 7/2/10. // Copyright (c) 2010 Leah Culver // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // #import #import "PullToRefresh.h" #define REFRESH_HEADER_HEIGHT 52.0f @implementation PullRefreshController @synthesize parentView, textPull, textRelease, textLoading, refreshHeaderView, refreshLabel, refreshArrow, refreshSpinner, refreshAction; - (id) initWhen:(UIScrollView*)parent pulledExecute:(void(^)())block textColor:(UIColor*)textColor { self = [super init]; if (self) { [self setupStrings]; self.refreshAction = block; self.parentView = parent; self.parentView.delegate = self; [self addPullToRefreshHeader:textColor]; } return self; } - (void)setupStrings{ textPull = [[NSString alloc] initWithString:@"Pull down to refresh..."]; textRelease = [[NSString alloc] initWithString:@"Release to refresh..."]; textLoading = [[NSString alloc] initWithString:@"Loading..."]; } - (void)addPullToRefreshHeader:(UIColor*)textColor { refreshHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0 - REFRESH_HEADER_HEIGHT, 320, REFRESH_HEADER_HEIGHT)]; refreshHeaderView.backgroundColor = [UIColor clearColor]; refreshLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, REFRESH_HEADER_HEIGHT)]; refreshLabel.backgroundColor = [UIColor clearColor]; refreshLabel.font = [UIFont boldSystemFontOfSize:12.0]; refreshLabel.textAlignment = UITextAlignmentCenter; refreshLabel.textColor = textColor; refreshArrow = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"arrow.png"]]; refreshArrow.frame = CGRectMake(floorf((REFRESH_HEADER_HEIGHT - 27) / 2), (floorf(REFRESH_HEADER_HEIGHT - 44) / 2), 27, 44); refreshSpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]; refreshSpinner.frame = CGRectMake(floorf(floorf(REFRESH_HEADER_HEIGHT - 20) / 2), floorf((REFRESH_HEADER_HEIGHT - 20) / 2), 20, 20); refreshSpinner.hidesWhenStopped = YES; [refreshHeaderView addSubview:refreshLabel]; [refreshHeaderView addSubview:refreshArrow]; [refreshHeaderView addSubview:refreshSpinner]; [self.parentView addSubview:refreshHeaderView]; } - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { if (isLoading) return; isDragging = YES; } - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (isLoading) { // Update the content inset, good for section headers if (scrollView.contentOffset.y > 0) self.parentView.contentInset = UIEdgeInsetsZero; else if (scrollView.contentOffset.y >= -REFRESH_HEADER_HEIGHT) self.parentView.contentInset = UIEdgeInsetsMake(-scrollView.contentOffset.y, 0, 0, 0); } else if (isDragging && scrollView.contentOffset.y < 0) { // Update the arrow direction and label [UIView beginAnimations:nil context:NULL]; if (scrollView.contentOffset.y < -REFRESH_HEADER_HEIGHT) { // User is scrolling above the header refreshLabel.text = self.textRelease; [refreshArrow layer].transform = CATransform3DMakeRotation(M_PI, 0, 0, 1); } else { // User is scrolling somewhere within the header refreshLabel.text = self.textPull; [refreshArrow layer].transform = CATransform3DMakeRotation(M_PI * 2, 0, 0, 1); } [UIView commitAnimations]; } } - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { if (isLoading) return; isDragging = NO; if (scrollView.contentOffset.y <= -REFRESH_HEADER_HEIGHT) { // Released above the header [self startLoading]; } } - (void)startLoading { isLoading = YES; // Show the header [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.3]; self.parentView.contentInset = UIEdgeInsetsMake(REFRESH_HEADER_HEIGHT, 0, 0, 0); refreshLabel.text = self.textLoading; refreshArrow.hidden = YES; [refreshSpinner startAnimating]; [UIView commitAnimations]; // Refresh action! refreshAction(); } - (void)stopLoading { if (!isLoading) { return; } isLoading = NO; // Hide the header [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDelegate:self]; [UIView setAnimationDuration:0.3]; [UIView setAnimationDidStopSelector:@selector(stopLoadingComplete:finished:context:)]; self.parentView.contentInset = UIEdgeInsetsZero; UIEdgeInsets tableContentInset = self.parentView.contentInset; tableContentInset.top = 0.0; self.parentView.contentInset = tableContentInset; [refreshArrow layer].transform = CATransform3DMakeRotation(M_PI * 2, 0, 0, 1); [UIView commitAnimations]; } - (void)stopLoadingComplete:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context { // Reset the header refreshLabel.text = self.textPull; refreshArrow.hidden = NO; [refreshSpinner stopAnimating]; } @end