UIViewController loading a UISplitViewController - objective-c

I have a UINavigationController as my rootViewController, which contains a UIViewController (which I will call projects for the sake of this discussion). In Projects, I have a button, which when clicked, I want to load a UISplitViewController - preferably sliding up from the bottom, although this is just a nice-to-have feature.
In the UISplitViewController, I have a "Close" button which I want to remove the UISplitViewController re-showing Projects.
From what I have read, UISPlitViewControllers must be the rootViewControllers. With that in mind, my code so far is as follows.
AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
...
// LOAD THE PROJECTS PANEL ROOT VIEW CONTROLLER INTO THE WINDOW
ProjectsListViewController *projects = [[ProjectsListViewController alloc] init];
navigationController = [[UINavigationController alloc] initWithRootViewController:projects];
[window setRootViewController:navigationController];
[window makeKeyAndVisible];
return YES;
}
ProjectsViewController
-(IBAction)loadDetails
{
ProjectNavigationController *projectNavPanel = [[ProjectNavigationController alloc] init];
ProjectDetailController *projectDetailPanel = [[ProjectDetailController alloc] init];
ProjectSplitViewController *splitRootController = [[ProjectSplitViewController alloc] init];
[splitRootController setViewControllers:[NSArray arrayWithObjects:projectNavPanel, projectDetailPanel, nil]];
[[self view] removeFromSuperview];
[[appDelegate window] setRootViewController:splitRootController];
}
UISplitViewController Naviagaion
- (void)loadProjects
{
// LOAD THE PROJECTS LIST BACK INTO VIEW
ProjectsListViewController *projectsList = [[ProjectsListViewController alloc] init];
[[[self parentViewController] view] removeFromSuperview];
[[appDelegate window] setRootViewController:projectsList];
}
Now, I know this is wrong, and unsurprisingly it is having adverse effects on other methods. In fact, as I type this, I noticed that the Projects page is being loaded in a navigationController on launch, but placed directly on the window when the splitViewController is closed. Can anyone help me by explaining the correct method of achieving this?
Thanks

Apple say that a UISplitViewController must be the topmost view controller in your app, and that it must be there for the entire lifetime of your app. As you've noticed, if you ignore this, everything can break.
There are some alternatives out there that don't break this way, e.g. MGSplitViewController. Google around. If you have the time, you could even cook your own implementation of a split view controller and be in complete control.
If you really want to use Apple's UIsplitViewController in "crazy" ways, then you can install it as root VC (as Apple demand), and have it at the root all the time, but then show other UIs modally over the top of it. Then hide the modal UI to make the split view controller appear. This is nasty and hacky though.
A while back I asked a related question that may be of interest:
Best way to switch between UISplitViewController and other view controllers?

Related

Can I Show A SplitViewController Using presentModalViewController?

