UISplitViewController is not being displayed correctly - ios

I created and loaded a UISplitViewController in an existing ViewController by writing the following code in the viewDidLoad method:
LeftPanelViewController *leftPanel = [[LeftPanelViewController alloc] initWithNibName:#"LeftPanelViewController" bundle:nil];
FirstViewController *firstView = [[FirstViewController alloc] initWithNibName:#"FirstViewController_iPad" bundle:nil];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:firstView];
UISplitViewController *splitController = [[UISplitViewController alloc] init];
splitController.viewControllers = [NSArray arrayWithObjects:leftPanel, self.navigationController, nil];
[self.view addSubview:splitController.view];
[self addChildViewController:splitController];
[splitController didMoveToParentViewController:self];
Everything is fine except for the fact that the splitController is not being drawn in the borders of the iPad, there's a space between the top of the screen and the top of the view. Even when I rotate the iPad the left panel is also having the same problem.
SplitViewController doesn't have a xib file, and when I change the added view for another that has, everything is correctly displayed.
Any ideas of what may cause this problem?
Notes:
Compiling and running the project in the simulator multiple times causes the SplitViewController to be displayed sometimes without any problems and others with spaces between any of the edges of the screen and the view. Running in the iPad causes always the same problem.

First of all.. why do you implement a container view controller? I guess you just want to present the splitViewController on its own, right? Than don't add the view yourself.
Instead correctly set it as your rootViewController on your window (preferably in applicationDidFinishLaunching).
self.window.rootViewController = splitViewController;
Container View Controller are not needed in standard cases. So you should never need to use the following methods:
addChildViewController:
removeFromParentViewController
willMoveToParentViewController:
didMoveToParentViewController:
Check the documentation of UIViewController.
If you really wanted to implement a Container View Controller, than you need to take care of the layout yourself. So you need to position / size the view of the other controller yourself. Depending on if you use AutoLayout or autoresizing, you need to set correct constraints/flags.

Related

Strange autolayout issue since ios 11

I have a UITableViewController which I present modally (full screen) on iPad.
Since ios11 (and never before) I've got a lots of strange display problem where the UITableViewController is apparently, at some point, unable to properly compute its heights, and when I click on a cell actions for another index path is triggered. The controller contains cells which is defined in a xib and the problem disappears if I replace this cell by a vanilla UITableViewCell. On the other hand, fixing the height of the cell from heightForRowAtIndexPath does not resolve the issue.
Now the strange thing is, if this TableViewController is not presented within a new NavigationController but is simply pushed from the current view controller, everything works (almost) fine.
I am presenting my controller like that:
MyTableViewController *tvc = [[MyTableViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:tvc];
[self.navigationController presentViewController:navController animated:YES completion:nil];
Is there anything obviously wrong with the above or that could explain what I am getting ?

UISplitViewController detailview changing result is gray area

I am trying to replace the detailview of a UISplitViewController for a quite a while now, but the solutions I found on the internet wasn't useful.
I am executing this:
DetailViewController* secondVc = [[DetailViewController alloc] init];
NSMutableArray* arr = [[NSMutableArray alloc] initWithArray:self.splitViewController.viewControllers];
[arr replaceObjectAtIndex:1 withObject:secondVc];
[self.splitViewController setViewControllers:arr];
DetailViewController is just a normal UIViewController (is this the problem?) I chose red as its background but I am seeing a completely gray area in the detail view after this code is executed.
What surprises me is that viewDidLoad and viewDidAppear functions are called for the DetailView class, but I can't see it on the screen. The self.view.frame is 0,0,768,1024 although all my settings are in landscape mode in storyboard.
I only want to use this in landscape mode, I don't need a generic solution.
What is the most basic way to change the detail view of a split view controller? I have looked at Apple's MultipleDetailViews but that felt like overkill since most of the code in it is about responding orientation changes, like hiding the master vc etc.
I suspect your problem is using alloc init to instantiate secondVC -- that would work if you made your controller's view in code, or in a xib with the name "DetailViewController". Since it appears that you're using a storyboard, then you should be using,
DetailViewController* secondVc = [self.storyboard instantiateViewControllerWithIdentifier:#"secondVC"]; // be sure to use this same identifier in the storyboard
In storyboard, select the view controller. On the right side, go to "Simulated Metrics" and pick "Detail" for "Size". As for the color, try setting it in viewDidLoad.

pushViewController does not cause new controller to draw view

Preface: I am not using *.xib files.
I instantiate a UINavigationController in a class that effectively serves as my 'rootViewController'. This 'rootViewController' also has two UITableViewController members that are drawn on different sections of the iPad screen. One of which is set as the root view for the navigation controller. Let's call it tableViewControllerA.
The problem is, when I invoke pushViewController on a valid UINavigationController, I see no effect:
[tableViewControllerA.navigationController pushViewController:tableViewControllerX animated:YES];
I've gathered from the posts I've searched today, that this push method should in turn cause the screen to redraw the top of stack controller.view. This is not what I'm seeing.
It seemed there was a disconnect in my implementation, and it was time to reference a working example in my environment (xcode 4.0). Assuming the canned templates would provide a working basis, I created a new navigation-based applications. I simply modified didFinishLaunchingWithOptions: as follows.
UIViewController *view1 = [[UIViewController alloc] init];
UIViewController *view2 = [[UIViewController alloc] init];
view1.title = #"view1";
view2.title = #"view2";
[self.navigationController pushViewController:view1 animated:YES];
[self.navigationController pushViewController:view2 animated:YES];
self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:view1];
[view1 release];
[view2 release];
I found similar results. When I launch the simulator the screen title reads the title of whatever the self.window.rootViewController is pointing at. With the code as is, the title of the resulting top screen reads "view1". When I initWithRootViewController:view2, the resulting top screen reads "view2".
So please tell me I'm stupid cuz xyz...
Thanks.
Here are some references and suggestions:
Simple tutorial for navigation based application:
http://humblecoder.blogspot.com/2009/04/iphone-tutorial-navigation-controller.html
Here is another one to create the step by step navigation controller and adding the views:
http://www.icodeblog.com/2008/08/03/iphone-programming-tutorial-transitioning-between-views/
and here a bit advance with navigation + tab bar controller:
http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/CombiningToolbarandNavigationControllers/CombiningToolbarandNavigationControllers.html
Without seeing your code, I have 2 theories:
Your syntax and calls are wrong when you do the push. Use this as a model:
-(void)Examplemethod {
AnotherClassViewController *viewController = [[[AnotherClassViewController alloc] initWithNibName:#"AnotherClassView" bundle:nil] autorelease];
[self.navigationController pushViewController:viewController animated:YES];
}
You are never adding the navigation controller to the view hierarchy which never adds the view either. Take a look at this.

Losing rotation support after programmatically adding UITabBarController to UIWindow

My application starts off with nothing but a UIWindow. I programmatically add a view controller to self.window in application:didFinishLaunchingWithOptions:.
myViewController = [[UIViewController alloc] init:...];
...
[self.window addSubview:myViewController.view];
[self.window makeKeyAndVisible];
At the same time i kick off a background process:
[NSThread detachNewThreadSelector:#selector(startupOperations) toTarget:self withObject:nil];
The startupOperations look something like this:
NSAutoreleasePool *threadPool = [[NSAutoreleasePool alloc] init];
// Load data
...
// When your done, call method on the main thread
[self performSelectorOnMainThread:#selector(showMainViewController) withObject:nil waitUntilDone:false];
// Release autorelease pool
[threadPool release];
showMainViewController removes myViewController, creates a UITabBarController and sets it as the window's main view:
[self.myViewController.view removeFromSuperview];
self.myViewController = nil;
tabBarController = [[UITabBarController alloc] init];
...
[self.window addSubview:tabBarController.view];
[self.window makeKeyAndVisible];
Questions:
All the view controllers are returning YES for shouldAutorotateToInterfaceOrientation:. Rotation works fine for myViewController but as soon as the tabBarController is made visible, rotation stops working and interface appears in Portrait. What's the reason behind this behavior?
Also, in iOS 4.x, UIWindow has rootViewController property. What's the role of this property? The new templates use rootViewController instead of [self.window addSubview:...]. Why is that?
Pretty strange. I tried and simulate your "view flow" in a simple tab bar based project and autorotation effectively works after removing the initial controller and adding the tab bar controller's view as a subview.
The only condition I found where it did not work is when self.window did contain a second subview that I did not remove. Could you check at the moment when you execute
[self.window addSubview:tabBarController.view];
what is self.window.subview content?
If that does not help, could you share in your question how you initialize the UITabBarController and UITabBar?
As to your second question, as you say rootViewController is the root controller for all the views that belong to the window:
The root view controller provides the content view of the window. Assigning a view controller to this property (either programmatically or using Interface Builder) installs the view controller’s view as the content view of the window. If the window has an existing view hierarchy, the old views are removed before the new ones are installed.
(Source)
You can also use that, but take care of assigning it already in applicationDidFinishLaunching, otherwise, if you "manually" add a subview and later change this property, it will not remove the subview you explicitly added.

How to use a UISplitViewController as a Modal View Controller?

I am trying to display a UISplitViewController presenting it as a Modal View Controller in my iPad app. I manage to have it display, but for some reason there is a gap to the left of the modal view the size of a Status Bar which is also preserved when the orientation is changed.
Does anybody know why this is happening? Or if this is even possible? Maybe I'm just digging myself a huge hole.
Like for many of you, I needed a 'modal way' to use the UISplitViewController. This seems to be an old issue, but all I found in StackOverflow was at best an explanation why the problem happens when you attempt to do so (like the accepted answer above), or 'hack-arounds'.
However, sometimes it is also not very convenient to change much of your code-base and make a UISplitViewController the initial object just to get it's functionality up and running.
In turns out, there's a way to make everybody happy (including Apple guidelines). The solution that I found best, was to use the UISplitViewController normally, but when needed to be shown/dismissed, use the following approach:
-(void)presentWithMasterViewController: (UIViewController *) thisMasterViewController
andDetailViewController: (UIViewController *) thisDetailViewController
completion:(void(^)(void))completion
{
masterViewController = thisMasterViewController;
detailViewController = thisDetailViewController;
[self setViewControllers:[NSArray arrayWithObjects:masterViewController, detailViewController, nil]];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
self.window.rootViewController = self;
[self.window makeKeyAndVisible];
if(completion)
completion();
}
-(void)dismissViewControllerWithCompletion:(void (^)(void))completion
{
self.window = nil;
masterViewController = nil;
detailViewController = nil;
if(completion)
completion();
}
Where "window", is a property of your UISplitViewController subclass. And the system will take care of the rest!
For convenience/reference, I uploaded this as a UISplitViewController subclass to gitHub:
ModalSplitViewController
--EXAMPLE ON HOW TO USE --
mySplitViewController = [[ModalSplitViewController alloc] init];
mySplitViewController.delegate = self;
[mySplitViewController presentWithMasterViewController:masterViewController andDetailViewController:detailViewController completion:nil];
// when done:
[mySplitViewController dismissViewControllerWithCompletion:nil];
mySplitViewController = nil;
Side-note: I guess most of the confusion originates from the fact that
the UISplitView usage example from Apple documentation uses the window
created in the appDelegate, and for the fact that most people are not
so familiar with the window concept - because we normally don't need
to (they are created once in StoryBoards or boilerplate code).
Additionally, if you are doing state restoration, one should not
forget that programmatically-created UIViewControllers won't
automatically be restored by the system.
The stock UISplitViewController was designed for use as the root view controller only. Presenting one modally goes against the Apple Human Interface Guidelines and has a high probability of getting rejected by the App Review Team. In addition, you may receive the error:
Application tried to present Split View Controllers modally
Technically, this is what I did:
1/ Subclass a UIViewController ie. #interface aVC: UIViewController
2/ In the viewDidLoad, set up a splitViewController, ie. aSplitVC
3/ Then self.view = aSplitVC.view
After all, present aVC as modalViewController
I agree with Evan that this is slightly off-color for Apple, but I was able to complete a working version of this with the following solution:
UISplitViewController *splitVC = [[UISplitViewController alloc] init];
splitVC.delegate = VC2;
splitVC.viewControllers = [NSArray arrayWithObjects:navcon1, navcon2, nil];
UINavigationController *splitNavCon = [[UINavigationController alloc] init];
splitNavCon.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[splitNavCon.view addSubview:splitVC.view];
VC2.splitParentViewController = splitNavCon;
[self presentViewController:splitNavCon animated:YES completion:nil];
This allowed me to have a working back button in the new UISplitViewController that was presented modally on the screen.
You'll notice that I actually pass the VC2 (the delegate of the UISplitViewController) its parent UINavigationController. This was the best way that I found I could dismiss the UISplitViewController from within the VC2:
[splitParentViewController dismissViewControllerAnimated:YES completion:nil];
I believe one can do the other way around: instead of custom controller presenting split controller, one can set up the split controller as the root window controller in storyboard, and on top of its view you can add your custom controller (ie, login screen) and remove it from the screen (removeFromSuperview for example) when it is needed.
That answer is not actually correct, because it not valid any longer since iOS8 and if you need to support even iOS7 you can do that like you put actually modally UIViewController which has a container as SplitView.
let mdSplitView = self.storyboard?.instantiateViewControllerWithIdentifier("myDataSplitView") as! MyData_SplitVC
self.addChildViewController(mdSplitView)
mdSplitView.view.bounds = self.view.bounds
self.view.addSubview(mdSplitView.view)
mdSplitView.didMoveToParentViewController(self)

Resources