Hi i have a splitViewController
mapViewController = [[MapViewController alloc] initWithManagedObjectContext:managedObjectContext startingRegion:startingRegion];
distanceViewController = [[DistanceTableViewController alloc] initWithManagedObjectContext:managedObjectContext];
distanceViewController.mapViewController = mapViewController;
setupViewController = [[SetupTableViewController alloc] initWithStyle:UITableViewStyleGrouped map:mapViewController.map];
setupViewController.positionSwitch.on = savePosition;
SearchTableViewController *searchViewController = [[SearchTableViewController alloc] initWithStyle:UITableViewStylePlain managedObjectContext:managedObjectContext];
searchViewController.mapViewController = mapViewController;
tabBarController = [[UITabBarController alloc] init];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
UINavigationController *mapNavigationController = [[[UINavigationController alloc] initWithRootViewController:mapViewController] autorelease];
UINavigationController *searchNavigationController = [[[UINavigationController alloc] initWithRootViewController:searchViewController] autorelease];
UINavigationController *distanceNavigationController = [[[UINavigationController alloc] initWithRootViewController:distanceViewController] autorelease];
UINavigationController *setupNavigationController = [[[UINavigationController alloc] initWithRootViewController:setupViewController] autorelease];
UISplitViewController* splitVC = [[UISplitViewController alloc] init];
splitVC.viewControllers = [NSArray arrayWithObjects:searchNavigationController, mapNavigationController, nil];
splitVC.title = #"iMetano";
splitVC.tabBarItem = [[[UITabBarItem alloc] initWithTitle:#"Mappa" image:[UIImage imageNamed:#"mapIcon2.png"] tag:0] autorelease];
NSArray *viewControllersArray = [NSArray arrayWithObjects: splitVC,setupNavigationController,nil];
[splitVC release];
tabBarController.viewControllers = viewControllersArray;
}
When i startup my app in portrait, all works fine.
When i startup my app in landscape this is the result
I see only the view of the first viewController SearchTableViewController with some pixel between the UINavigationController and the status bar
When i rotate in portrait and after i return in landscape i see both viewController's view, but the second have some pixel between the statusBar and the UINavigationControllor
I can't understand why.
apple says not to put a split view controller inside something else, like a tab bar controller
After looking at my code and IB time after time. This is the best that I could come up with. Not sure if is the best one but it works for me. Im loading a default detail view controller. If I load the controller directly in the viewDidLoad then the problem occur. If I load it from the selector the problem goes away. I hope this helps. I have this code in the RootViewController.
- (void)viewDidLoad {
[super viewDidLoad];
[self performSelector:#selector(loadController) withObject:nil afterDelay:0];
}
-(void)loadController{
UIViewController <SubstitutableDetailViewController> *detailViewController = nil;
WebViewController *newDetailViewController = [[WebViewController alloc] initWithNibName:#"WebViewController" bundle:nil];
[newDetailViewController setTitle:#"Home"];
NewNavController <SubstitutableDetailViewController>*navController = [[NewNavController alloc] initWithRootViewController:newDetailViewController];
detailViewController = navController;
NSArray *viewControllers = [[NSArray alloc] initWithObjects:self.navigationController, detailViewController, nil];
splitViewController.viewControllers = viewControllers;
}
I had this exact same problem when attempting the combination of tab bar, split view and navigation controllers. I noticed that the alignment gap is only present when the application first fires up and the first tab is auto-selected because it's the first tab in the tab bar controller's array of view controllers. After switching tabs and then coming back to the one with the misaligned nav controller in a split view, there was no alignment problem present. So, to replicate this behavior and get rid of the misalignment when the screen is first rendered I added:
[tabBarController setSelectedViewController:splitVC];
right after setting the view controller array on the tab bar controller. Works like a champ now.
I know this is an old question, but here's the hack I just used to get around this problem for anyone who has a navigation hierarchy like mine:
UITabBarController
Tab0->UINavigationController->MGSplitViewController _or_ UISplitViewController
Tab1->UINavigationController->SomeOtherViewController
Tab2->Etc...
Nothing I tried could get rid of that 20px gap that occurs only once, at bootup, if the device orientation is anything except UIInterfaceOrientationPortrait. The 20px gap is caused by the UINavigationBar for the split view's UINavigationController above having a non-zero origin.y value; most likely, you'll find it to be 20.
Also, I found that this is only a problem if the device is running iOS < 5.0.
I check for this issue in the view controller code of my MGSplitViewController (i.e. self = an MGSplitViewController):
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
if(self.doIOS4OneTimeRotationHack == YES)
{
self.doIOS4OneTimeRotationHack = NO;
for(UINavigationController *navController in [self viewControllers])
{
if(navController.navigationBar.frame.origin.y != 0.0f)
{
[UIView animateWithDuration:0.01
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:
^(void)
{
navController.navigationBar.frame = CGRectMake(navController.navigationBar.frame.origin.x,0.0f, navController.navigationBar.frame.size.width,navController.navigationBar.frame.size.height);
}
completion:
^(BOOL finished)
{
//NSLog(#"Shifted navbar 0x%x up!",navController.navigationBar);
}];
}
}
}
}
With the animation set to finish in just 0.01 seconds, it happens so fast that you'll never even notice it as your bootup splash screen disappears and your MGSplitViewController view appears in its place. Maybe play around with it and make it instantaneous; I had to get it working and move onto my next task, so I didn't fool with it past that point.
I don't like resorting to hacks like this, but this was the only way I was able to get around this problem. ScottS' solution below sounded great, but unfortunately didn't work for me.
Related
I have the following structure on my iPhone app
AppDelegate / UITabBarController / 5 UINavigationControllers(My tabs) / UIViewController(like rootViewController for each UINavigationController)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
HomeViewController *homeViewController = [[HomeViewController alloc] init];
GoalsTableViewController *goalsTableViewController = [[GoalsTableViewController alloc] init];
HistoryViewController *historyViewController = [[HistoryViewController alloc] init];
SettingsViewController *settingsViewController = [[SettingsViewController alloc] init];
InfoViewController *infoViewController = [[InfoViewController alloc] init];
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.delegate = self;
self.navBarActivity = [[UINavigationController alloc] initWithRootViewController:homeViewController];
self.navBarSettings = [[UINavigationController alloc] initWithRootViewController:settingsViewController];
self.navBarHistory = [[UINavigationController alloc] initWithRootViewController:historyViewController];
self.navBarGoals = [[UINavigationController alloc] initWithRootViewController:goalsTableViewController];
self.navBarAbout = [[UINavigationController alloc] initWithRootViewController:infoViewController];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:self.navBarActivity, self.navBarGoals, self.navBarHistory,self.navBarSettings, self.navBarAbout, nil];
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
return YES;
}
In some UIViewControllers I implemented a MFMailComposeViewController in order to send emails.
I experimented a weird issue (reproduced on simulator and real devices iOS 5.0 and 5.1)...
Using an iPhone Simulator (only iOS 5.0 or 5.1), if I simulate a low memory warning while a MFMailComposerViewController modal is open on the screen and then tap on Cancel and then tap on Delete|Save draft, when the modal is dismissed the parent view seems not visible (blanked view).
The life cycle seems work fine due, if I follow the same steps but after simulate a low memory warning I send the email from MFMailComposeViewController modal, when modal is dismissed, my parent view looks fine.
Any suggestions how to prevent my parent view from being unloaded on memory warning?
Edit1
I figured out what is happening, after unloading and comeback to the view and entering the last view within viewdidload(life cycle), the tabBar is not inserting navigation view. I check the subviews of tabBar:
UITransitionView
==><UIViewControllerWrapperView>
==> empty
<UITabBar>
I reintegrated the view of navigationBar by adding as subview in viewdidload:
UIView *tabBarControllerWrapperView = [[[self.tabBarController.view.subviews objectAtIndex:0] subviews] objectAtIndex:0];
// tabBar UIViewControllerWrapperView has not views
if([tabBarControllerWrapperView.subviews count] == 0)
{
// add navigationbar view
[tabBarControllerWrapperView addSubview:self.navigationController.view];
}
There is no better way to fix it, any thoughts?
After a memory warning is possible that on the non visible controllers is called viewDidUnload (iOS <= 5 ).In your case the view of the controller that presented the modal mailcomposer was probably unloaded.
The idea behind viewDidUnload is that you save the data you need to restore the view once viewDidLoad is called again. What you have to keep in mind is that your viewDidLoad can be called multiple times.
On iOS6 viewDidUnload is not called anymore, so this logic must be moved to didReceiveMemoryWarning
Hi I just started experimenting on iOS 5. I created a project without storyboard and trying to add views programmatically (no use of interface builder at all). I have following code but rootViewController property of the window does not seem to work. I did NSLog on self.tabController and it shows me value(not null) but on the other side when after self.window.rootViewController = self.tabController, i output self.window.rootViewController it gives me null in console.
I have been struggling with this issue for a long time now. Any help would be appreciated.
Following is my didFinishLaunching method:
self.dataSource = [[[ADJWebDataSource alloc] init] autorelease];
ADJBrowseListingsViewController *browseListingsVC = [[ADJBrowseListingsViewController alloc] init];
ADJSecondViewController *secondVC = [[ADJSecondViewController alloc] init];
tabBarController = [[UITabBarController alloc] init];
tabBarController.view.frame = CGRectMake(0, 0, 320, 460);
navController = [[UINavigationController alloc] initWithRootViewController:browseListingsVC];
NSMutableArray* viewControllers = [[NSMutableArray alloc] initWithCapacity:2];
[viewControllers addObject:browseListingsVC];
[viewControllers addObject:secondVC];
[navController release];
[browseListingsVC release];
[secondVC release];
tabBarController.viewControllers = viewControllers;
[viewControllers release];
browseListingsVC.dataSource = self.dataSource;
NSLog(#"controller %#", self.tabBarController);
self.window.rootViewController = self.tabBarController;
NSLog(#"controller1 %#", self.window.rootViewController);
[self.window makeKeyAndVisible];
return YES;
Thanks
Vik
When you are using story board, why are you still creating objects for view controllers?
You can directly prepare the flow of your views in story board, add necessary segues etc.
If your view controller is floating (without any segues), you have to use the method "instantiateViewControllerWithIdentifier" in story board class.
For a view controller if you want to add navigation in story board, select the view controller, go to menu "Editor"->"Embed in" and select navigation controller. It will add navigation controller to your view controller.
Figured it out with the help of Firoze. Actually, I had to allocate and initialize self.window programmatically. I was confused as I never had to do that in iOS 4 or earlier. But then I just realized prior to iOS5, every project has a MainWindow.xib which had self.window allocated and initialized, now if I am not using storyboard in iOS5, there is no .xib file, I needed to allocate and initialize it myself in the code
My UITabBar is not completely showing after I present a UITabBarController from a UIViewController. Please can you tell me what I am doing wrong?
My code is:
//some method
LoggedInViewController *lvc = [[[LoggedInViewController alloc] initWithAccount:account] autorelease];
[self presentModalViewController:lvc animated:YES];
- (void)viewDidLoad
{
self.tabController = [[UITabBarController alloc] init];
LoggedInFeedNavigationController *navController = [[LoggedInFeedNavigationController alloc] initWithAccount:self.account];
[self.tabController setViewControllers:[NSArray arrayWithObject:navController]];
[self.view addSubview:self.tabController.view];
[super viewDidLoad];
}
It's not a good practice to do:
[viewController1.view addSubview:viewController2.view];
The point of the MVC design is lost. The view controller should get your data (from the model) and put it in the view. If you have more than one view just arrange the functionality of the views to accept the corresponding data.
So if you need a tab bar controller you should do the following:
// assuming you are in the same initial controller
UITabBarController* pTabBarControllerL = [[UITabBarController alloc] init];
MyFirstController* pFirstControllerL = [[MyFirstController alloc] init];
[pTabBarControllerL setViewControllers:[NSArray arrayWithObject:pFirstControllerL]];
// perhaps set more tab bar controller properties - button images and so on
[self presentModalViewController:pTabBarControllerL animated:YES];
// release the memory you do not need
-(void)viewDidLoad {
// do your work in pFirstControllerL
}
PS: You should not subclass UINavigationController and UITabBarController.
Actually according to the Apple's recommendations UITabBarViewController should be the root in the UIWindow hierarchy. We had hard times trying to put TabBar VCs or Navigation VCs not to the root.
I've been stuck trying to puzzle this out for a couple days now, and I'll admit I need help.
The root view controller of my application is a tab bar controller. I want to have each tab bar a different navigation controller. These navigation controllers have completely different behavior.
So how do I set this up in terms of classes? Per Apple's documentation, I'm not supposed to subclass UINavigationViewController. So where do I put the code that drives each of these navigation controllers? Does it all get thrown in App Delegate? That would create an impossible mess.
This app should run on iOS 4.0 or later. (Realistically, I can probably require iOS 4.2.)
This is taken from one of my applications. As you say, you are not supposed to subclass UINavigationController, instead you use them as they are and you add viewcontroller on the UINavigationController's. Then after setting the root viewcontroller in each UINavigationController, you add the UINavigationController to the UITabBarController (phew!).
So each tab will "point" to a UINavigationController which has a regular viewcontroller as root viewcontroller, and it is the root viewcontroller (the one you add) that will be shown when a tab is pressed with a (optional) navigationbar at top.
UITabBarController *tvc = [[UITabBarController alloc] init];
self.tabBarController = tvc;
[tvc release];
// Instantiates three view-controllers which will be attached to the tabbar.
// Each view-controller is attached as rootviewcontroller in a navigationcontroller.
MainScreenViewController *vc1 = [[MainScreenViewController alloc] init];
PracticalMainViewController *vc2 = [[PracticalMainViewController alloc] init];
ExerciseViewController *vc3 = [[ExerciseViewController alloc] init];
UINavigationController *nvc1 = [[UINavigationController alloc] initWithRootViewController:vc1];
UINavigationController *nvc2 = [[UINavigationController alloc] initWithRootViewController:vc2];
UINavigationController *nvc3 = [[UINavigationController alloc] initWithRootViewController:vc3];
[vc1 release];
[vc2 release];
[vc3 release];
nvc1.navigationBar.barStyle = UIBarStyleBlack;
nvc2.navigationBar.barStyle = UIBarStyleBlack;
nvc3.navigationBar.barStyle = UIBarStyleBlack;
NSArray *controllers = [[NSArray alloc] initWithObjects:nvc1, nvc2, nvc3, nil];
[nvc1 release];
[nvc2 release];
[nvc3 release];
self.tabBarController.viewControllers = controllers;
[controllers release];
This is how I go from one viewcontroller to another one (this is done by tapping a cell in a tableview but as you see the pushViewController method can be used wherever you want).
(this is taken from another part of the app)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (self.detailedAnswerViewController == nil) {
TestAnsweredViewController *vc = [[TestAnsweredViewController alloc] init];
self.detailedAnswerViewController = vc;
[vc release];
}
[self.navigationController pushViewController:self.detailedAnswerViewController animated:YES];
}
The self.navigationcontroller property is of course set on each viewcontroller which are pushed on the UINavigationController hierachy.
hours ago I post a question on organizing portrait and landscape mode in iPhone and now I think I know how to do it using willRotateToInterfaceOrientation:duration.
The first screen is 'Map View' with one button that leads to 'Setting View'. The Map View does not support rotate but for the Setting View I made separate view for portrait and landscape and they swap accordingly when rotated.
, ,
As you can see when Setting button pressed SettingView is added on the view stack as usual. So basically I use three view controllers; Setting, SettingLandscape and SettingPortrait.
I still found problem in rotating view in iPhone when I use navigationViewController. Segmented control is not working. it crashes without error message. It used to working fine without rotation.- when I'm not using multiple view for rotation-.
rotateViewController.m
This is root view controller.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
-(IBAction) buttonPressed{
Setting *settingViewController = [[Setting alloc] initWithNibName:#"Setting" bundle:[NSBundle mainBundle]];
UINavigationController *navController1 = [[UINavigationController alloc] initWithRootViewController: settingViewController];
[self.navigationController presentModalViewController:navController1 animated:YES];
[settingViewController release];
[navController1 release];
}
Setting.m
This view controller does nothing but swap views when rotate and shows appropriate view between portrait and landscape.
In Setting.m, I swap view as follow;
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if (toInterfaceOrientation==UIInterfaceOrientationLandscapeRight) {
NSLog(#"to Right");
SettingLandscape *setting_landscape = [[SettingLandscape alloc] initWithNibName:#"SettingLandscape" bundle:[NSBundle mainBundle]];
self.view = setting_landscape.view;
[setting_landscape release];
}
if (toInterfaceOrientation==UIInterfaceOrientationLandscapeLeft) {
NSLog(#"to Left");
SettingLandscape *setting_landscape = [[SettingLandscape alloc] initWithNibName:#"SettingLandscape" bundle:[NSBundle mainBundle]];
self.view = setting_landscape.view;
[setting_landscape release];
}
if (toInterfaceOrientation==UIInterfaceOrientationPortrait) {
NSLog(#"to Portrait");
SettingPortrait *settingportrait = [[SettingPortrait alloc] initWithNibName:#"SettingPortrait" bundle:[NSBundle mainBundle]];
self.view = settingportrait.view;
[settingportrait release];
}
if (toInterfaceOrientation==UIInterfaceOrientationPortraitUpsideDown) {
NSLog(#"to PortraitUpsideDown");
SettingPortrait *settingportrait = [[SettingPortrait alloc] initWithNibName:#"SettingPortrait" bundle:[NSBundle mainBundle]];
self.view = settingportrait.view;
[settingportrait release];
}
}
In viewWillAppear, Setting view controller also has ;
self.title = #"Shell ";
self.navigationController.navigationBarHidden = NO;
self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:#"Done" style:UIBarButtonItemStylePlain target:self action:#selector(Done)] autorelease];
and Done is
- (void) Done{
[self dismissModalViewControllerAnimated:YES];
}
SettingLandscape.m
This view stacked on when the view is rotated. This view controller has it's navigation bar.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.title = #"Setting Landscape";
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
in viewDidLoad;
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"landscape:viewDidLoad");
//self.title = #"SettingLandscape";//not working!!
//self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:#"Done1" style:UIBarButtonItemStylePlain target:self action:#selector(Done)] autorelease];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
stringflag4MapType = [[NSString alloc] initWithString:#"blah"];
stringflag4MapType = [defaults stringForKey:#"flag4MapType"];
if (![stringflag4MapType isEqualToString:#"Hybrid"] && ![stringflag4MapType isEqualToString:#"Standard"] && ![stringflag4MapType isEqualToString:#"Satellite"]) {
segmentedControl4MapType.selectedSegmentIndex = 0;
}else if ([self.stringflag4MapType isEqualToString:#"Standard"]) {
segmentedControl4MapType.selectedSegmentIndex = 0;
}else if ([self.stringflag4MapType isEqualToString:#"Satellite"]) {
segmentedControl4MapType.selectedSegmentIndex = 1;
}else if ([self.stringflag4MapType isEqualToString:#"Hybrid"]) {
segmentedControl4MapType.selectedSegmentIndex = 2;
}
and following call does not get invoked. strange. doesn't matter rotation works anyway.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if (toInterfaceOrientation==UIInterfaceOrientationPortrait) {
NSLog(#"to Portrait");// does not print out.
SettingPortrait *settingportrait = [[SettingPortrait alloc] initWithNibName:#"SettingPortrait" bundle:[NSBundle mainBundle]];
self.view = settingportrait.view;
[settingportrait release];
}
if (toInterfaceOrientation==UIInterfaceOrientationPortraitUpsideDown) {
NSLog(#"to PortraitUpsideDown");
SettingPortrait *settingportrait = [[SettingPortrait alloc] initWithNibName:#"SettingPortrait" bundle:[NSBundle mainBundle]];
self.view = settingportrait.view;
[settingportrait release];
}
}
ok now, as you can see from those snap shots there are two navigation bar and each has its bar button, Done and Item. The Done button came from Setting and the Item button from SettingPortrait or SettingLandscape. All button's selector is same, that leads back to map view. The button Done works fine, but the button Item crashes. I need a button on navigation bar after rotation that acts like back button . I guess once I did 'self.view = settingportrait.view;' the problem starts.
The reason why I need the Item button work is that the segmented control started crashing once I add code to support rotation. If I found reason how to make the Item button-that is inside rotation view- work I think I can make the segmented control work as well.
You can download the whole code at https://github.com/downloads/bicbac/rotation-test/rotate-1.zip
https://github.com/downloads/bicbac/rotation-test/rotate-1.zip
this sample code is amazing for me. It solve my problem of rotating view just by simple delegate method
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
my best attempt to answer question without looking at code (don't have time tonight :( )
When you present setting viewcontroller modally, your top viewcontroller is setting.
When rotation happens, you load setting_landscape or setting_portrait viewcontroller, but only retain the view inside setting_landscape|portrait. Thus, setting_landscape/portrait viewcontrollers are released. When device is rotated, it's probably "setting" viewcontroller receiving rotation message, not the "setting_landscape/portrait" viewcontroller because they are not pushed on to the viewcontroller stack.
So, when you click on item or segment control, it will call delegate, which is probably set to setting_landscape|portrait which is released already.
What is the message in console you get with crash?
My recommendation would be to build setting viewcontroller with segmented control, then use "willAnimateRotationToInterfaceOrientation:duration:" function to reposition the segmented control to the right position and frame. Just by returning YES to all orientation, rotation should be supported, doesn't it?
What was the reason for using two separate viewcontroller for landscape/portrait? (I do this sometimes, but rarely)
Edit* you need to use "willAnimateRotationToInterfaceOrientation" callback to animate the changes, not "willRotate..."