I know this question has asked by many users,But i did not found any answer related to my issue.
I'm using UISplitViewController ,my application starts with login page, so i have hidden masterView on start, after some time on DetailViewController i shown the master viewController using Delegate method.
- (BOOL)splitViewController:(UISplitViewController *)svc shouldHideViewController:(UIViewController *)vc inOrientation:(UIInterfaceOrientation)orientation NS_AVAILABLE_IOS(5_0)
{
return hideMaster;
}
i have initialised hideMaster to NO ,and my ViewDidLoad() of DetailViewController is below
ViewDidLoad(){
[self.splitViewController.view setNeedsLayout];
self.splitViewController.delegate =Nil;
self.splitViewController.delegate = self;
[self.splitViewController willRotateToInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation duration:0];
[super viewDidLoad];
}
it worked fine my master is now unhide.But on the same detailViewController i have a Back Button on which i'm poping the current ViewController to last viewController and again i want to hide masterViewController my code for back button is below.
- (IBAction)back:(id)sender {
hideMaster = NO;
self.splitViewController.delegate =Nil;
self.splitViewController.delegate = self;
[self.splitViewController willRotateToInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation duration:0];
[self.navigationController popViewControllerAnimated:NO];
}
Its poping the CurrentViewController to last ViewController but its not Hiding the masterViewController
Please help me out.
#JohnD,I went through your code.you are hiding the master view controller while poping the last View controller,but the viewController to which you are navigating is still showing the master View controller.therefore your masterView is still there.
follow the steps given below.
1.make delegate of UInavigationController to that ViewController which is your last singleView controller.
2.In last single ViewController(which is delegate of UINavigationController) implement following delegate method.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[self.splitViewController.view setNeedsLayout];
self.splitViewController.delegate =Nil;
self.splitViewController.delegate = self;
[self.splitViewController willRotateToInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation duration:0];
}
3.Now implement delegate method of UISplitViewController in side lastViewController which is given below.
- (BOOL)splitViewController:(UISplitViewController *)svc shouldHideViewController:(UIViewController *)vc inOrientation:(UIInterfaceOrientation)orientation NS_AVAILABLE_IOS(5_0)
{
return hideMaster;<<===============I'm assuming hide master as a variable of shared object(Singleton).
}
4.Now change your - (IBAction)back:(id)sender method with following one.
- (IBAction)back:(id)sender {
hideMaster = Yes;
[self.navigationController popViewControllerAnimated:NO];
}
I hope this will work,if you stuck some where please let me know.
you are using UISplitViewController inside your app,since you have many views but whole app is not using UISplitViewController only one view using it.
Its better to use another approach.
Related
My iOS app has:
TabBarController
NavigationController1
TableView1
ViewController1 (Details View)
NavigationController2
TableView2
ViewController2 (Details View)
Behavior:
When the app loads, I see the TableView1.
I select an Item in the table, and it takes me via Show (Push) segue the details view 1.
I switch to the second tab on the bottom, and see TableView2.
I select an item and it takes me to details view 2
I navigate back to first tab, and see details view 1
Desired:
When performing last step, I'd like to dismiss the details view and see the first TableView1, and when switching back to second tab, I want that one to be dismissed and to see the table view.
I've tried different combinations of dismissViewControllerAnimated and popToRootViewControllerAnimated but I just don't seem to figure it out.
MainTabBarController.h
#interface MainTabBarController : UITabBarController <UITabBarControllerDelegate>
MainTabBarController.m
- (void)viewDidLoad {
[super viewDidLoad];
self.delegate = self;
}
...
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController{
// NSLog Works fine, and displays information in the output
NSLog (#"%# %lu", tabBarController.selectedViewController.title, tabBarController.selectedIndex);
// None of the lines below achieve the desired result
[viewController.navigationController popToRootViewControllerAnimated:YES];
[viewController dismissViewControllerAnimated:YES completion:nil];
[tabBarController.navigationController popToRootViewControllerAnimated:YES];
[tabBarController dismissViewControllerAnimated:YES completion:nil];
}
One option is to make use of the UITabBarControllerDelegate. Listen for changes to the tab selection. Based on the new tab, get the tab's navigation controller and call its popToRootViewControllerAnimated: method.
Update based on the code added to the question:
The problem is with how you try to pop the view controllers. You want this:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController{
// NSLog Works fine, and displays information in the output
NSLog (#"%# %lu", tabBarController.selectedViewController.title, tabBarController.selectedIndex);
// If the selected tab's root controller is a navigation controller
// pop to the top view controller in the tab's navigation stack
if ([viewController isKindOfClass:[UINavigationController class]]) {
UINavigationController *nav = (UINavigationController *)viewController;
[nav popToRootViewControllerAnimated:NO];
}
}
Here is a simple solution for this.
Try to implement the following methods of UIViewContorller
- (void)viewWillDisappear:(BOOL)animated; // Called when the view is dismissed, covered or otherwise hidden. Default does nothing
- (void)viewDidDisappear:(BOOL)animated; // Called after the view was dismissed, covered or otherwise hidden. Default does nothing
Go to your detail-1 view controller and implement the method - (void)viewWillDisappear:(BOOL)animated.
Do a pop for that controller.
Same you should do for the detail-2
Here is the code snippet that will help you.
In Appdelegate.m
#interface AppDelegate ()<UITabBarControllerDelegate>
#property(nonatomic, strong) MainTabBarController *rootTabBarController;
#end
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.rootTabBarController = [[MainTabBarController alloc]init];
self.rootTabBarController.delegate = self;
self.window.rootViewController = self.rootTabBarController;
[self.window makeKeyAndVisible];
}
TabBarController delegate implementation
-(void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
NSUInteger index = [self.rootTabBarController.viewControllers indexOfObject:viewController];
NSLog(#"Index : %lu", (unsigned long)index);
switch (index) {
case 0:
// pop other tab barcontrollers pushed or modal windows
[self.rootTabBarController flushViewControllerStackForIndex:1];
break;
case 1:
[self.rootTabBarController flushViewControllerStackForIndex:0];
break;
default:
break;
}
}
MainTabBarController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self setViewControllers:#[
[[UINavigationController alloc] initWithRootViewController:[[FirstViewController alloc]init]],
[[UINavigationController alloc] initWithRootViewController:[[SecondViewController alloc]init]]
] animated:YES];
}
-(void)flushViewControllerStackForIndex:(NSUInteger )index {
[[self.viewControllers objectAtIndex:index] popToRootViewControllerAnimated:NO];
}
Here is screenshot in sequence for the sample I ran.
Here is the Sample code.
That should solve your purpose & is the right approach.
Now you may need to fine tune your own logic in flushViewControllerStackForIndex to check if there is just only controller being pushed on stack or a combination of push & modal. So better try to navigate on the Stack & do-a-dismiss-if-a-modal or do-a-pop-if-a-push.
Hope that helps.
You can directly set the view controllers currently on the navigation stack. All you have to is directly set the viewControllers property of the navigation controllers when switch tabs in the tabbar controller.
Set NavigationController1.viewcontrollers = #[tableView1] when you switch to tab1
I am quite new to iPad Development, so please forgive my questions that might seem a little obvious.
I have an existing application for the iPhone which I am converting to a universal app for the iPad version.
The UI is going to be simple.
A Split View Controller where the Master represents a Static UITableView (Person, Timeline, Event, Date) for the user to select. The Detail will of course display whichever cell was selected in the UITableView Master. Both the Detail and Master have been created in Storyboard and are both NavigationControllers.
I am trying to do the common aspect of: Landscape mode shows both the Master and Detail View, while Portrait shows only the Detail View, but with a UIBarButtonItem that pops out the Master when requested.
Issue
When I rotate from Landscape to Portrait, the UIBarButtonItem is visible and I can press it to bring out the Master View in Portrait mode.
In portrait mode, if I bring out the button and select a different cell from the Master View, when I dismiss the Master by tapping anywhere in the Detail View, it doesn't now show the UIBarButton.
I am following http://www.dharmaworks.net/Consulting/switching-detail-views-in-uisplitviewcontroller-with-ios7/ as a way to get try get this working.
Code
MasterTableView:
#pragma mark - Split View Delegate
- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController
{
UINavigationController *navController = [[[self splitViewController ] viewControllers ] lastObject ];
id vc = [[navController viewControllers] firstObject];
[vc setSplitViewButton:barButtonItem forPopoverController:popoverController];
}
- (void)splitViewController:(UISplitViewController *)splitController willShowViewController:(UIViewController *)viewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
UINavigationController *navController = [[[self splitViewController ] viewControllers ] lastObject ];
id vc = [[navController viewControllers] firstObject];
[vc setSplitViewButton:nil forPopoverController:nil];
}
SplitViewPresenter Protocol
#property (nonatomic, strong) UIBarButtonItem *splitViewBarButtonItem;
-(void)setSplitViewButton:(UIBarButtonItem *)splitViewButton forPopoverController:(UIPopoverController *)popoverController;
Detail View
-(void) turnSplitViewButtonOn: (UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *) popoverController {
barButtonItem.title = NSLocalizedString(#"Master", #"Master");
_splitViewBarButtonItem = barButtonItem;
[self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES];
self.masterPopoverController = (MasterTableViewController *)popoverController;
}
-(void)turnSplitViewButtonOff
{
NSLog(#"SplitViewButtonOff Called");
// Called when the view is shown again in the split view, invalidating the button and popover controller.
[self.navigationItem setLeftBarButtonItem:nil animated:YES];
_splitViewBarButtonItem = nil;
self.masterPopoverController = nil;
}
-(void) setSplitViewButton:(UIBarButtonItem *)splitViewButton forPopoverController:(UIPopoverController *)popoverController
{
NSLog(#"Split View Being Called");
if (splitViewButton != _splitViewBarButtonItem) {
if (splitViewButton) {
NSLog(#"Split View Button Being Called");
[self turnSplitViewButtonOn:splitViewButton forPopoverController:popoverController];
} else {
[self turnSplitViewButtonOff];
NSLog(#"Split View Button Not Being Called");
}
}
}
The .h file of the Detail View is declared to use the SplitViewPresenter.
Update
I understand why this is occurring. The setter is only getting called when I rotate from the Landscape to Portrait mode. My question is, how do I get this to work without the rotation? Also, within the link above that I have followed, I have not implemented any of the didSelectRow code. Should I be doing that?
Any guidance on this would really be appreciated.
I made masterdetailview with different viewcontrollers in my storyboard and linked every one with push segue. Now when I click on list item new DetailViewController opens, but every single one have no navigationbar. I added navigation item on top off every View, added title for every single one, but after all that navigation bar is not showing. I am working on iPad app and in both orientations navigation bar is missing.
EDIT:
My appDelegate is same as template when you create your Master-Detail project:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
splitViewController.delegate = (id)navigationController.topViewController;
}
return YES;
}
One of my DetailViewControllers:
#import "MediaExpDetailViewController.h"
#interface MediaExpDetailViewController ()
#property (strong, nonatomic) UIPopoverController *masterPopoverController;
#end
#implementation MediaExpDetailViewController
#pragma mark - Managing the detail item
- (void)setDetailItem:(id)newDetailItem
{
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
}
if (self.masterPopoverController != nil) {
[self.masterPopoverController dismissPopoverAnimated:YES];
}
}
- (void)viewDidLoad{
[super viewDidLoad];
}
#pragma mark - Split view
- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController
{
barButtonItem.title = NSLocalizedString(#"MasterButton", #"Master");
[self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES];
self.masterPopoverController = popoverController;
}
- (void)splitViewController:(UISplitViewController *)splitController willShowViewController:(UIViewController *)viewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
// Called when the view is shown again in the split view, invalidating the button and popover controller.
[self.navigationItem setLeftBarButtonItem:nil animated:YES];
self.masterPopoverController = nil;
}
#end
Your DetailViewController has to be connected to a UINavigationController. If you set up a new master-detail-project from Xcode's "New Project" menu you will get this:
Notice: Every Detail view controller has to be connected to a UINavigationController (directly or indirectly).
Edit: To connect multiple UINavigationControllers, do the following:
Your initial detail views UINavigationController is connected with the split view (as usual). Every others detail views UINavigationController is connected with the master view (with a replace segue):
If you want to give the user the possibility to switch back to the initial detail view, you also have to connect the initial view controller with the master (thus having two connections, one to the split, one to the master).
To switch between detail view controllers, you call performSegueWithIdentifier:sender: in your master view controller.
Edit 2:
To add your detail views as the split view's delegate implement the following method, which gets called earlier than viewDidLoad
- (void)awakeFromNib
{
self.splitViewController.delegate = self;
}
I have now been stuck on this problem for more then 2 weeks! In my project, I have 1 single ViewController(slide) I want to enable both landscape and portrait in. The rest of all controllers/views(slides) I want to enable portrait-mode only.
The tricky part is, the "ViewController" I am referring to is connected to both NavigationControllers and TabBarControllers. See the scheme below where the ViewController I want to enable both landscape/portrait is named: ReferredViewController.
TabBarController ----> NavigationController ----> FristViewController --(push event)--> ReferredViewController
So far I have tried to make a CATEGORY for both NavigationControllers and TabBarControllers. But since my NavigationControllers and TabBarControllers are placed at the very start of the project this will set the rules for the whole project. My ReferredViewController is placed at the end or in the middle of the projects "storyboard". I have tried to set the rules by code aswell for the single ReferredViewController without any success.
My best shot is to change the event between FirstViewController and ReferredViewController from "push" to "modal". ReferredViewController can then rotate both portrait/landscape and the rest of the project is locked in portrait. BUT, as you may know all navigations (NavigationBar) will be lost and the user will become stuck at that single slide.
So I am trying to enable the NavigationBar with the following code example in the ReferredViewController.m file:
ShowTaskViewController *detailViewController = [[ShowTaskViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
navController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self.navigationController presentModalViewController:navController animated:YES completion:nil];
[navController release];
[detailViewController release];
But ofc nothing happens and I am back to square one again :O. FML!
In this line:
[self.navigationController presentModalViewController:navController
animated:YES
completion:nil];
you are conflating two UIViewController instance methods:
- (void)presentViewController:(UIViewController *)viewControllerToPresent
animated:(BOOL)flag
completion:(void (^)(void))completion
- (void)presentModalViewController:(UIViewController *)modalViewController
animated:(BOOL)animated
The first of these is now the standard, the second method was deprecated in ios6.
Also the presenting view controller should be self (the ReferredViewController), not self's navigationController.
Your presented view controller can dismiss itself thus
[[self presentingViewController] dismissViewControllerAnimated:YES
completion:(void (^)(void))completion];
But take a look at fibnochi's answer, it may be a better way for you to achieve your result.
you have to over ride UITaBarController because it is you base view controller. I have done this for my navigation controller. Tell me if this helped.
#interface UINavigationController (Autorotation)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation;
- (BOOL) shouldAutorotate;
- (NSUInteger) supportedInterfaceOrientations;
#end
#implementation UINavigationController (Autorotation)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
if ([self.visibleViewController isKindOfClass:[MWPhotoBrowser class]] || [self.visibleViewController isKindOfClass:[ZoomPictureViewController class]]) {
return YES;
}
return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
}
-(BOOL) shouldAutorotate{
return YES;
}
-(NSUInteger) supportedInterfaceOrientations{
if ([self.visibleViewController isKindOfClass:[MWPhotoBrowser class]] || [self.visibleViewController isKindOfClass:[ZoomPictureViewController class]]) {
return UIInterfaceOrientationMaskAll;
}
return UIInterfaceOrientationMaskPortrait;
}
I have 3 UIViewControllers in my UINavigationController.
At some points I want to go to rootViewController and from there navigate to a new UIViewController, and it doesn't aeem to work.
Any suggestions?
- (IBAction)goToRootAndNavigateToViewController
{
[self.navigationController popToRootViewControllerAnimated:YES];
MyViewController *mvc = [[MyViewController alloc] init];
[self.navigationController pushViewController:mvc animated:YES];
[mvc release];
//This takes me to the rootViewController but it doesn't navigate to MyViewController
}
Trying to use performSelector:WithDelay:
- (void)goToRootAndNavigateToViewController
{
[self.navigationController popToRootViewControllerAnimated:YES];
[self performSelector:#selector(doSomething) withObject:nil afterDelay:10];
}
- (void)doSomething
{
MyViewController *mvc = [[MyViewController alloc] init];
[self.navigationController pushViewController:mvc animated:YES];
[mvc release];
}
I believe the popToRootViewController takes up the full NSRunLoop.
You would need to push your next view controller with a separate function using something like performSelector:withObject:afterDelay.
or you could always just do a [self.navigationController setViewControllers:] call to set them manually
You need to push your mvc controller when the animation is complete. Try calling it once the first animation is done (e.g. in - (void)viewDidAppear:(BOOL)animated)
I don't know why you are facing this problem, but one solution you could try is to push the new view controller in your root view controller's -viewDidAppear: method.
I guess it has to do with your current viewcontroller using - (IBAction)goToRootAndNavigateToViewController, is somehow losing its control once being popped. Thus, making consecutive statements to be not working.
If I were you, I would make sure pushing MyViewController instance is done always at rootViewController of your choice, not from the current viewcontroller which is going to be popped from UINavigationController and possibly to be released and deallocated.
Probably, you may want to add delegate method implementation for, such as - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated in your rootViewController
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
// Check the right condition for pushing MyViewController...
// if it's YES...
MyViewController *mvc = [[MyViewController alloc] init];
[self.navigationController pushViewController:mvc animated:YES];
[mvc release];
}
In this implementation, you may push MyViewController instance. One thing you have to do beforehand is using some kind of conditional flag, which will make sure the situation is correct for popToRootViewController then push MyViewController