Perform an action when tabBarController changes - iphone

I have a tabbar application that has one screen that displays statistics based on the data presented in the tableviews of over tab screens. I would like to refresh this view once the stats view becomes selected again. I have implemented the tabbarcontrollerdelegate protocol to take an action when the viewcontroller.tabbaritem.title isequaltostring:#"foo". which works fine for my nslog statement but when i try and trigger the viewcontroller to execute the viewdidload method it never happens. And the code to refresh the stats view is in the viewdidload method.
From my AppDelegate
- (void)tabBarController:(UITabBarController*)tabBarController didEndCustomizingViewControllers: (NSArray*)viewControllers changed:(BOOL)changed
{
}
- (void)tabBarController:(UITabBarController*)tabBarController didSelectViewController:(UIViewController*)viewController {
if([viewController.tabBarItem.title isEqualToString:#"Summary"]) {
NSLog(#"didSelectViewController %#", viewController.tabBarItem.title);
[viewController viewDidLoad]; //FAIL
}
}

Never call viewDidLoad by yourself. That is a delegate method that is sent to the view controller after the view has loaded, you shouldn't call it manually.
In this case, view controllers that have views which are managed by a tab bar controller are sent the viewWillAppear:, viewDidAppear:, viewWillDisappear: and viewDidDisappear.
You should should use these methods to perform actions when your views are shown and hidden.
Example: implement viewDidAppear: and refresh your stats view.
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated]; // don't forget to call super, this is important
// do your refreshing here
}

Related

iPhone: viewWillAppear only when coming back from other views

viewWillAppear is called both when going to the view and when coming back to the view from other views.
I want to select(highlight) and fade-out a cell only when coming back from other views.
Is there a delegate method to do this?
I'm using UINavigationViewController.
If you're on iOS 5, you can use these new properties:
These four methods can be used in a view controller's appearance
callbacks to determine if it is being presented, dismissed, or added
or removed as a child view controller. For example, a view controller
can check if it is disappearing because it was dismissed or popped
by asking itself in its viewWillDisappear: method by checking the
expression ([self isDismissing] || [self
isMovingFromParentViewController]).
- (BOOL)isBeingPresented __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0);
- (BOOL)isBeingDismissed __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0);
- (BOOL)isMovingToParentViewController __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0);
- (BOOL)isMovingFromParentViewController __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0);
In your code:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (!(self.isMovingToParentViewController || self.isBeingPresented))
{
// animate
}
}
EDIT:
If you're using a UITableViewController, setting the property -clearsSelectionOnViewWillAppear to YES will do this for you. You only have to do it manually if you're using a regular UIViewController with a UITableView subview.
If you are targeting iOS 5, you can use [self isBeingPresented] and [self isBeingDismissed] to determine if the view controller is being added or removed from the nav controller.
I'm also suspecting that you could improve the logic of when you select/deselect the cell in your table view such that it doesn't matter whether the view controller is coming or going.
The usual way to do it is this: when someone selects a row in the table view in view controller A, it gets selected/highlighted and you push a new view controller B. When view controller B is dismissed, you animate the deselection of the table view row in viewDidAppear (so the user can see it fading out) in view controller A. You wouldn't worry about whether view controller A has just appeared or is re-appearing, because there would only be a selected table view cell in the appropriate case.
viewWillAppear is getting called when the view appears
after the viewDidLoad
after you dismiss or pull a view controller
You could change the viewWillAppear to the following
- (void) viewWillAppear:(BOOL)animated
{
static BOOL firstTime = YES;
if (!firstTime)
{
//Do your alpha animation
}
firstTime = NO;
}
In your UINav Controller you could create a "lastView" property and have each of your view controllers (that are controlled by your UINav Controller) set this property on "viewWillAppear"... in your target view... the one you want to do the highlighting and fading you could check this property of the UINav Controller and see if it's NIL or not.
That's just one way to do it. This wouldn't work if you pop up a modal or the like.

How to pop a viewController and then Push a viewController through delegation

