I am doing an iPad app based on a UISplitViewController. I have a little problem with the toobar button when my app launched in potrait. The button to show the popover is not displayed.
However when I rotate my iPad into landscape and then back to portrait, the button shows !
It looks like the following method is not called on launch (this is were I have the code showing the button):
- (void)splitViewController:(UISplitViewController *)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController: (UIPopoverController *)pc
This method is not called when the app launches but only when there is a rotation. What is even stranger is that I made a test app using Xcode UISplitViewController template + core data (which is similar to the app I am working on, and is the template I used to make this app). On the test app on which I have not made a single line of code, the button shows when I launch my app in portrait mode and the method above is also called upon launching, as opposed to my other app. Does anyone had a similar problem ?
Finally, it is not very clear from apple documentation whether this method is supposed to be called when a UISplitViewController is first shown:
http://developer.apple.com/library/ios/#documentation/uikit/reference/UISplitViewControllerDelegate_protocol/Reference/Reference.html%23//apple_ref/doc/uid/TP40009454
"Kshitiz" has the right concept. first I set the self.splitviewController.delegate = self in the viewDidLoad method, which it is a bit late to set this delegation. So, I tried to set the delegation in earlier stage which is awakeFromNib method. Then it works well.
So, the problem is after view already loaded by viewDidLoad, then the delegation will not work, it will work some time after some activities (such as rotate the iPad). So the earlier stage than viewDidLoad is awakeFromNib.
Here is the code that works:
- (void) awakeFromNib{
[super awakeFromNib];
self.splitViewController.delegate = self;
}
Have you set a splitviewcontroller delegate?
Generally the problem arises when delegate is not set.
I was having the exact same problem, and Martin Gunnarsson's response led me to the solution.
Before, I was setting the UISplitViewController's delegate property after the delegate view (the detail view) had already been loaded, in viewDidLoad:. By this time, the UISplitViewController had already sent the initial splitViewController:willHideViewController:withBarButtonItem:forPopoverController: message. I simply hadn't set the delegate soon enough.
The solution was to assign the delegate in the main app delegate, in application:DidFinishLaunchingWithOptions:. In this case, my delegate was contained within a navigation controller, so I had to dig one layer deeper to get it.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
UINavigationController *mainNavigationController = (UINavigationController *)[splitViewController.viewControllers objectAtIndex:1];
HPMainViewController *mainViewController = [mainNavigationController.viewControllers objectAtIndex:0];
splitViewController.delegate = mainViewController;
return YES;
}
This drove me spare as well, the more so since I'm working on two iPad projects with out-of-the-box splitViewController and the first one always shows the 'Master' button while the second one never did. I compared outlets and relationships and delegates until I was cross-eyed, but finally found the answer in the appDelegate. It turned out I had commented out a bit too much in the application:didFinishLaunchingWithOptions:, specifically where the splitViewController.delegate is set.
- (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;
}
Adding this to the appDelegate saves you from having to subclass the splitViewController.
I also tried connecting the delegate in IB, but for some reason it wouldn't have none of that. Storyboard design flaw, imho.
I was stuck on this for quite sometime. Finally got it to work. The awakeFromNib did not work for me. The didFinishLaunchingWithOptions did. Might be because I am running some query that populates the items in the popover controller.
I'm having the same issue. My view is set up in IB, and it seems this is a timing issue. The split view delegate gets set after the split view has notified about the initial orientation "change". Adding the split view to an outlet in the app delegate made the button appear at portrait startup for me, but when I open the popup it's empty. This can probably be worked around somehow, but I think it's weird that the split view doesn't notify its delegate about the current orientation when it's set.
Related
I have a pretty standard setup of UITableview as master controller in UISplitviewController, universal iOS9 app.
Again, standard fare, I inserted a UISearchBar as the header of the table.
self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
self.searchController.searchResultsUpdater = self;
self.searchController.dimsBackgroundDuringPresentation = NO;
self.searchController.hidesNavigationBarDuringPresentation = NO;
[self.searchController.searchBar sizeToFit];
self.tableView.tableHeaderView = self.searchController.searchBar;
self.definesPresentationContext = YES;
No need for showing the rest of my code, the search and all work as expected, on the iPhone. My issue is that the search bar does not ever receive focus or present the keyboard when run on iPad. No response, seems not to receive touches. Does not even respond to programmatically attempting to becomeFirstResponder.
Nothing happens.
The search bar is visible and placed appropriately, there are no underlay or overlay issues.
It works fine on iPhone. Receives touch, presents keyboard, search works. Presumably there is a critical difference in presentation when UISplitviewController is collapsed. With hours of search, pouring through the documentation and every tutorial I come across, I have not found a similar experience which is surprising.
Additional note: If I use multi tasking to shrink the split view to collapsed on iPad, the search bar then works, same as on iPhone. It definitely has to do with the collapsed state of the split view.
UPDATE: (Getting closer)
After further experimentation I find that the search bar works on the iPad if you start the app in landscape. In portrait mode, my primary controller is hidden. It slides in by selection of the displayModeButtonItem, which is set to the leftBarButtonItem of the detail controller. That is when the searchBar breaks and no longer will respond to touches.
If I start in landscape, then rotate to portrait, it stops working as well. Rotating back to landscape does not fix it. Once broke, it stays broke until a restart.
I tried moving definesPresentationContext to the viewWillAppear method of the master controller, but that had no effect.
Further Update:
Another unexpected circumstance is that there is no problem with the search bar at all on iPhone 6plus. Works regardless of orientation, collapsed or no and doesn't matter which state it starts in. I would expect this would be same as iPad when in landscape orientation. Works completely on iPhone.
I still have not figured this out. My latest attempt to fix, I moved all the search controller initialization to the viewDidLayoutSubviews method. No change whatsoever.
Also, I noticed that when the search is active, keyboard onscreen, if I rotate the iPad the keyboard goes away but the search bar remains active. I didn't realize at first, then I saw the cancel button still displayed. It won't receive touches and does not have keyboard, but it is apparently still first responder.
Sample Project Uploaded to GitHub:
Github Repository
NOTE- There was no issue until I added my UISplitview Delegate methods to the project. I wanted to add this to my question right away, so, I haven't yet even tried to see exactly how these delegate methods affect the search bar but obviously the issue is created there somewhere.
Update:
I tried a few more variations with no success.
Move search bar to section header. No joy.
Put search bar in a UIView as a content view. No joy.
Remove search bar on disappear, reinsert on did appear. No joy.
Manipulate the size of search bar frame to ensure it is not clipped. No joy.
I also looked at the view hierarchy in profiler, the search bar is top level, not covered by anything else.
Final Wrap:
#tomSwift workaround did correct my issue on iPad. On further testing, I found it broke my iPhone UI. Presumably, this is more an issue with my own setup and the timing of creation of the Master/Detail View Controllers. It would seem some important objects no longer were created in time as perhaps the detail view controller didn't exist yet. I poked around to fix by moving critical items into App Delegate but that became unwieldy and I couldn't easily narrow it down, so I hacked a fix with:
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible;
}
A more elegant solution is likely to be had, but this has taken enough of my time. Everything works now. Radar filed- 28304096
This is clearly a bug in UISplitViewController and/or UISearchController. Certainly file a radar w/ Apple and attach your code sample.
I think the bug involves UISplitViewController.displayMode UISplitViewControllerDisplayModeAutomatic. When I change the preferredDisplayMode to UISplitViewControllerDisplayModeAllVisible then everything starts to work:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem;
splitViewController.delegate = self;
splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible;
return YES;
}
The main thing this changes is the default layout for portrait on iPad, which I understand may not be ideal. I also played with setting preferredDisplayMode to UISplitViewControllerDisplayModeAllVisible initially then setting it back to UISplitViewControllerDisplayModeAutomatic; this worked great until the screen orientation changed - then it all broke again. You might have some luck going further down that path.
I also made the following addition in the AppDelegate, to force the search controller to deactivate on displayMode change. (I also had to expose the searchController property publicly on the MasterViewController).
- (void)splitViewController:(UISplitViewController *)svc willChangeToDisplayMode:(UISplitViewControllerDisplayMode)displayMode {
UINavigationController *navigationController = [svc.viewControllers firstObject];
MasterViewController* mvc = (MasterViewController*) navigationController.topViewController;
[mvc.searchController setActive: NO];
}
Played around with your project and discovered that the issue is that your search controller is never set to active. So I subclassed UISplitViewController and made it a delegate for itself so it could notify the child view controllers when the mode changes, so you can set it active at the right time.
Use this delegate method to create the notification:
- (void)splitViewController:(UISplitViewController *)svc willChangeToDisplayMode:(UISplitViewControllerDisplayMode)displayMode {
NSLog(#"WILL CHANGE TO DISPLAY MODE: %ld", (long)displayMode);
NSNumber *displayNumber = [NSNumber numberWithInteger:displayMode];
NSDictionary *userInfo = #{ #"displayMode": displayNumber };
NSNotification *modeChangedNotif = [NSNotification notificationWithName:#"modeChanged" object:nil userInfo:userInfo];
[[NSNotificationCenter defaultCenter] postNotification:modeChangedNotif];
}
Then in your MasterViewController you can do this in viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(modeChanged) name:#"modeChanged" object:nil];
In that method, you can do a check for a particular displayMode enum, but for the sake of getting it working I just did:
- (void) modeChanged {
[self.searchController setActive:YES];
}
I was surprised to observe that within didFinishLaunchingWithOptions a call to [UIApplication sharedApplication].keyWindow.rootViewController returns nil.
I want to set some things up once on app launch where I need to reference the rvc, however this behavior means I'll have to do it elsewhere.
If it can't be done in didFinishLaunchingWithOptions then the only other choice is applicationDidBecomeActive? But with the additional irritation (a small irritation, but still you'd think it shouldn't have to be necessary) of having to have a flag to ensure the set up steps only happens once and not every time appliationDidBecomeActive is called.
Is there somewhere else I can access the rootViewController on app launch to set additional steps up once?
There isn't a keyWindow at that time in the application lifecycle, so the reason there's no rootViewController to get on the keyWindow is because keyWindow is nil. But the app delegate has a property for your window, so you can just get self.window.rootViewController.
However, if you always have the same root view controller, you could probably do at least some of what you want to do (maybe all of it) in your root view controller's viewDidLoad method. This generally should only ever be called once, because with iOS 6 and later your offscreen view controllers' views are never unloaded.
You can get it from window:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIViewController *rootViewController = self.window.rootViewController;
return YES;
}
I have an application created from the tabbed application template. (ARC, iOS 4)
There are several tabs and there is a button on the 2. tabs viewcontroller.view(ViewCont2).
This button loads another viewcontroller's(ModalViewCont) view by presentModalViewController method.
There is a close button on ModalViewCont which calls dismissModalViewControllerAnimated.
In viewDidDisappear of ViewCont2, i am setting self.view = nil and other outlets to nil to unload the view so it will be fresh loaded next time it appears on screen. I am doing this because it inherits from a base class(BaseViewCont) which initializes some general properties of the view controller and adds some buttons, labels etc. in viewDidLoad method. So, ViewControllers that inherit from this base class may configure those properties differently as they wish in their viewDidLoad method.
Problem
Now, when ModalViewCont on screen, pressing the Home button to put application in background and after getting the application back, closing the ModalViewCont does not bring back the ViewCont2's view but a black screen with the tabbar at the bottom. The same thing happens without putting the application background/foreground; if other tabs tapped before tapping the 2. tab.(EDIT : This happens only if self.view set to nil in viewWillDisappear instead of viewDidDisappear.)
I determined that ViewCont2 loads a new view (checked it's reference) but view's superview is nil so the new view is not displayed but a black screen.
Things that did not work
Using [self.view removeFromSuperview]; before setting self.view=nil,
In viewWillAppear adding view to the parent; [self.parentViewController.view addSubview:self.view]; This one did not work smoothly, view placed slightly up of the screen. This is because there are several other superviews in the hierarchy.
Solutions i considered;
1- If superview is nil in viewDidLoad, it becomes available in viewWillAppear (assumption). So, viewWillAppear method of ViewCont2 could be used to get the superview loaded correctly by the following;
_
if (self.view.superview == nil)
{
self.tabBarController.selectedViewController = nil;
self.tabBarController.selectedViewController = self;
}
2- viewWillAppear method of base class could be used instead for initialization so there is no need to unload the view. So, performance could be optimized, it will not be unloaded each time view disappears. Also, it would be better to perform initialization only once by checking a flag, instead of performing it every time it appears.
Questions
1- Why does not the superview restored? What should i do for it? (This is the main problem i want to understand and solve instead of trying alternatives...)
2- Am i doing something wrong by assigning nil to view for unloading it? If so, how should i unload the view properly in such case like this(tabbed application)?
3- Is anything wrong with the 1. solution? Does it seem like a kludge? Is that assumption about superview and viewWillAppear correct?
EDIT : It seems that when viewDidLoad is called earlier than it should(i.e when view nilled in viewWillDisappear instead of viewDidDisappear), superview is not set.
It seems weird, but your suggestion (1) is indeed a correct workaround for this problem:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (!self.view.superview) { // check if view has been added to view hierarchy
self.tabBarController.selectedViewController = nil;
self.tabBarController.selectedViewController = self;
}
}
Your second suggestion is good for performance (because view loading is an expensive operation) - but it will not solve the problem. You can also end up with a black screen without setting the view to nil in the following situation (test this in the iOS simulator):
open the modal view
simulate a memory warning -> this will unload the views in the tabbarcontroller
press home button and open the app again
close modal view -> black screen
Generally you can assume that in viewDidLoad the view property is set and in viewWillAppear + viewDidAppear the view has been added to the view hierarchy; so the superview should be there at that time (Here the superview is a private view of the tabbarcontroller of class UIViewControllerWrapperView). However in our case, although the view is reloaded (at the time of app resume), it is not added to the view hierarchy resulting in a black screen. This seems to be a bug in UITabBarController.
The workaround forces the appearance selectors to be performed again. So viewWillAppear will be called again, this time with a superview in place. Also viewDidAppear will be called twice!
Setting self.view to nil is okay, but should not be necessary in most cases. Let the system decide when to unload the view (iOS can unload views when memory gets low). The view controller code should be designed in a way so that the UI can be reconfigured at any time without reloading the view.
You do not have full control over when views are loaded and unloaded, and you are not supposed to load/unload views manually yourself.
Instead, you should think of view loading/unloading as something that's entirely up to your UIViewControllers, with you being responsible only for:
Implementing the actual loading, by associating your UIViewController subclass with a nib file or by implementing loadView manually.
Optionally implementing the viewDidLoad, viewWillUnload and viewDidUnload callbacks, which are called by the view controller when it decides to load/unload its view.
The fact that you have no full control of when the above callbacks will be called, has implications about what should go into them.
In your case, if I understand correctly, whenever your ViewCont2's view disappears, you want to reset it so that when it reappears it will be in some "clean" state. I would implement this state reset in some method, and call it both from viewDidLoad and from viewDidDisappear. Alternatively, you can have the "clean" logic in viewWillAppear.
Or maybe you want to clean ViewCont2's view only when the present button is tapped? In that case, clean the view both in viewDidLoad, and when the button is tapped.
What I offer is that when the modal view controller is active, and you dismiss the view, that you add a new view to the navigation view controllers viewControllers, then that view is told to remove its predecessor.
You can play with my project to see if you think it works for you.
EDIT: my comment on the selected answer is that this technique obviously works now, but I myself am having a hard time followiing it. The code in my project uses the system in a simple and direct fashion - when the modal view is told to dismiss itself, it calls a method (could be in any class) that adds a new view to the navigation controller's array, then dismisses itself. For a bit of time there are two view controllers of the same time, the new one stacked over the old one. When the new view controller appears, based on seeing a flag it silently and behind the scenes removes the undesired viewController from the nab bar's stack, and poof, it goes away.
I have found the actual solution to the UITabBarController bug(memory warning,app enter back/foreground,dismiss modal). Using UITabBarController as the root view controller is the cause of the bug. So, we could use another view controller as the root view controller and present the tab bar from it. I have tested it on iOS 5.1 simulator.
Of course, the overhead of extra UIViewController is subject to debate. Also, it's against the Apple documentation;
Unlike other view controllers, a tab bar interface should never be installed as a child of another view controller.UITabBarController Class Reference
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// A root view controller other than the actual UITabBarController is required.
self.window.rootViewController = [[UIViewController alloc] init];
[self.window makeKeyAndVisible];
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:viewController1, ..., nil];
[self.window.rootViewController
presentModalViewController:self.tabBarController animated:NO];
}
I have found other solutions;
First one causes the warning: "Application windows are expected to have a root view controller at the end of application launch" although there is root view controller.
Although it seems kludgy, the temporary view controller will be released with the first one.
Second one seems more reasonable.
.
- (void) tabBarBlankScreenFix1
{
self.window.rootViewController = [[UIViewController alloc] init];
[self.window makeKeyAndVisible];
[self.window addSubview:self.tabBarController.view];
self.window.rootViewController = self.tabBarController;
}
- (void) tabBarBlankScreenFix2
{
self.window.rootViewController = [[UIViewController alloc] init];
[self.window makeKeyAndVisible];
[self.window addSubview:self.tabBarController.view];
}
I think you shouldn't assign the view to nil.
If I understand you right you want to refresh/reload content every time the view appears.
So instead of setting the view to nil, you should try to refresh it. You can do it by adding:
- (void)viewWillAppear{
[self.view setNeedsDisplay];}
Please tell me if I understand your issue right
I have made an ipad application using an UISplitViewController, which functions in both orientation. Now I want to add banners to this application. How do I do this? In interface builder I can only add a portait banner to the detailView, which works more or less, but when I turn the iPad and click the banner it opens in portrait mode instead of landscape mode. And the banner can never get the prescribed width for ipad-landscape mode.
Trying to do it programmatically, it tells me that the parent of the adbannerview should be a UIViewController.
This same problem drove me nuts for ages until I found the iAdSuite sample. So, to expand on Erran's answer: Use the iAdSuite sample code from Apple.
Get yourself a working split view app using storyboards.
Include the iAd Framework.
Copy the BannerViewController.h and .m files to your app. Then in AppDelegate.m in "application didFinishLaunching" copy the line from iAdSuite's AppDelegate as per the last line here:
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
splitViewController.delegate = (id)navigationController.topViewController;
UINavigationController *masterNavigationController = splitViewController.viewControllers[0];
_bannerViewController = [[BannerViewController alloc] initWithContentViewController:splitViewController];
In the section for iPhone you need this line:
_bannerViewController = [[BannerViewController alloc] initWithContentViewController:navigationController];
Just before the return statement add this
self.window.rootViewController = _bannerViewController;
Add this at the top of the .m
#implementation AppDelegate{
BannerViewController *_bannerViewController;}
#import "BannerViewController.h"
Or create the bannerViewController property any way you prefer.
Amend the .h as follows:
#import <UIKit/UIKit.h>
#class BannerViewController;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
That was enough to get it all working. The whole split view app now operates inside the BannerView. That handles all the AdBannerDelegate functions.
Good luck :-)
In the iAdSuite Apple sample code there is a split view controller iAd implementation which you can easily add into your application. :^)
i've taken #ipwnstuff's answer a bit further.
first caveat: iAd only allows you to display in "portrait" or "landscape", and on iPad, this means 768x66 or 1024x66 respectively. this means there's no possibility of confining the ADBannerView to only the UISplitViewController detail view unless you want to roll your own that has a slightly wider view (and thus slightly narrower masterViewController view. i wanted to stick with storyboards, so i didn't want to go this route.
second caveat, the iAdSuite stuff #ipwnstuff pointed to is not immediately storyboard friendly. it creates the UISplitViewController programmatically, and you have to supply the master and detail either via .XIB or programmatically. since i had a working storyboard into which i wanted to integrate iAds, i wanted to extend that. also, the iAdSuite solution does not hide the master view in portrait mode, and i still wanted that.
so … starting an existing iPad.storyboard file, and then integrating with the SplitBanner sample from with iAdSuite as follows:
UISplitViewController *splitViewController = (id)self.window.rootViewController;
splitViewController.delegate = (id)splitViewController.detailUIViewController;
CGRect splitViewFrame = splitViewController.view.frame;
splitViewFrame.origin.y -= application.statusBarFrame.size.height;
splitViewFrame.size.height += application.statusBarFrame.size.height;
splitViewController.view.frame = splitViewFrame;
// initWithContentViewController: the thing that's in the iAdSuite SplitViewBanner example
self.bannerViewController
= [[BannerViewController alloc] initWithContentViewController:splitViewController];
self.window.rootViewController = self.bannerViewController;
i was thus able to use a storyboard UISplitViewController as the childViewController of the BannerViewController provided by iAdSuite.
ok, third caveat: there's one glitch, and that's that if you have the normal bar-button setting via splitViewControllerDelegate set up, the delegate won't get called if you rotate during the ad, and so the button will temporarily show up when it's not supposed to or not show up when it's supposed to.
At first it looks a bit like you forgot to uncomment or implement the shouldAutorotateToInterfaceOrientation variable... But I'm not quite sure. What happens when you tap on the banner? Does it open a new View and an UIWebView or something? Or something else? And when we are talking about AD banners right now, you should probably think about implement Apples iAd Service.
I am using a UISplitViewController inside a UITabBarController with a plain UIViewController in the master pane of the split view and a UINavigationController in the detail pane, which itself contains a vanilla UIViewController.
I am aware that Apple advise to use split views at the root level only, however I have seen other applications (eg, Amazon- 'Wish List' tab) that use split views in tabs so I'm sure it's possible.
My problem is that the delegate methods of the split view, ie. those in UISplitViewControllerDelegate do not get called, which prevents me from creating my pop-over menu when switching into Portrait mode.
The methods in question are the following -
// Called when a button should be added to a toolbar for a hidden view controller
- (void)splitViewController: (UISplitViewController*)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem*)barButtonItem forPopoverController: (UIPopoverController*)pc;
// Called when the view is shown again in the split view, invalidating the button and popover controller
- (void)splitViewController: (UISplitViewController*)svc willShowViewController:(UIViewController *)aViewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem;
// Called when the view controller is shown in a popover so the delegate can take action like hiding other popovers.
- (void)splitViewController: (UISplitViewController*)svc popoverController: (UIPopoverController*)pc willPresentViewController:(UIViewController *)aViewController;
The UISplitViewController does receive the rotation notifications.
I can get the willShowViewController method to be called if I force the status bar orientation to landscape right (or left) at the beginning of the app launch, using
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight];
However, the willHideViewController doesn't get called. And I don't want to force the app to start in landscape. If I do the same thing but force it to portrait, I don't receive the callbacks.
I don't understand why the split view controller is not calling it's delegate methods when it is otherwise behaving correctly. These methods should be called from its method-
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
internally, and when I breakpoint inside this, I can check that the delegate is set and that it is still alive.
Been stuck on this all day! Everything else is working great and I'm very pleased that the splitview / tabbar / navbar combination is working well. I just need these notifications.
Should I perhaps just call them manually when I rotate? Seems very wrong when the `UISplitViewController' should be doing this.
Solved, it has to be at either root level or a direct subview of a tabBar which also must be at root level. Annoying!
First, try to see if you are setting the correct delegates.
e.g., lets say you created three controllers,
UISplitViewController* splitView;
UIViewController* masterView;
UIViewController* detailView;
You implemented the delegate protocol at the detail view, so that when orientation changes, detail view should be able to put a button in the toolbar.
Now in order for splitView to call this function from delegate, you need to set the delegate itself.
So somewhere, if you are missing the following call,
splitView.delegate = detailView;
detailView's will never get notified of the orientation changes etc. At least this is where I was stuck.
I like the following method of sending a message from the master UIViewController to the detail UIViewController. Somewhere inside the master's implementation:
id detailViewController = [[self.splitViewController viewControllers] lastObject];
[detailViewController setSomeProperty:…];
This is from Paul Hegarty's Fall 2011 Stanford iTunesU iPad and iPhone Application Development course.