I don't know if what I'm trying to do is possible, but because I haven't the desired results, I guess not.
What I'm trying and need to do is to call a SplitViewController from a previous ViewController, using presentViewController.
I know, SplitViewController have to be the rootViewController, but I need to explore the most possible options to achieve what I need to do.
I have a MainMenu with buttons, and with every button, I need to call a SplitViewController. First, how can do this?
What I'm trying to do is this:
First, in AppDelegate I'm calling the MainMenu, and add as a subview and other things:
-(BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window addSubview:self.mainMenu.view];
[self.mainMenu presentModalViewController:self.firstMenu animated:NO];
[self.window makeKeyAndVisible];
return YES;
}
Then, in the MainMenu, I'm calling SecondViewController, in modal view, using this code:
SecondViewController *secV = [[SecondViewController alloc]initWithNibName:#"SecondViewController" bundle:nil];
secV.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:secV animated:YES];
In this SecondViewController, I'm creating SplitViewController, with Master & DetailViewController's, using this code:
-(void)viewDidLoad{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
UISplitViewController *splitViewController = [[UISplitViewController alloc]init];
SecondMenuViewController *secMenu = [[SecondMenuViewController alloc]init];
UINavigationController *navLef = [[UINavigationController alloc]init];
[navLef pushViewController:secMenu animated:NO];
SecondMainViewController *secMain = [[SecondMainViewController alloc]init];
UINavigationController *navRig = [[UINavigationController alloc]init];
[navRig pushViewController:secMain animated:NO];
splitViewController.delegate = secMain;
splitViewController.viewControllers = [NSArray arrayWithObjects:navLef, navRig, nil];
MainAppDelegate *mainApp = [[MainAppDelegate alloc]init];
[mainApp changeRootViewController:splitViewController];
navRig = nil;
navLef = nil;
secMain = nil;
secMenu = nil;
splitViewController = nil;
}
As you can see, I'm calling a method in MainAppDelegate, to change view and RootViewController, because SplitViewController have to be RootViewController. This is the method:
-(void)changeRootViewController:(UISplitViewController *)splitViewController{
[self.window addSubview:splitViewController.view];
self.window.rootViewController = splitViewController;
}
I know, this looks like a mess. And when I run, the SplitViewController never shows, so I assume, what I'm trying to do is not possible? Or In what I'm wrong?
If it is everything, what can I do to show a SplitViewController after my MainViewController?
I'm using XCode4.4 and iOS5
Thank you very much
A better way would be to make your UISplitViewController the root view controller in application:didFinishLaunchingWithOptions:. Then present your MainMenu on top of it. You can change the subviews displayed by the split view controller to correspond to what button the user pushes in your MainMenu.
First, didFinishLaunchingWithOptions: is too early to be calling presentModalViewController. You haven't even got an interface yet!
Second, you don't seem to have a root view controller (although perhaps you're getting one from a nib? you should probably stop doing that; use the techniques shown in the current application templates).
Third, note that now that we have custom container views, there is no need for you to use UISplitViewController at all; you can construct your own view / view controller hierarchy, and you might be happier doing so, since UISplitViewController is not a very well-constructed class.

UINavigationController — left and right flip animation between pushes and pops when presented via presentViewController

Supposed you've got:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
UIViewController *rootViewController = [[UIViewController alloc] init];
rootViewController.view.backgroundColor = [UIColor whiteColor];
[self.window setRootViewController:rootViewController];
[self.window makeKeyAndVisible];
UINavigationController *modal = [[UINavigationController alloc] initWithRootViewController:[[PTFrontViewController alloc] init]];
modal.modalPresentationStyle = UIModalPresentationFormSheet;
[rootViewController presentViewController:modal animated:YES completion:NULL];
return YES;
}
whereas PTFrontViewController and PTBackViewController view controllers have nothing interesting for sake of this example.
How could you push an instance of PTBackViewController from PTFrontViewController animating as in UIViewAnimationTransitionFlipFromLeft or UIViewAnimationTransitionFlipFromRight?
I am already well aware of these three things:
this is not exactly how you should make use of presentViewController
there is a good reason for UINavigationController's default animation
there are several answers how to "customize" UINavigationController's default animation while pushing and poping, but if you try the code for your self you will notice that when a view controller is presented via presentViewController there are drop shadows and background views that won't get animated correctly
So please answer taking these things in mind. Thank you.
First - forget UINavigationController. If you don't need the default animation, just put a UINavigationBar into your controllers. It will get a little easier.
Second - this is a difficult problem, you can't create such an animation only within the modal controller because the background wouldn't be repainted.
Sincerely, the easist solution I see is too forget the modal controller and just add the view controller as a child of your root controller. Then you can control all the animations but you have to write everything by yourself (including the background fading).

UITableView partially hidden by UITabBar

