Wednesday, October 3, 2012

"Pull Down To Refresh" integration for updating data in UITableViews

Hello Guyz , in this section we will be talking about "Pull Down To Refresh" integration for updating your data in UITableView. This kind of effect can be seen in  facebook app, where we  pull down the view and it refreshes/updates the facebook feeds. In the similar way, in this section i will write a bunch of codes that basically do is: when we pull down the UITableView, it will updates its datasource and reload the table section. Here, is the example screen that will help you to understand about this effect more wisely.
download and add this dropdown image to be used in project named updateArrow.png


 Lets start coding !!!
Let me assume that you have a UITableViewController class or UIViewController class that has a UITableView in it. Don't forget to include <UITableViewDelegate,UITableViewDataSource>
In your .h file:
Declare,synthesize the following objects and variables.
   NSMutableArray *data; // data source for table
   UILabel *updateLabel; // updating information

   UILabel *lastUpdateLabel;  // displaying time of last update
   UIImageView *updateImageView; // pull down/up  image
   BOOL shouldUpdate;

   NSOperationQueue *queue;
   float offset;
   BOOL isUpdating;
   UIActivityIndicatorView *spinner;

 
In your .m file :
- (void)viewDidLoad{
    [super viewDidLoad];

    updateLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, -40, 320, 20)];
    updateLabel.textAlignment = UITextAlignmentCenter;
    updateLabel.text = @"Pull down to refresh...";
    updateLabel.textColor = [UIColor darkGrayColor];
    updateLabel.backgroundColor = [UIColor clearColor];
    lastUpdateLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, -20, 320, 20)];
    lastUpdateLabel.textAlignment = UITextAlignmentCenter;
    lastUpdateLabel.textColor = [UIColor darkGrayColor];
    lastUpdateLabel.backgroundColor = [UIColor clearColor];
    lastUpdateLabel.font = [UIFont fontWithName:@"Helvetica" size:10];

   // now making a text file named "LastUpdateDate.txt", so that we can store last updated date and time there
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *documentsFolderPath = [documentsDirectory stringByAppendingPathComponent:@"LastUpdateDate.txt"];
    lastUpdateLabel.text = [NSString stringWithContentsOfFile:documentsFolderPath encoding:NSStringEncodingConversionAllowLossy error:nil];
     updateImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"updateArrow.png"]];
      updateImageView.frame = CGRectMake(30, -50, 27, 40);
     [self.tableView addSubview:updateLabel];
    [self.tableView addSubview:lastUpdateLabel];
    [self.tableView addSubview:updateImageView];
     data = [NSMutableArray arrayWithObjects:@"Data", @"Data", @"Data", nil]; // initializing your table data source
     shouldUpdate = NO;
     isUpdating = NO;
    self.tableView.rowHeight = 50.0f;
    self.tableView.delegate = self;

    self.tableView.dataSource = self;
}

Now, we will use UIScrollView delegates to track pull down effect. To be noted here(I haven't mentioned about it) is that, be sure of creating UITableView cell and populating table with original datasoure in "data" array.
-(void)scrollViewDidScroll:(UIScrollView *)sender {
    offset = self.tableView.contentOffset.y;
    offset *= -1;
    if (offset > 0 && offset < 60) {
        if(!isUpdating) updateLabel.text = @"Pull down to refresh...";
        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationBeginsFromCurrentState:YES];
        [UIView setAnimationDuration:0.2];
        updateImageView.transform = CGAffineTransformMakeRotation(0);
        [UIView commitAnimations];
        shouldUpdate = NO;
    }
    if (offset >= 60) {
        if(!isUpdating) updateLabel.text = @"Release to refresh...";
        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationBeginsFromCurrentState:YES];
        [UIView setAnimationDuration:0.2];
        updateImageView.transform = CGAffineTransformMakeRotation(3.14159265);
        [UIView commitAnimations];
        shouldUpdate = YES;

          }
    if (isUpdating) {
        shouldUpdate = NO;
    }
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
     if (shouldUpdate) {
        queue = [NSOperationQueue new];
        NSInvocationOperation *updateOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(updateMethod)  object:nil];
        [queue addOperation:updateOperation];

        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:0.2];
        self.tableView.contentInset = UIEdgeInsetsMake(60, 0, 0, 0);
        [UIView commitAnimations];
    }
}
- (void) updateMethod {
    //UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Update!" message:@"Perform whatever action you want!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
    //[alert performSelectorOnMainThread:@selector(show) withObject:nil waitUntilDone:NO];

       [self performSelectorOnMainThread:@selector(startSpinner) withObject:nil waitUntilDone:NO];
  }


- (void) startSpinner {
    spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    //spinner.color = [UIColor lightGrayColor];
    spinner.center = updateImageView.center;
    updateImageView.hidden = YES;
    [spinner startAnimating];
    [self.view addSubview:spinner];
    updateLabel.text = @"Updating....";
    isUpdating = YES;
    [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(finishUpdateMethod) userInfo:nil repeats:NO];

  //this is the place where you can perform your data updating procedure(data populating from web or your database), for this section i am simply reloading data after 3 seconds of pull down
}

-(void) finishUpdateMethod {
    [self stopSpinner];
    [self setUpdateDate];
    [data addObject:@"New Data"];[data addObject:@"New Data"];
    //[self.tableView reloadData];
    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationMiddle];   
}
- (void) stopSpinner {
    [spinner removeFromSuperview];
    updateImageView.hidden = NO;

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.2];
    self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
    [UIView commitAnimations];
    isUpdating = NO;
}
- (void) setUpdateDate {
    NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
    [dateFormat setDateFormat:@"MM/dd/yyyy hh:mm a"];
     NSDate *now = [[NSDate alloc] init];
    NSString *dateString = [dateFormat stringFromDate:now];
    NSString *objectString = [[NSString alloc] initWithFormat:@"Last updated on %@", dateString];
    lastUpdateLabel.text = objectString;
   
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *documentsFolderPath = [documentsDirectory stringByAppendingPathComponent:@"LastUpdateDate.txt"];
    [objectString writeToFile:documentsFolderPath atomically:YES encoding:NSStringEncodingConversionAllowLossy error:nil];
}


Note : Instead of - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{} you can use - (void) scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(CGPoint *)targetContentOffset {} for iOS 5 and later.

No comments:

Post a Comment