How to know when a UITableView animation is completed? - 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?

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];
}];

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");
}
];

Well if you want to perform an action once the scrollToRowAtIndexPath has been fired.
- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated
You need to create a CAAnimation pointer like
CAAnimation *myAnimation;
Then set the delgate to self
myAnimation.delegate = self;
Once you do that, these following delegate methods should activate where you can put your code:
- (void)animationDidStart:(CAAnimation *)theAnimation
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag

Related

How to reload table of one view from other view

I am trying to reload table of MyView.m from MySliderController.m but its not reloading.
i have declared below method in MyView.m
-(void)updateData{
[self.itablview reloadData];
}
and calling that method form MySliderController.m class by below code
DDMenuController *menuController = (DDMenuController*)((AppDelegate*)[[UIApplication sharedApplication] delegate]).DDmenuController;
MyView *obj = [[MyView alloc]init];
if(indexPath.row == 3){
[menuController showRootController:YES];
[obj updateData];
}
that method gets called but table is not reloading even i am checking existence of tableview by below code
-(void)reloadtable:(id) sender{
NSlog(#"%#",self.itableview)
[self.itableView reloadData];
}
in console i get (null)
but when i am checking in below code
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"%#", self.itableView);
}
i am getting below message . it means table exists
2013-06-29 19:31:48.673 slidingViews[7025:c07] <UITableView: 0x79d9400; frame = (0 0; 320 480); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x714e940>; layer = <CALayer: 0x714e3f0>; contentOffset: {0, 0}>
what can be the issue. Please check my code here
https://github.com/kanwarpalSingh/mycode
Your problem is that when you're instantiating the MyView you're actually creating a new (but not visible) version of the MyView view controller, not the original MyView you originated from. There are a few different methods to handle this and it really comes down to what works best for you.
One example would be to subscribe MyView to a custom notification originating from MySliderControl such as this...
MyView.m
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateData:) name:#"updateDataOnMyView" object:nil];
}
MySliderControl.m
[[NSNotificationCenter defaultCenter] postNotificationName:#"updateDataOnMyView" object:nil];
You can also use a delegate method if you don't like this setup. There are plenty of tutorials out there on creating your own delegates as well. Good luck!

Call [tableView reloadData]; on a viewController from a modalViewController

I have a modalViewController that comes up over the top of a viewController with a tableView. When the user clicks a button on the modalViewController I want to reload the tableView within the viewController with this:
[tableView1 reloadData];
I do not want to put the reload in the viewDidAppear or viewWillAppear methods as they get called when i do not need the tableView to reload (i.e. when the user clicks the back button to return to the tableView).
Is there a way to do this?
Try
1) write one method which reloads the table data.
2) Call it on the back button clicked.
This is the classic delegate pattern problem, in your modal view controller you need a delegate reference to the current view controller presenting it
//Modal
#protocol ModalVCDelegate
- (void)tappedBackButton;
#end
#class ModalVC: UIViewController
#property id<ModalVCDelegate> delegate;
#end
#implementation
- (void)backButtonTapped:(id)sender
{
if (self.delegate)
[self.delegate tappedBackButton];
}
#end
Now, in your presenting VC, just process this delegate message
//Parent VC
- (void)showModal
{
ModalVC *vc = [ModalVC new];
vc.delegate = self;
//push
}
- (void)tappedBackButton
{
[self.tableView reloadData];
//close modal
}
You can use delegate . If find it more harder then alternative is to use NSNotificationCenter. You can see accepted answer for Refreshing TableView. This is really very short, easy and understandable way.
using Notification like bellow Method:-
Create NSNotificationCenter at yourViewController's ViewdidLoad Mehod
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(ReloadDataFunction:)
name:#"refresh"
object:nil];
[super viewDidLoad];
}
-(void)ReloadDataFunction:(NSNotification *)notification {
[yourTableView reloadData];
}
Now you can Call this Notification from your modelViewController BackButton or else you want from calling this Refresh notification like putting this line of code:-
[[NSNotificationCenter defaultCenter] postNotificationName:#"refresh" object:self];
NOTE: postNotificationName:#"refresh" this is a key of particular Notification
Try to use this one
Make a Button and click on this button and than you can reload your data.
This button make custom and use it on background.
- (IBAction)reloadData:(id)sender
{
[tblView reloadData];
}
You can use NSNotification to refresh table on ViewController.
Inside viewController :
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
Write code in viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reloadMainTable:)
name:#"ReloadTable"
object:nil];
- (void) reloadMainTable:(NSNotification *) notification
{
[tableView reload];
}
Inside ModelViewController:
[[NSNotificationCenter defaultCenter]
postNotificationName:#"ReloadTable"
object:nil];
Here you can also send custom object instead of nil parameter. But be care full about removal of NSNotification observer.

Enable all textfields in UITableViewCell when in Edit mode