I've got a UITabBarController which contains a UINavigationController. Within the visible UIViewController, I'm creating a UITableView programatically as follows:
self.voucherTableView = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame] style:UITableViewStylePlain];
self.voucherTableView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
However, the UITabBar is overlapping the UITableView.
When I output the height of the [[UIScreen mainScreen] applicationFrame], it returns 460.00 whereas it should be 367.00.
In Interface Builder, I'm using the 'Simulated Metrics' which automatically sets the height of the view to 367.00.
Is there something I'm missing, no matter what I try I can't see to get the 367.00 height that I need.
As a temp fix, I've set the frame of the UITableView manually, this isn't really ideal so it would be nice to work out why this isn't working:
self.voucherTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 367) style:UITableViewStylePlain];
You should use self.view.bounds rather than [[UIScreen mainScreen] applicationFrame] as the last one returns you the whole screen frame while self.view.bounds provides you with your view bounds wich seems what you are searching for.
You should add the UINavigationController instance to the UITabBarController and then add a table view controller to the rootViewController property of the UINavigationController instance which should make your life a lot easier.
As a simple example of this, create an empty window-based application (the templates make this a lot more confusing than it really is).
Add your UIViewController/UITableViewController subclasses to the project then use this code as a guide to setting up your project. This code is in your AppDelegate class:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// create our table view controller that will display our store list
StoresViewController *storeListController = [[StoresViewController alloc] init];
// create the navigation controller that will hold our store list and detail view controllers and set the store list as the root view controller
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:storeListController];
[navController.tabBarItem setTitle:#"TableView"];
[navController.tabBarItem setImage:[UIImage imageNamed:#"cart.png"]];
// create our browser view controller
BrowserViewController *webBrowserController = [[BrowserViewController alloc] init];
[webBrowserController.tabBarItem setTitle:#"WebView"];
[webBrowserController.tabBarItem setImage:[UIImage imageNamed:#"web.png"]];
// add our view controllers to an array, which will retain them
NSArray *viewControllers = [NSArray arrayWithObjects:navController, webBrowserController, nil];
// release these since they are now retained
[navController release];
[storeListController release];
[webBrowserController release];
// add our array of controllers to the tab bar controller
UITabBarController *tabBarController = [[UITabBarController alloc] init];
[tabBarController setViewControllers:viewControllers];
// set the tab bar controller as our root view controller
[self.window setRootViewController:tabBarController];
// we can release this now since the window is retaining it
[tabBarController release];
[self.window makeKeyAndVisible];
return YES;
}
In the code sample above the BrowserViewController is a subclass of UIViewController and the StoresViewController class is a subclass of UITableViewController. The UITabBarController and UINavigationController instances are created programmatically and added to the window.
By subclassing the UITableViewController class you avoid having to create a UITableView instance programmatically and get most everything you need out of the box.
When you need to push a detail view onto the UINavigationController instance's stack, you just have use something similar to this:
[self.navigationController pushViewController:YourDetailViewControllerInstance animated:YES];
This will add the detail view UIViewController subclass to the UINavigationController instance's view hierarchy for you and animate the transition.
Lots of controllers in this, but it's totally worth it and will avoid a lot of the problems you're experiencing as this method allows the views to manage resizing and take toolbars/navigation bars into account all by themselves.

ios4 sdk ipad - Can we put two VIEWS inside window?

I want to place two views side by side like they did in the MAIL app for the ipad. Is this possible with UINavigationController?
I would like to place two UINavigationController side by side
Dont worry about the syntax I just want to know if it is possible
UINavigationConroller *left;
UINavigationController *right;
[WIndow addSubView: left];
[WIndow addSubView:right];
UIWindow inherits directly from UIView, so it is possible to add multiple objects to it, though if you add multiple view controllers then only one of them will receive rotation events.
The correct means to do a Mail-type presentation is to use a UISplitViewController, which automatically handles the transition between two view and one view mode. The Xcode template for a 'Split View-based Application' will even set you up so that you have an button to view the left tableview as a UIPopoverController if you're in portrait.
EDIT: example code, with a split view controller:
// a tiny little method to vend a new navigation controller; following Cocoa patterns, because it
// has 'new' in the name it vends an owning reference (ie, not autoreleased, retain count +1)
- (UINavigationController *)newController
{
UITabBarController *tabBar = [[UITabBarController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:tabBar];
[tabBar release];
return navController;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// generate a split view controller
UISplitViewController *splitView = [[UISplitViewController alloc] init];
// grab a couple of navigation controllers
UINavigationController *navController1 = [self newController];
UINavigationController *navController2 = [self newController];
// add the navigation controllers to the split view controller
splitView.viewControllers = [NSArray arrayWithObjects:navController1, navController2, nil];
[navController1 release];
[navController2 release];
// and put the whole thing on screen
[window addSubview:splitView.view];
[window makeKeyAndVisible];
return YES;
}

UISplitViewController programmatically without nib/xib

I usually create my projects without IB-stuff. The first thing I do is to strip off all references to xibs, outlets updated plist, etc and so forth. No problems, works great (in my world)!
Now, I just installed 3.2 and tried to develop my first iPad app. Following same procedure as before, I created a UISplitView-based application project and stripped off all IB-stuff. Also, I followed the section in Apple's reference docs: Creating a Split View Controller Programmatically, but nevertheless, the Master-view is never shown, only the Detail-view is (no matter what the orientation is). I really have tried to carefully look this through but I cannot understand what I have missed.
Is there a working example of a UISplitViewController without the nibs floating around somewhere? I have googled but could not find any. Or do you know what I probably have missed?
Declare your splitviewcontroller in your delegate header, use something like this in your didfinishlaunching
ensure you add the UISplitViewControllerDelegate to the detailedViewController header file and that you have the delegate methods aswell. remember to import relevant header files
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
splitViewController = [[UISplitViewController alloc] init];
rootViewController *root = [[rootViewController alloc] init];
detailedViewController *detail = [[detailedViewController alloc] init];
UINavigationController *rootNav = [[UINavigationController alloc] initWithRootViewController:root];
UINavigationController *detailNav = [[UINavigationController alloc] initWithRootViewController:detail];
splitViewController.viewControllers = [NSArray arrayWithObjects:rootNav, detailNav, nil];
splitViewController.delegate = detail;
[window addSubview:splitViewController.view];
EDIT - as per Scott's excellent suggestion below, don't add to the windows subview, instead
[self.window setRootViewController:(UIViewController*)splitViewController]; // that's the ticket
[window makeKeyAndVisible];
return YES;
}
//detailedView delegate methods
- (void)splitViewController:(UISplitViewController*)svc
willHideViewController:(UIViewController *)aViewController
withBarButtonItem:(UIBarButtonItem*)barButtonItem
forPopoverController:(UIPopoverController*)pc
{
[barButtonItem setTitle:#"your title"];
self.navigationItem.leftBarButtonItem = barButtonItem;
}
- (void)splitViewController:(UISplitViewController*)svc
willShowViewController:(UIViewController *)aViewController
invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
self.navigationItem.leftBarButtonItem = nil;
}
I also prefer code to IB ;-)
Oldish thread, but thought I'd spare reader time + grief when the above technique fails to produce a UISplitViewController that responds correctly to device orientation change events. You'll need to:
Ensure all subordinate views respond properly in
shouldAutorotateToInterfaceOrientation. Nothing new here.
Rather than add the UISplitViewController's view to the main window,
[window addSubview:splitViewController.view]; // don't do this
instead set the main window's root controller to the UISplitViewController:
[self.window setRootViewController:(UIViewController*)splitViewController]; // that's the ticket
Adding the splitviewcontroller's view as a subview of the main window (barely) allows it to co-present with sibling views, but it doesn't fly with UISplitViewController's intended use case. A UISplitViewController is a highlander view; there can only be one.
Swift 5.2
iOS 13
Both master and detail view controllers are embedded in navigation controllers
let splitViewController = UISplitViewController()
splitViewController.delegate = self
let masterVC = MasterViewController()
let detailVC = DetailViewController()
let masterNavController = UINavigationController(rootViewController: masterVC)
let detailNavController = UINavigationController(rootViewController: detailVC)
splitViewController.viewControllers = [masterNavController,detailNavController]
You can put this code in your AppDelegate's (or in SceneDelegate if your target is iOS 13.0+)didFinishLaunchingWithOptions function. Just remember to make the splitViewController your rootViewController like this
self.window!.rootViewController = splitViewController
I had just met the same problem.
make sure that your child viewController of splitview can Autorotate to interface orientation.
you can change the function in your childViewController like this:
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
then the master view will be shown.