I have a UITableViewController that when a cell is pressed, I want the controller to pop itself, and then have the controller it pop's to, push another view controller onto the stack.
I am invoking this method because the popped-to viewController is the delegate of the tableViewController
I am currently invoking this method with a delay on it, because otherwise, everything gets screwed up waiting for the animation to end. Doing it this way seems a bit hacky and seems to me like it would fail if someone's device didn't pop the view in the allotted wait time I have given it.
Here is some of the code:
//**** code in my tableViewController ***//
[self.navigationController popViewControllerAnimated:YES];
[self.delegate cellPressedInTableViewControllerWithCalculationsModel:(id)anArgmentMyDelegateMethodTakes];
// **** Code in the viewController being popped to ****//
//CalculationsViewController is a subclass of UIViewController
CalculationsViewController *calcViewController = [[CalculationsViewController alloc] init];
//some customization code would go her
[self.navigationController performSelector:#selector(pushViewController:animated:) withObject:calcViewController afterDelay:0.75];
//this seems like the arbitrary part, the 0.75 second delay.
[calcViewController release];
There seems like there should be a better way to pop/push through delegation that will execute after the animation finishes. The wait time seems to me like it could cause unexpected problems.
I have also tried using:
performSelectorOnMainThread:withObject:waitUntilDone
But the code just executes immediately and the view hierarchy screwed up.
I have also looked at this question:
Delegation question
and it has gotten me this far, but I am curious to see if there is a better way to perform such a task,
Thanks.
edit: I have also tried wrapping the method in an instance of NSInvocation, and I couldn't get it to coordinate the method call until after the animation finished without arbitrarily setting the delay
The cleanest way to do more than a single push or pop of a view controller is to set the UINavigationControllers view controllers array.
For example, to pop and then push a view controller:
MyTableViewController *vc = [[MyTableViewController alloc] init];
NSMutableArray *controllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
[controllers removeLastObject];
[controllers addObject:vc];
[self.navigationController setViewControllers:controllers animated:YES];
You should use a flag to overcome this situation. You set this flag in viewWillDisappear method of view controller being popped. When this flag is set then and then you can push another view controller on stack. Hope it's clear.
How about when you dismiss your UIViewController containing the table you send a NSNotifcation in your viewDidDisappear method like so:
- (void)viewDidDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"loadOtherVC" object:nil];
}
And in your parent view controller that will push a new view controller, you add an observer for that notification like so:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(LoadOtherVC:) name:#"loadOtherVC" object:nil];
You should have a method that matches the selector.
- (void) LoadOtherVC:(NSNotification *) notification
{
// load your other view controller you want here
}
Don't pop it from the top level controller. Call the delegate method that will pop the view off the navigation controller and then push a new one on.
I liked Alexandre's solution but if I were a delegate person, I wouldn't want to use notification. So in that case, we can just use the delegate in the viewDidDisappear method.
So, in the didSelectRowAtIndexPath method, you can pop the controller-
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[self.navigationController popViewControllerAnimated:YES];
}
and in the same View Controller, the ViewController you are popping, call the delegate method in the viewDidDisappear method like-
-(void)viewDidDisappear:(BOOL)animated{
[self.delegate cellPressedInTableViewControllerWithCalculationsModel:(id)anArgmentMyDelegateMethodTakes];
}
Then in the controller that is pushed can implement the delegate method and inside that delegate method, you can do whatever you want.

How do be notified when a ViewController is removed from a NavigationController

Is there a way to be notified when a ViewController is removed from a UINavigationController because the back button was pressed?
You can use viewWillDisappear: in the view controller that is disappearing. If the other view controller needs to be notified, you can use a delegate method to notify it:
//in the disappearing view controller, class MYViewController
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
//do stuff you need to do
if ([self.delegate respondsToSelector:#selector(myViewControllerDidDisappear:)])
[self.delegate myViewControllerDidDisappear:self]; //bottom view controller is delegate
}
In conjunction with eman's method, check
[navController.viewcontrollers count]
If it is one greater than before (you need to maintain a count) then something was pushed. If it is one less, and viewWillDisappear: was called, then the view controller was removed.

presentModalViewController viewDidLoad not being called

I have an event which calls a view to appear, but the -viewdidload event isn't appearing as expected each time it's called. Here's the method I use to call it...
[self presentModalViewController:addItemViewController animated:YES];
then inside the addItemViewController, the method is
- (void)viewDidLoad {
NSLog(#"alright, lad!");
}
To close the view, I have a button with the code
- (IBAction)cancel {
[self dismissModalViewControllerAnimated:YES];
}
the "alright, lad" log is shown the first time the view appears, but never again when it's launched. Is there a method I can use to let the app "forget" about the view? Or should I be using another load method? I tried loadView (I think) but that had a blank screen...
Thanks for any help!
viewDidLoad is only called when the view is first instantiated. If you're not recreating the view controller each time, you'll only get it called once (and called again if you get a memory warning, and the view is nil'd out). You probably want to use viewWillAppear: or viewDidAppear:.
Make sure you call the superclass in each of those methods, e.g.
- (void)viewWillAppear:(BOOL)animated {
NSLog(#"view appeared");
[super viewWillAppear:animated];
}

Update UIViewControllers after switching of the tabbar selection

I have two view controllers in a tabbar which can both edit data. Therefore, I need to call a reload_data function whenever the user makes a switch on the tabbar. How can I catch the switch or the appearance of the viewcontroller. Somehow viewDidAppear is not called on a tabbar switch. And I do not want to use the tabbarController delegate for this, because several viewControllers are affected (and I cannot set them all as delegate). What is a good way to solve this?
e.g. this didn't work:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];
[self reloadData];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:YES];
[self reloadData];
}
If you're using Interface Builder, make sure the class for the viewController your expecting to reload is defined (Select the ViewController in IB, then CMD-4, make sure class is defined to be the class you want viewWillAppear and viewDidAppear to be called in).
If you're not using IB, post your code for init/calling the viewController.