Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to know when a UITableView animation is completed?

Tags:

uikit

iphone

I want to do a series of things in reaction to the end of certain UITableView animations. For example, I want the table view cell to highlight (via selectRowAtIndexPath) after it has been scrolled to via scrollToRowAtIndexPath.

How can this be done?

like image 291
Boon Avatar asked Aug 27 '09 07:08

Boon


2 Answers

Basic template:

[UIView animateWithDuration:0.2 animations:^{
    //do some animations, call them with animated:NO
} completion:^(BOOL finished){
    //Do something after animations finished     
}];   

Example: Scroll to row 100. When finished, get the cell at this row and make the cell content view with tag=1 to the firstResponder:

[UIView animateWithDuration:0.2 animations:^{
        [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:100 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
    } completion:^(BOOL finished){
        //Do something after scrollToRowAtIndexPath finished, e.g.:
        UITableViewCell *nextCell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:100 inSection:0]];
        [[nextCell.contentView viewWithTag:1] becomeFirstResponder];        
    }];   
like image 52
Przemyslaw Avatar answered Nov 15 '22 12:11

Przemyslaw


I realize this an old post but I was having a similar problem and created a solution that worked well for me. I applied the techniques used on NSCookBook for creating UIAlertViews with blocks. The reason I went for this was because I wanted to use the built-in animations rather than UIView's + animateWithDuration:animations:completion:. There is a larger difference between these animations with the change to iOS 7.

You create a category for UITableView and in the implementation file you create an inner private class that will callback the block by assigning it as your tableview's delegate. The catch is that until the block is called, the original delegate will be "lost" so to speak, since the new delegate is the object that will call the block. That is why I put a notification to send a message when the block has been called to reassign the original UITableViewDelegate. This code has been tested and is working on my end.

// Header file
@interface UITableView (ScrollDelegateBlock)

-(void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath
             atScrollPosition:(UITableViewScrollPosition)scrollPosition
                     animated:(BOOL)animated
               scrollFinished:(void (^)())scrollFinished;

@end


// Implementation file
#import "UITableView+ScrollDelegateBlock.h"
#import <objc/runtime.h>

NSString *const BLOCK_CALLED_NOTIFICATION = @"BlockCalled";

@interface ScrollDelegateWrapper : NSObject <UITableViewDelegate>

@property (copy) void(^scrollFinishedBlock)();

@end

@implementation ScrollDelegateWrapper

-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
    if (self.scrollFinishedBlock) {
        [[NSNotificationCenter defaultCenter] postNotificationName:BLOCK_CALLED_NOTIFICATION object:nil];
        self.scrollFinishedBlock();
    }
}

@end

static const char kScrollDelegateWrapper;

static id<UITableViewDelegate>previousDelegate;

@implementation UITableView (ScrollDelegateBlock)

-(void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath
             atScrollPosition:(UITableViewScrollPosition)scrollPosition
                     animated:(BOOL)animated
               scrollFinished:(void (^)())scrollFinished {
    previousDelegate = self.delegate;
    ScrollDelegateWrapper *scrollDelegateWrapper = [[ScrollDelegateWrapper alloc] init];
    scrollDelegateWrapper.scrollFinishedBlock = scrollFinished;
    self.delegate = scrollDelegateWrapper;

    objc_setAssociatedObject(self, &kScrollDelegateWrapper, scrollDelegateWrapper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    [self scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(blockCalled:)
                                                 name:BLOCK_CALLED_NOTIFICATION
                                               object:nil];
}

/*
 * Assigns delegate back to the original delegate
 */
-(void) blockCalled:(NSNotification *)notification {
    self.delegate = previousDelegate;
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:BLOCK_CALLED_NOTIFICATION
                                                  object:nil];
}

@end

You can then call the method like any other with a block:

[self.tableView scrollToRowAtIndexPath:self.currentPath
                          atScrollPosition:UITableViewScrollPositionMiddle
                                  animated:YES
                            scrollFinished:^{
                                NSLog(@"scrollFinished");
                            }
];
like image 29
Chris Avatar answered Nov 15 '22 14:11

Chris