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.
Related
I am trying to add a navigation controller inside a tab bar controller in a brand new non-storyboard (plain old nib) style.
I found this demo which assumes that XCode has a new project template called "Tab Bar Application". It doesn't. Now XCode 4.6 has "New Tabbed Application". Of course, Apple in their great wisdom has decided that I should not have a main window nib (.xib) and should have the tab bar controller and its pages coded for me in the app delegate instead of in a new-user-friendly NIB instantiated tab controller. I guess that's because it's more flexible that way, and you can write code to decide what tabs the user sees and what tabs they don't see. If I turn on Storyboards, then I guess I can do everything visually still.
I am deeply confused by the long and tortured history of XCode version differences, which affect the validity of existing questions on stackoverflow and demos elsewhere on the web, that reference different iOS versions, and different XCode versions, and make assumptions specific to versions of XCode and iOS that now appear to have changed, and which each rely on different choices with respect to the contents of your Main Nib File Base Name being set or not set.
I also find that it seems that pre-storyboard-Nibs and the complexities of combining various UIKit widgets and controllers has been a primary motivation behind the creation of storyboards.
I am working in a real non-storyboard nib-based application that appears to have been created before this change of heart at apple, and I can see that at startup a lot of unecessary views are created automatically by nib-instantiation and then deleted, in order to dynamically hide and show tabs on the tab bar controller. This appears to have been thought about a great deal at Apple, and they've changed the recommended practices implicitly by changing the way new applications are generated in XCode. I'm not questioning their wisdom, in fact I appreciate the change, but it's left me lost and confused.
Anyways, I'm just trying to put a navigation controller inside a tab in the main tab bar, and I already have an application that must have been started back when XCode used to generate a main window and generate a "Tab Bar Application" with a top level view that is a tabbed view, and the tab bar controller is nib instantiated. The demo above assumes as much.
Apple apparently famously never has provided a demo of this obvious combination of tab bar plus navigation controller. Or so I'm told. And the Apple Human Interface Guidelines apparently state (or used to state?) that it's better to put a navigation controller inside a tab bar than vice versa, and my question should be understood as wishing to comply with the HIG however possible, so I believe I'm asking about the recommended combination, not the discouraged combination.
Here's what I've tried so far:
Tried to follow this blog post, from circa 2009, which assumes things true about older versions of XCode that are no longer true.
Starting with a new tabbed application which XCode generated for me, with storyboards turned OFF, I have a root app delegate .m file that it generated for me that apparently creates at app-startup time, a Tab Bar Controller object completely in code, and which has no Main Window nib. The following code is entirely written by Apple, and I am wondering where (as a relatively new Cocoa developer) I'm supposed to break into this and place my new stuff, if I wanted to change one of the tabs to hav a nav bar and its associated UINavigationViewController:
-- This marker helps stackoverflow's busted markdown system not be confused --
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
UIViewController *viewController1, *viewController2;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
viewController1 = [[RPDAFirstViewController alloc] initWithNibName:#"RPDAFirstViewController_iPhone" bundle:nil];
viewController2 = [[RPDASecondViewController alloc] initWithNibName:#"RPDASecondViewController_iPhone" bundle:nil];
} else {
viewController1 = [[RPDAFirstViewController alloc] initWithNibName:#"RPDAFirstViewController_iPad" bundle:nil];
viewController2 = [[RPDASecondViewController alloc] initWithNibName:#"RPDASecondViewController_iPad" bundle:nil];
}
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = #[viewController1, viewController2];
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
return YES;
}
It now appears that what used to be possible without writing code (according to that 2009 example on the blog I linked to) is now done purely in code. I have read about 500 pages of "Programming iOS 5", worked for a few hundred hours on my first app, and tried a lot of demo applications but I'm still a relatively unseasoned Cocoa/iOS developer, and I believe part of my confusion over all this is the "controller and view" pattern, and its rules for combining them, both in code, and in nibs, are not entirely clear to me.
--
Update: You can has teh codez! In the interest of helping out future XCode-cocoa-iOS noobs like me, I have made a complete demo app and posted it on github here.
Screenshot:
I was as confused as you were when I did the same thing, but once you understand the structure you will find out that it is quite simple.
Basically, You want to create a UINavigationController with the RootViewController you want to display first:
[UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:theViewControllerYouWantToDisplayFirst];
Then just add this controller to the array for your tab buttons.
NSArray *tabs = [[NSArray alloc]
initWithObjects: navController, otherTab, etc];
Then, set your tabs using such array.
UITabBarController *rootController = [[UITabBarController alloc] init];
[rootController setViewControllers:tabs];
and add it to your tab controller to your window:
[self.window setRootViewController:rootController];
And that is it. I took me some time to realize the way things are structured. The Tab View controller just holds a bunch of ViewControllers, you just need to make one of those view controllers your NavigationController.
If you don't mind starting a new app to get the basics going, try this:
Create a Tabbed Application (or whatever it's called nowadays):
Then select the view you want to have embedded in a NavigationController.
Then go to Editor -> Embed -> in NavigationController like shown below:
Your result will look like this:
Cheers!
As explained in the first two answers (both correct), I add another method to come back to the original "Tabbed Application" template (maybe version 3 of XCode? I don't remember).
In this template you'll have a main xib file with, inside, your TabBarController.
You must start from the Empty Application template, then go through this steps:
Add a new file, User Interface, Application Xib. In this tutorial I'll assume you will name it "Application.xib"
Go to your AppDelegate.h file and change this line:
#property (strong, nonatomic) UIWindow *window;
adding IBOutlet:
#property (strong, nonatomic) IBOutlet UIWindow *window;
Go inside AppDelegate.m and change your applicationDidFinishLaunching method with this
:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// delete all and leave only the return
return YES;
}
Go inside Application.xib, select "App Delegate" from the Objects Menu, and then in the identity inspector change the Class --> write AppDelegate (or your AppDelegate.h class name)
Always in Application.xib, right click again on App Delegate Object, you will see a "window" Outlet --> link it to the Window object in the Object inspector on the left
Now, come back to the project, go in supporting files, main.m and change the return line like this:
:
return UIApplicationMain(argc, argv, nil, nil);
then, go inside the main .plist file and add a key: right click, add, key, "Main Nib File Base Name", Type String, Value "Application" (without quotes, without .xib extension)
Ok, now you have an empty "old style" nib application. Now you can go again inside Application.xib, drag your TabBar and link it as the Window "root view controller" (right click on the Window, link the root view controller property).
Then, inside the tab bar, you can drag ViewControllers, Navigation controllers, ecc...
If you need other details or images write me
EDIT:
I uploaded a sample project if you want to look: http://www.lombax.it/documents/ManualTab.zip
Screenshot:
I had a working application that I wanted to place under one "leg" of a tabbed application. I wanted to develop other portions of the "bigger app" under the other tabs. I didn't want to use storyboards. That removed a good deal of the solutions I found. Most of the remaining solutions were developed using older versions of Xcode and they simply wouldn't work with the version of Xcode I downloaded in Feb of 2013.
I tried creating my own tabbed application - (starting with an "Empty Application") as some of the developers on this thread had used. I felt like I was getting close, but I simply couldn't get it to work.
There is an excellent tutorial on using a "Tabbed Application with a Navigation Controller" by Vishal Kurup.
With a minor modification (listed below) by following the video I was able to slip my existing application under the default "Tabbed Application" created by Xcode.
Create a TabBar Controller with a Navigation Controller + Detail View in Xcode 4.3
can be found at
http://www.youtube.com/watch?v=UMpNbCs4mr4
the only real change I had to make from the default "Tabbed Application" was in the AppDelegate.m:
the following code supplied by the selecting "Tabbed Application" wouldn't serve my purpose
self.tabBarController.viewControllers = #[viewController1, viewController2];
this did
self.tabBarController.viewControllers = [NSArray arrayWithObjects:navigation ,
viewController1 ,viewController2, viewController3, viewController4, nil];
A big thank you goes out to Mr Vishal Kurup.
He has made a number of quality videos on iOS development.
I am in the process of converting an iPhone app to a universal app.
I've changed all the xCode settings and added a new MainWindow-iPad.xib
I've hooked all the interface builder references up (e.g. MainWindow-iPad to the appropriate iPad class viewController, window etc)
I've added a new iPad class for the opening view controller and declared it in my AppDelegate.h file, and synthesized them in my AppDelegate.m file:
UIWindow *window;
UIWindow *windowiPad;
AppViewController *viewController;
AppViewControlleriPad *viewControlleriPad;
in the app delegate I check for which device I am running and load the appropriate class:
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// Override point for customization after application launch.
navController = [[UINavigationController alloc] initWithRootViewController:viewControlleriPad];
// Add the view controller's view to the window and display.
[windowiPad addSubview:navController.view];
[windowiPad makeKeyAndVisible];
[navController setNavigationBarHidden:YES animated:NO];
} else {
//I am an iPhone!
// Override point for customization after application launch.
navController = [[UINavigationController alloc] initWithRootViewController:viewController];
// Add the view controller's view to the window and display.
[window addSubview:navController.view];
[window makeKeyAndVisible];
[navController setNavigationBarHidden:YES animated:NO];
}
And everything seems to work fine - except it still loads the iPhone nib/xib file AppViewController.xib instead of AppViewControlleriPad.xib. You're welcome to ask for more information but I can't figure out how to get it to load the AppViewControlleriPad.xib when running an iPad rather than the iPhone/original xib file.
I thought, perhaps naively, that if the xib file has the same name as the class of the view controller then the ViewController would use that as its xib.
How can I fix it so that the correct xib is loaded for the correct device?
You have a spelling mistake - correct sufix for iPad resources is "~ipad" instead of "-iPad". With "~ipad" your resources will be loaded automatically depending to device.
And you probably don't need another UIWindow for your iPad controllers. You can use the same window at app starting point. Good practice is also to have one view controller (inside you can perform some special things for different devices) and two nibs (one "normal" and the second one with "~ipad" sufix). In rare cases if whole controller behavior is completely different you may want to use two view controllers.
It may be due to the reason that you didn't connect iPad's view controller to files owner in IB.
I am just starting with iPad App development. I want to use splitViewController in my app. I want to use different viewControllers. These will be loaded on rightHandView of an ipad when user selects appropriate on tableviewcontroller present on left hand.
I am using iOS SDK 5.0 without storyboard. I have seen apple's example of multipleDetailView and tried to follow similar procedure but its not working with iOS 5.0 sdk and Xcode4.2, I can not able to access MainWindow.Xib in my project as there is not one when you create project with XCode4.2 and master detail template.
Can anyone tell me how to approach this problem or direct me to appropriate resources ?
Regards,
Sumit
It seems that compared to previous versions, XCode 4.2 generates the relevant code in the "AppDelegate.m" instead of somewhere in the .xib file. I'm not sure about how to work with a MainWindow.xib here, but you could easily push other view controllers in the detail view navigation controller programmatically:
Use the following code for example on a button touch up action:
- (IBAction)buttonClick:(id)sender {
MySecondViewController *vc = [[MySecondViewController alloc] initWithNibName:#"MySecondViewController" bundle:nil];
[self.navigationController pushViewController:vc animated:TRUE];
}
To dismiss the top controller and get back you can use either
[self.navigationController popViewControllerAnimated:TRUE];
in the new top-of-the-stack controller (here MySecondViewController) or just the Back button in the navigation bar.
I am creating an application with Landscape Right orientation. For that I set the Initial interface orientation property in info.plist file. Then in every view I handled
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return(interfaceOrientation==UIInterfaceOrientationLandscapeRight);
}
It works fine in simulator but in device its behave differently.
My first view is in proper orientation. There is is popover which display another view that comes in portrait mode. Still my status bar is in Landscape Right..
For navigating from one view to another view I am using..
self.window.rootViewController = self.myNav;
I have multiple navigation Controller and adding those using the upper code.
I am not getting what is the problem.
Any help will be appreciated.
EDIT: I had used
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
I never get this issue in simulator but getting this in device and not every time. I have used Supported interface orientations (iPad) too and set Landscape (right home button) value for item0.
Thanks In advance
You need to set the "Simulated Metrics > Orientation" property of your top view (in all your xib files) to be "Landscape". The default is portrait.
The question was answered pretty well here - Landscape Mode ONLY for iPhone or iPad .
I also have an app that like yours only supports UIInterfaceOrientationLandscapeRight. I haven't run into any orientation issues so far. I only have one UIViewController under the window. This UIViewController has its own UITabBar that I use to change pages. So, we change pages differently. I set my UIViewController using the rootViewController property of the window just like you, but again I only have one.
Also, I never had to do anything like the [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications] call that you included. Since you only support LandscapeRight orientation, you shouldn't care to be notified of changes.
EDIT
I created a sample project, which I can post if necessary, and I think I may know your problem. First - do you only encounter the sizing issue inside popovers? If so, then I don't think orientation is throwing you off but the popover itself. My sample project has 3 view controllers. The first loads the second by changing the window's rootViewController. That worked fine. The second view controller has a button to open a popover. This will show up wrong unless you set the contentSizeForPopover on the view controller as shown below:
- (IBAction) showVC3InPopover
{
UIViewController * vc3 = [[VC3 alloc] init];
/** Set the contentSizeForViewInPopover property to determine how
large the view will be in the popover! You can do this in the
init method(s) of the view controller if you prefer, it's just
easy to do here */
vc3.contentSizeForViewInPopover = vc3.view.frame.size;
[_popover release], _popover = nil;
_popover = [[UIPopoverController alloc] initWithContentViewController:vc3];
[vc3 release];
[_popover presentPopoverFromRect:_showPopoverButton.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
See if this fixes your problem. If it does, there are other ways to control the popover size, but this is just what I typically do. However, just know that the PopoverController ignores the size of the view in the viewcontroller you load.
How many views (or viewControllers) you have? You might need to implement this orientation logic in all those views??
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.