I have a bunch of custom UITableViewCells with a label and textbox. I have the textbox disabled but I want to make it so when the user taps the Edit button it will make the textboxes editable. How can I do this so that ALL the UITextFields in the UITableView become enabled?
I have
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
[self.navigationItem setHidesBackButton:editing animated:YES];
if (editing) {
}
}
but cannot add the textbox enable in there since I don't have access to all the textfields. Would I need to add code to grab all the cells and loop through them and enable the textfields?
I would do this by setting a isEditing BOOL on your UITableViewDelegate in the setEditing:animated: method and just updating visible cells when the value is changed.
NSArray *visibleCells = [myTable visibleCells];
for (MyTableViewCell *cell in visibleCells)
cell.textField.enabled = isEditing;
Then, using your UITableViewDelegate again, update new cells as they appear in tableView:willDisplayCell:forRowAtIndexPath:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
cell.textField.enabled = isEditing;
}
Edit your subclass of UITableViewCell and register your instances for an editing notification in your subclass's viewDidLoad or init method:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(disableTextBox) name:#"EditingIsEnabled" object:nil];
And implement a method called disableTextBox that disables the text box for that cell.
Then in your setEditing:animated method, post the notification when you want to start editing:
[[NSNotificationCenter defaultCenter] postNotificationName:#"EditingIsEnabled" object:self];
Override the method dealloc in your UITableViewCell and remove yourself as an observer, or you'll crash:
[[NSNotificationCenter defaultCenter] removeObserver:self];
If you're not using ARC, make sure to call [super dealloc]. If you're using ARC, do not call super.
You can do the same thing when you want to disable all the cells, just post a notification with a different name like EditingIsDisabled.
Let me know if you need me to flesh out the code a bit more.
Edit: I like DBD's method better in this situation.

animation issues when editing a uitableview

This is really simple, though still driving my nuts. I have a uitableview where I am trying to animate transition in and out of editing mode. This is what I took from an example that I have seen. It does do the job, but without the animation.
Any thoughts?
- (IBAction) EditTable:(id)sender
{
if(self.editing)
{
[super setEditing:NO animated:YES];
[tblSimpleTable setEditing:NO animated:YES];
[tblSimpleTable reloadData];
[self.navigationItem.leftBarButtonItem setTitle:#"Edit"];
[self.navigationItem.leftBarButtonItem setStyle:UIBarButtonItemStylePlain];
}
else
{
[super setEditing:YES animated:YES];
[tblSimpleTable setEditing:YES animated:YES];
[tblSimpleTable reloadData];
[self.navigationItem.leftBarButtonItem setTitle:#"Done"];
[self.navigationItem.leftBarButtonItem setStyle:UIBarButtonItemStyleDone];
}
}
PS: I am also not sure why I need this line: [super setEditing:NO animated:YES]; but things just dont seem to work at all without it. I just saw a few examples online that dont do that.
Thanks!
Maybe you should not reloadData when set editing property.
BTW, What's your "super" class? Normally you don't have to invoke [super setEditing:YES animated:YES];
Is it only the button that isn't animating properly? Either way you should probably be using super.editButtonItem instead of your own; it's animated and just setting the text and style like that (I believe) isn't. As far as calling the super, are you overriding one of the editing methods and not calling the super method from within there? And xuzhes's answer about the reloadData is, I believe, correct as well.
Try this:
#Implementation YourViewController // This can (should) be a subclass of UITableViewController to make your life easier
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = self.editButtonItem; // Automatically calls setEditing:animated: and changes itself to "Edit"/"Done" between presses
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
if (editing == YES) {
// Do stuff here
} else {
// Do stuff here
}
// Reload all sections of the table view
// THIS IS THE PART YOU'RE INTERESTED IN
NSRange range = NSMakeRange(0,[self.tableView numberOfSections]);
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:range];
[self.tableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic];
}
Check out the documentation for comments on the methods from Apple :)

How to use presentModalViewController in iPhone/iPad?

Hi i am very new in iPhone/iPad developmet.
In my application on clicking of button in want to show view controller like presentModalViewController and i am able to do that which contains the UITableView with some numbers of values. on selecting particulate row i want to pass values to controller which is behind that controller.
for that i am using apple sample application PhotoPicker code. http://developer.apple.com/library/ios/#samplecode/PhotoPicker/Introduction/Intro.html
But i am not able to understand the what i did wrong in my code.
I am not able to go in the code which is in the MyViewController.m
- (void)didFinishWithCamera
{
[self dismissModalViewControllerAnimated:YES];
//Here is my some logic
}
can any one help me for this...how to call this function from OverlayViewController?
please refer above link and guide me or give me some steps or guide me for the same.
use delegation.
I use something like this in a app I'm writing at the moment:
// MySecretSelectionViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[delegate mySecretSelectionViewController:self didSelectObject:[self objectForIndexPath:indexPath] atIndexPath:indexPath];
}
// MyViewController.m
- (void)mySecretSelectionViewController:(MySecretSelectionViewController *)es didSelectObject:(MySecretObject *)object atIndexPath:(NSIndexPath *)indexPath {
// do something with the selected object
[self dismissModalViewControllerAnimated:YES];
}
- (void)showMySecretSelectionViewController:(id)sender {
MySecretSelectionViewController *vc = ...
vc.delegate = self;
// present ViewController
}
You can also do this with use of NSNotificationCenter.
Inside MyViewController.m:
- (void)viewDidLoad
{
// your code
// Add observers
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didFinishWithCamera) name:#"YourObserverName" object:nil];
}
+ (void)callDidFinishWithCamera
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"YourObserverName" object:nil];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
// your code
}
From OverlayViewController.m:
[MyViewController callDidFinishWithCamera];
Use the above class method to call didFinishWithCamera in MyViewController from OverlayViewController