Custom iOS UITabBarController Delegate Triggers viewDidLoad - ios

Issue:
An UITabBarController delegate method calls viewDidLoad and negates/ignores the screen selection.
Background:
I am making a split-view controller for small iOS screens. So, far the screen looks something like this:
Custom Split-View Controller
For the Detail View (RS), I'm using a UITabBarController, with the TabBar removed. You can find the code for this here.
The user selects the buttons on left, and a delegate sends the button's tag id to the UITabBarController delegate method.
The delegate method looks like this:
// delegate method in subclass of UITabBarController
-(void) screenSelected:(int)screenNum
{
NSLog(#"delegate arrived: %d", screenNum);
self.selectedIndex = screenNum;
// code goes to viewDidLoad
}
With breakpoints in place, I determined that the screen does not actually change.
If this screenSelected method is instead called from the UITabBarController viewDidLoad method (when the app first opens), the method works fine and the selectedIndex is changed as expected.
I'm trying to figure out why the delegate is triggering viewDidLoad. Does the delegate reset the view?
Thanks

Yes, viewdidload will call every time because you are initializing tabs again. setSelectedIndex intialize tabbarcontroller evrytime from UITabbarcontroller class. You should not do that from uitabbarcontroller class. just initialize tabbarcontroller onece.
You should not use tabbarcontroller like this way. according to your requirement you not need to use tabbar just shows viewController on button click.
hope this will work.

Related

Is there any way to avoid calling viewdidload method like tabbarcontroller?

I'm developing an application which will work based on maps. So once user opens MapViewController then I will load some data every 5 seconds.
I'm using navigation controller(Push view controller).
So every time when user goes to MapViewController viewdidload method calling. I don't want like that.
That's why I'm trying to avoid viewdidload method like tabbarcontroller.
Is there any way to achieve this?
viewDidLoad is getting called because your MapViewController is getting deallocated when you pop it off of the top of your navigation controller. When you recreate the view controller, it's getting allocated all over again, and the view loads again. If you keep a reference to MapViewController in the class containing your navigation controller, then ARC will not deallocate the object, and you can use this reference to push it back onto the stack so viewDidLoad will not get called again.
Edit: Adding code for reference:
MapViewContoller *mapViewController; // declared globally so there's a strong reference.
- (void) openMapViewController {
if (!mapViewController) {
mapViewController = [self.storyboard instantiateViewControllerWithIdentifier: MapViewControllerID];
}
[self.navigationController pushViewController: mapViewController, animated: YES];
}
Try this
-(void)clickForPush{
// declarre viewcontroller e.g 'saveRecipeVC' instance globally in interface
if (!saveRecipeVC) {
saveRecipeVC = [self.storyboard instantiateViewControllerWithIdentifier:SaveRecipeVCID];
}
[self.navigationController pushViewController:saveRecipeVC animated:YES];
}
viewDidLoad is intended to use when,not possible or efficient to configure 100% of an interface in a XIB. Sometimes, a particular property you wish to set on a view isn't available in a XIB. Sometimes, you use auto layout, and you realize that the editor for that is actually worse than writing auto layout code. Sometimes, you need to modify an image before you set it as the background of a button.
If you dont want to do these things make your viewDidLoad empty. Than avoiding. Or
Add code conditionaly into your viewDidLoad.
(void)viewDidLoad {
[super viewDidLoad];
if(condition) {
// put your code here
}
}

iOS presentViewController doesn't invoke viewDidLoad

I'm implementing my own 'back' button. Where onClick, the following code is executed in the ViewController (VC) being dismissed:
Dismiss current VC (VC#1)
Pop current VC (VC#1) off my custom navigationStack
Get the last VC (VC#2) from the navigationStack, and present it using
presentViewController
What happens is the back works visually works - i.e. current VC disappears, previous VC appears. However, the viewDidLoad method is not called. So the screen isn't updated with data updates from viewDidLoad.
[self dismissCurrentViewController:self completion:^{
[TWStatus dismiss];
FHBaseViewController *vcToDisplay = [[FHDataManager sharedInstance] popNavigationStack];
[vcToDisplay.homeVC presentViewController:vcToDisplay animated:NO completion: ^{ }];
}];
Questions:
I was under the impression that viewDidLoad always gets called when presentViuewController is used??
I 'build' the screen using a method called ONLY from viewDidLoad in VC#2. How is iOS displaying the screen without coming into viewDidLoad?
btw, I'm not using storyboards. Any help is appreciated!
My guess is that viewWillAppear is being called but viewDidLoad is not, at least not when you expect it is. viewDidLoad should be called once, but depending on how you're managing the view controllers, viewDidLoad may not be triggered every time your view appears (which happens after loading).
The completion handler is called after the viewDidAppear: method is called on the presented view controller. from presentViewController doc
so put this in your code with a breakpoint on the call to super and verify it is getting called when this transition occurs.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
edit: since you verified that viewWillAppear is getting called, then I would say that it's coming down to how you are managing the view controller life cycle. Even with a standard UINavigationController, viewDidLoad is not called when a view is shown as a result of popping items on the navigation stack. I would move your logic to viewWillAppear if you are dead set on not using UINavigationController
When I make a back button pragmatically I use:
[self.navigationController popViewControllerAnimated:YES];
This will invoke the viewDidLoad method. Use that instead of your current code.

Why does my tabbar controller execute code from a different view controller than the active one?

Firstly, I have set both viewcontrollers to be UITabBarController delegates. Both are part of a tab bar controller. I did this by putting the following code into each viewDidLoad:
self.tabBarController.delegate = self;
Then I added the following delegate method to CalculatorsViewController:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
[self presentCalculatorsView];
}
Where presentCalculators view simply reveals a subview within the same view controller.
I also added the following delegate method to the OptionsViewController:
-(void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
[self presentHomeScreen];
}
Again this method simply reveals another subview within the viewController.
The problem I am having is that the OptionsViewController presentHomeScreen method is only called if I do not visit the CalculatorsViewController. Once I do visit the CalculatorsViewController in the app and then return to OptionsViewController,
[self presentHomeScreen]
is never called. In fact, it appears that it still calls the method from the CalculatorsViewController. I tested it with an NSLog statement.
Any ideas why one method overrides the other? Or why the tab bar button executes code from another viewController, other than the one that is active?
EDIT* It is almost as if the one viewController 'steals' the delegate from the other.
By calling self.tabBarController.delegate = self; on each viewDidLoad method, you are basically telling the tab bar controller to use abandon the current delegate and use the current view controller as delegate.
Note that the viewDidLoad method is called only once under normal circumstances. (It may be called again when the view of your view controller is unloaded due to memory warning, for example, then you access the view of your view controller again, which calls loadView/awakeFromNib and viewDidLoad. I'm not entirely sure on this scenario though.) In your scenario:
Open OptionsViewController for the first time - tab bar controller's delegate is OptionsViewController
Open CalculatorsViewController for the first time - tab bar controller's delegate is now CalculatorsViewController
Go back to OptionsViewController - tab bar controller's delegate is still CalculatorsViewController, as the viewDidLoad is not called again
If you must change the delegate, you can do it instead in the viewWillAppear method.

How do i reset my tab to default view?

I have UITabViewController running with UINavigationController. In each tab i have a different TableViews that are roots of my application tree.
When i click an item of table, it goes to next level viewing another, detailed TableView, still having TabBar and NavigationBar on screen. It works perfectly, except for one thing. If i'm viewing details in one tab, then switch to another tab, and go back again, then i still see my detail. What i want to achieve, is to reset tab after leaving it.
I expect that i have to put something in viewDidUnload or simmilar, but couldn't find the right solution.
Hope you can help.
at
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
do
[viewController.navigationController popToRootViewControllerAnimated:YES];
That will navigate the tab's view controller to the root view whenever the tab is selected.
That's completely OK to keep your main window view controller (tab bar controller) instance at application delegate.
What you need to add is to set the delegate or whatever other initialized class to be the tab bar controller's delegate like this:
myTabBarController = [UITabBarController alloc ...
myTabBarController.delegate = self; // the app delegate will be also the tab bar delegate
in the app delegate, then you add the following method to the app delegate:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController{
[viewController.navigationController popToRootViewControllerAnimated:YES];
}
The tab bar controller will call this method whenever it's tab is selected.
You will also want to make the application delegate confirm UITabBarControllerDelegate this way:
#interface PSAppDelegate : UIResponder <UITabBarControllerDelegate>
that will let the compiler know that app delegate must or might have the methods declared in the protocol and will also give you so convenient auto-complition of this method.
One way to do this is to get the view controllers associated with the tabviewcontroller and then create new objects.
self.tabBarController?.viewControllers! will get an array of viewControllers for the tab bar. In my case they are a UINavigationControllers but they could be anything. I then get the viewController associated with that and reset that.
If you don't have navigation controllers you can just create new view controller objects and go with that.
below is the solution in my case (with the navcontroller)
let vc = self.tabBarController?.viewControllers![0] as! UINavigationController
let newVC = YourViewControllerClass()
vc.viewControllers[0] = newVC as UIViewController
This replaces the old (populated) vc with a new one!

Cant use TabBar delegate methods

I got an app with my custom TabBar Controller Class.
I tried to implement tabbar controller delegate method:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
NSLog(#"%i",tabBarController.selectedIndex);
}
But it doesnt work. Why?
in ViewDidLoad i write:
self.tabBarController.delegate = self;
And in .h i implement:
#interface BaseViewController : UITabBarController <UITabBarControllerDelegate>
In your custom TabBarController, do not use
self.tabBarController.delegate = self;
But use
self.delegate = self;
.tabBarController returns the nearest ancestor in the view controller hierarchy that is a tab bar controller, but your custom TabBarController IS the controller you want to target, so no need to search in its hierarchy
You have said, that it's your custom TabBarController. What is the customisation you've done? If you changed the TabBar panel and replaced it with your own to use
setSelectedIndex:
setSelectedViewController:
methods manually, then you should call delegate's methods manually too.
According to the Apple's documentation:
There are two types of user-initiated changes that can occur on a tab
bar:
The user can select a tab.
The user can rearrange the tabs.
Both types
of changes are reported to the tab bar controller’s delegate, which is
an object that conforms to the UITabBarControllerDelegate protocol.
Also check the UITabBarControllerDelegate Protocol Reference
In iOS v3.0 and later, the tab bar controller calls this method regardless of whether the > selected view controller changed. In addition, it is called only in response to user taps in > the tab bar and is not called when your code changes the tab bar contents programmatically.
Delegate will respond only if user interacts with its UITabBar control.

Resources