// // 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 "PullRefreshTableViewController.h" #define REFRESH_HEADER_HEIGHT 52.0f @implementation PullRefreshTableViewController @synthesize textPull, textRelease, textLoading, refreshHeaderView, refreshLabel, refreshArrow, refreshSpinner; - (id)initWithStyle:(UITableViewStyle)style { self = [super initWithStyle:style]; if (self != nil) { [self setupStrings]; } return self; } - (id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self != nil) { [self setupStrings]; } return self; } - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self != nil) { [self setupStrings]; } return self; } - (void)viewDidLoad { [super viewDidLoad]; [self addPullToRefreshHeader]; } - (void)setupStrings{ textPull = [[NSString alloc] initWithString:@"Pull down to refresh..."]; textRelease = [[NSString alloc] initWithString:@"Release to refresh..."]; textLoading = [[NSString alloc] initWithString:@"Loading..."]; } - (void)addPullToRefreshHeader { 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 = NSTextAlignmentCenter; 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:UIActivityIndicatorViewStyleGray]; 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.tableView 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.tableView.contentInset = UIEdgeInsetsZero; else if (scrollView.contentOffset.y >= -REFRESH_HEADER_HEIGHT) self.tableView.contentInset = UIEdgeInsetsMake(-scrollView.contentOffset.y, 0, 0, 0); } else if (isDragging && scrollView.contentOffset.y < 0) { // Update the arrow direction and label [UIView animateWithDuration:0.25 animations:^{ 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); } }]; } } - (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 animateWithDuration:0.3 animations:^{ self.tableView.contentInset = UIEdgeInsetsMake(REFRESH_HEADER_HEIGHT, 0, 0, 0); refreshLabel.text = self.textLoading; refreshArrow.hidden = YES; [refreshSpinner startAnimating]; }]; // Refresh action! [self refresh]; } - (void)stopLoading { isLoading = NO; // Hide the header [UIView animateWithDuration:0.3 animations:^{ self.tableView.contentInset = UIEdgeInsetsZero; [refreshArrow layer].transform = CATransform3DMakeRotation(M_PI * 2, 0, 0, 1); } completion:^(BOOL finished) { [self performSelector:@selector(stopLoadingComplete)]; }]; } - (void)stopLoadingComplete { // Reset the header refreshLabel.text = self.textPull; refreshArrow.hidden = NO; [refreshSpinner stopAnimating]; } - (void)refresh { // This is just a demo. Override this method with your custom reload action. // Don't forget to call stopLoading at the end. [self performSelector:@selector(stopLoading) withObject:nil afterDelay:2.0]; } - (void)dealloc { [refreshHeaderView release]; [refreshLabel release]; [refreshArrow release]; [refreshSpinner release]; [textPull release]; [textRelease release]; [textLoading release]; [super dealloc]; } @end