I have 3 tabs in my UITabbarController, that I created in my Appdelegate.
When I open the app, I have made the selected tabbarItem the third tabbarItem.
The user can only select the UITabBarItem at Index 0, when he is logged in.
I tried every thing to restrict the user from going to TabBarItem_0 when he is at TabBarItem_2.
But nothing worked. I used
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
}
But it's not working as I desired. I checked the stackoverflow and found almost the same question, where I found this delegate. But this is not working for me as desired. I googled, but couldn't find any solution other than stackoverflows links, which didn't help this time.
On the click of that disabled TabBar Item, I have to show a pop up. How can I implement that, too?
Try something like this,
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController;
{
if (tabBarController.selectedIndex == 0) {
if(isUserLoggedIn)
return YES;
else
return NO;
}
return YES;
}
If this does not work then,
Add this after you create the bar bar in app delegate,
[[[[self.tabBarController tabBar]items]objectAtIndex:0]setEnabled:FALSE];
once you log in enable it again
[[[[self.tabBarController tabBar]items]objectAtIndex:0]setEnabled:TRUE];
Quick hack that i used to present a popup on top of the current view when tapped on a tab bar item.
In your TabBarViewController class, implement the UITabBarControllerDelegate and remember to set self.delegate = self.
After that
func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool {
if viewController.title == "Unique_title" //set an unique title for the view controller in storyboard or the view controller class.
{
performSegueWithIdentifier("YourModalViewIdentifier", sender: nil)
return false
} else {
return true
}
}
That should help you display a modal view when tap is received on the uitabbaritem. I know using title as an unique identifier is bad practice, but just a quick solution to achieve what you want.
Hope that helps.
You can do this in your code
- (void)viewDidLoad {
...
[self checkLogin];
...
}
- (void)checkLogin {
if (!loggedIn) {
[[[[self.tabBarController tabBar]items]objectAtIndex:0]setEnabled:NO];
} else {
[[[[self.tabBarController tabBar]items]objectAtIndex:0]setEnabled:YES];
}
}
- (void)tapLogin {
// Do the login action
}
- (void)processLoginResult {
// Process the result of the login action
// If the result is success, set 'loggedIn = YES'
// Otherwise, set 'loggedIn = NO'
...
[self checkLogin];
...
}
If you want to do it with the Storyboard, simply selected the TabBarItem in the destination view controller scene and uncheck the Enabled box.
This is what I did in Swift 2.1:
self.tabBarController!.tabBar.items![0].enabled = false
Here is how you disable a tabbar item in Swift 3 and 4
tabBarController.tabBar.items![0].isEnabled = false
Related
I have the following requirements:
A Tabbar Navigation, with 2 "Tabs" that include a Login. So if a user tab (for example) an Button3 of the UITabBar ill get a fullscreen login view - But only on 2 specific tabs.
This is the workflow:
If there is an active session (i am using a login session management) - i will not present a login button - but if there is none, a login window should appear in fullscreen.
So when ill now show in any of the RootViewControllers in ViewDidLoad the loginViewController, the "content" of the rootViewControllers still get loaded.
So ill my opinion ill should handle after click on a button, WITHOUT loading the RootViewController for that tab.
Can anyone help me whats the best approach to solve such things?
You can use -(BOOL)tabBarController:shouldSelectViewController: method of UITabBarDelegate to handle tab selection and present login view instead of activating tab if needed.
You'll possible need to store a callback (i. e. block), in which you activate tab manually using tabBar.selectedIndex = index.
-(BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
NSInteger index = [_tabBarController.viewControllers indexOfObject:viewController];
if (index==4)
{
if (_authorized)
{
return YES;
}
else
{
[self authorize];
__weak UITabBarController *tabBarController = _tabBarController;
_authorizationCallback = ^(){
//
tabBarController.selectedIndex = 4;
};
return NO;
}
}
return YES;
}
I would like to display an image in response to a local notification event that occurs while the iPhone is locked. When the user swipes on the notification, I would like my app to go to the root view controller, via popToViewController:animated, and then push a view controller that displays the image. This works when I set animated = YES. When animated = NO, the view controller that displays the image doesn't respond when the user taps the back button. Any thoughts on why the image view controller's navigation controls don't work when I popToViewController without animation? Thanks in advance.
Here's the relevant code...
- (void) localNotificationHandler
{
#ifdef kAnimatePop
animated = YES; // This works
#else
animated = NO; // This doesn't work
#endif
_showImage = YES;
// Check if this view controller is not visible
if (self.navigationController.visibleViewController != self) {
// Check if there are view controllers on the stack
if ([self.navigationController.viewControllers count] > 1) {
// Make this view controller visible
[self.navigationController popToViewController:self animated:animated];
}
}
}
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (_showImage) {
_showImage = NO;
// Show image in a view controller
[self performSegueWithIdentifier:#"MainToImageSegue" sender:self];
}
}
You don't set _showImage until after popToViewController has been invoked. If it's animated viewDidAppear won't be called until later, so it unexpectedly works. If it's not animated then viewDidAppear is called immediately, before _showImage has been set. Move _showImage = true; to before the nav controller stack manipulations.
I might do the check this way:
- (void) localNotificationHandler{
...
...
if (self.navigationController.topViewController != self) {
[self.navigationController popToRootViewControllerAnimated:NO];
}
}
I would also recommend pushing your image view controller programmatically with something like
[self.navigationController pushViewController:<imageVC> animated:YES];
If your image view controller's navigation buttons are not working, it is likely because it is sending those navigation messages to nil. aka, the imageViewControllers self.navigationController is equal to nil.
I'm not sure how to fix this with storyboards, but if you present it programatically that problem should go away.
How can I prevent a tab from being clicked, depending on conditions ? I tried several things and I probably missed some basics.
I'm having an UITabBarCustomController implementing UITabBarController. In the viewDidLoad, depending on a condition, i force the selectedIndex to 1, which works.
Nevertheless, i'm not able to prevent users leaving this tab. I tried to set a UITabBarControllerDelegate on the other ViewController's linked with my UITabBar, where I implemented - (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController, and finally i also tried to implement this delegate and method on my custom UITabBarCustomController. But in both cases, when i click on another tab, nothing happens and users are able to access the tab (NSLog in those methods don't show in debug).
Any ideas ? thanks.
// Edit : Test code in delegate method :
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
NSLog(#"test");
return NO;
}
This is how you can stop/prevent Tabbar items to switch your tab on tabbar item click
For Swift 3.0
Make sure you have implemented UITabBarControllerDelegate and set UITabbarController's delegate to self
then override this delegate in your controller
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if viewController == tabBarController.viewControllers?[2] {
return false
} else {
return true
}
}
I have a Tab Bar Controller with four navigation controllers added to it. The navigation controllers appear as Tab Bar Items in the Tab Bar Controller. Now I want to add a fifth button to the tab bar, that does not open another view, but triggers some custom code. I want to display an overlaying "share menu" when clicking that Tab Bar Item regardless on which of the four pages the user is. How can I do that?
I can suggest to add dummy UIViewController to the last index and handle UITabBarControllerDelegate
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController {
if ([viewController == ...your dummy view controller...]) {
//Your custom action
return NO;
}
return YES;
}
Krivoblotsky has given the right answer! I'd like to elaborate a little more for anyone who is confused because for the full implementation there are a couple more moving parts. Let's say you have the app below. As it is when you click the home or profile icon the respective view will display. Let's say instead of the profile view to display, you want to add your custom transition / behavior.
To do this:
1. Given ProfileViewController class, you want include the UITabBarControllerDelegate in your ProfileViewController
#interface ProfileViewController : ViewController <UITabBarControllerDelegate> #end
2. Access your tabBarcontroller's delegate and set this as yourself in your ProfileViewController.m's viewDidLoad
self.tabBarController.delegate = self;
Essentially what this does is say hey, you know the tabBarController's delegate? (The guy that handles events) I know a guy and let this guy (self) handle those events instead. Like in English, you DELEGATE work to other people (you are the delegating object). The thing that handles the work, is the DELEGATE.
3. Implement the custom needed behavior
-(BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:
if ([viewController isKindOfClass:[ProfileViewController class]]){
NSLog(#"It's a profile");
return NO };
};
else{ return YES; }
The NO return says, when ProfileViewController is selected, do not do default behavior and display it's view.
Excellent explanation of delegates
In Storyboard, add a UIVIewController and connect it to the tab button you want to perform your custom action.
Give that UIViewController a unique title. e.g. "for custom action". It really doesn't matter, as nobody will ever see that title. It is just for you to use in the code below to identify that tab was tapped.
Create the class below and assign it to your UITabBarController in Storyboard
class TabBarController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
delegate = self
}
func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool {
if viewController.title == "for custom action" {
//do your custom actions
return false
}
return true
}
}
You should simply implement the following UITabBarDelegate method:
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item;
I have an application with a tab bar controller and each view contains a navigation controller. My MainWindow looks as follows: Image here http://www.freeimagehosting.net/image.php?7bc867a594.png
Everything works fine as it is but I noticed a problem when pushing a details view to the navigation controller. In the didSelectRowAtIndexPath for a tableviewcontroller that belongs to the tab bar controller (the one called Latest in the image) I am doing this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ArticleViewController *articleController = [[ArticleViewController alloc] initWithNibName:#"ArticleView" bundle:nil];
[self.navigationController pushViewController:articleController animated:YES];
[articleController release];
articleController = nil;
}
The ArticleViewController has its own tabbar because it needs to display different things. The problem is that when I push the ArticleViewController into the navigationController I see both tabbars at the bottom of the view. Is there any way I can solve this problem?
Thanks in advance
After spending hours and posting a question here I found that the solution to this problem is adding the following line after the instantiation of ArticleController.
articleController.hidesBottomBarWhenPushed = YES;
If you prefer storyboard configuration over coding there is a toggle for that. Just go destinationViewController > Attribute Inspector:
A very simple solution:
destinationViewController.hidesBottomBarWhenPushed = YES;
In your case:
articleController.hidesBottomBarWhenPushed = YES;
Hope this helps!
You can simple hide parent tabbar through storyboard .
Select viewcontroller > Attribute Inspector > check Hide Bottom Bar on Push
You can add below code in the view controller, which you are pushing.
-(BOOL)hidesBottomBarWhenPushed
{
return YES;
}
This will hide the tabbar in the pushed view controller only and as you pop the view controller tabbar remains unhide in rest all view controllers.
Swift version (3.x and above)
override var hidesBottomBarWhenPushed: Bool {
get {
return navigationController?.topViewController == self
}
set {
super.hidesBottomBarWhenPushed = newValue
}
}
Thanks
for swift 3,write the same code by you unhide the tabbar before pushviewController code like below
var frame = self.tabBarController?.tabBar.frame
frame?.origin.y = self.view.frame.size.height - (frame?.size.height)!+112
UIView.animate(withDuration: 0.2, animations: {
self.tabBarController?.tabBar.frame = frame!
})
self.navigationController?.pushViewController(viewController, animated: true)
or use just whant to unhide the tabbar u can use
viewController.hidesBottomBarWhenPushed = false
enter image description here
Go to interface builder in Xcode -> open attribute inspector and check the item 'Hide Bottom bar on Push' for view controller you don't want to show tab bar. It will work!!
Use property hidesBottomBarWhenPushed in the controller that you want to hide.
For hide, all controllers put into prepare for segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
segue.destination.hidesBottomBarWhenPushed = true
}