Orientation Problem while using insertSubview - ipad

I get an orientation problem while using the following to code to display a view on top of a split view.
[window addSubview:aSplitViewController.view];
[window insertSubview:aViewController.view aboveSubview:aSplitViewController.view];
the plain view has a couple of buttons and labels.
So the problem I am facing is that the first view opens in landscape mode but the labels and buttons on the view are in portrait mode.
UPDATE: Here is some code so if anyone wants to see more details...
In my App Delegate
- (void) makeSplitViewController {
NSMutableArray *controllers = [NSMutableArray arrayWithArray:tabBarController.viewControllers];
// First tabbbar item
// detail view
detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailView" bundle:nil];
UINavigationController *navDetailView = [[[UINavigationController alloc] initWithRootViewController:detailViewController] autorelease];
navDetailView.hidesBottomBarWhenPushed = YES;
// root view
rootViewController = [[RootViewController alloc] initWithStyle:UITableViewStylePlain];
rootViewController.detailViewController = detailViewController;
rootViewController.navigationItem.title = #"List";
UINavigationController *navRootView = [[[UINavigationController alloc] initWithRootViewController:rootViewController] autorelease];
navRootView.hidesBottomBarWhenPushed = YES;
navRootView.navigationBar.barStyle = UIBarStyleBlackTranslucent;
splitViewController = [[UISplitViewController alloc] init];
splitViewController.tabBarItem.title = #"Face Sheet";
splitViewController.tabBarItem.image = [UIImage imageNamed:#"gear1.png"];
splitViewController.navigationItem.title = #"Face Sheet";
splitViewController.viewControllers = [NSArray arrayWithObjects:navRootView, navDetailView, nil];
splitViewController.delegate = detailViewController;
splitViewController.hidesBottomBarWhenPushed = YES;
[controllers addObject:splitViewController];
// Second tabbbar item
scoreViewController = [[ScoreCardViewController alloc] initWithNibName:#"TableViewController" bundle:nil];
scoreViewController.tabBarItem.title = #"Score Card";
scoreViewController.tabBarItem.image = [UIImage imageNamed:#"gear1.png"];
scoreViewController.navigationItem.title = #"Score Card";
[controllers addObject:scoreViewController];
tabBarController.viewControllers = controllers;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Create tabbar
tabBarController = [[UITabBarController alloc] init];
//tabBarController.delegate = self;
// Set window
[window addSubview:splashController.view];
[window insertSubview:tabBarController.view belowSubview:splashController.view];
[self.window makeKeyAndVisible];
application.statusBarOrientation = UIInterfaceOrientationLandscapeRight;
return YES;
}
and here is the code in my SplashScreenView
- (IBAction) proceedButtonClick:(id)sender
{
// Initialize loginpopview
PhysicianLoginViewController *loginViewController = [[PhysicianLoginViewController alloc] init];
popOverController = [[UIPopoverController alloc] initWithContentViewController:loginViewController];
popOverController.popoverContentSize = CGSizeMake(350, 200);
popOverController.delegate = self;
// Set a notification to dismiss it later
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(loginViewControllerDone:) name:#"loginViewControllerDone" object:popOverController.contentViewController];
// Present popover
if ([popOverController isPopoverVisible])
{
[popOverController dismissPopoverAnimated:YES];
}
else
{
[popOverController presentPopoverFromRect:CGRectMake(485, 600, 100, 100) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES];
}
}
// Dismiss popview controller and setup the tabbar
- (void)loginViewControllerDone:(NSNotification *)notification{
[[NSNotificationCenter defaultCenter] removeObserver:self];
// Button in content view controller was tapped, dismiss popover...
[self.popOverController dismissPopoverAnimated:YES];
// remove subview
[self.view removeFromSuperview];
// set tabbar
i3EAppDelegate *appDelegate = (i3EAppDelegate *) [[UIApplication sharedApplication]delegate];
[appDelegate makeSplitViewController];
}
It would be great if someone could point out where I am going wrong. I have been stuck with this problem for quite a few days and I have tried everything that comes to my mind...

UIWindow has a subview that it uses for rotations and puts other views inside of that. You need to insert yourself into the root view (or something lower), not the window. Look at -[UIWindow rootViewController].
UIView *rootView = [[[self window] rootViewController] view];
[rootView addSubview:view];
This will work as long as you're using something with a root view controller. This will work as long as rootViewController isn't nil. If you're doing a raw "View Based" application, then it's usually best to pick another view and add your view as its sibling rather than digging through the undocumented hierarchy:
UIView *sibling = ... (some other view)
[[sibling superview] addSubview:view];

Related

UINavigationBar with UISegmentedControl partially covers childViews

I have read many other threads on this and the Apple docs, but haven't found a solution yet for my particular problem.
My app uses a UITabBarController as the rootViewController, and in one of the tabs I have a UISegmentedControl in the navigationBar to switch between three child UITableViewControllers.
(In the real app two of the childVCs are a custom UIViewController, I'm just using three UITableViewControllers for the sample app).
The segmentedControl setup and the switching all works fine. The thing that goes wrong is that only the first UITableViewController is shown correctly. For the second and third one, part of the first cell is hidden under the navigationBar. When I click through all three, the first one is still ok.
I have made a little sample app to show what's going on, using very bright colors for demonstration purposes: https://www.dropbox.com/s/7pfutvn5jba6rva/SegmentedControlVC.zip?dl=0
Here is also some code (I'm not using storyboards):
// AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
FirstViewController *fvc = [[FirstViewController alloc] init];
UINavigationController *firstNavigationController = [[UINavigationController alloc] initWithRootViewController: fvc];
SecondViewController *svc = [[SecondViewController alloc] init];
UINavigationController *secondNavigationController = [[UINavigationController alloc] initWithRootViewController: svc];
// Initialize tab bar controller, add tabs controllers
UITabBarController *tabBarController = [[UITabBarController alloc] init];
tabBarController.viewControllers = #[firstNavigationController, secondNavigationController];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.rootViewController = tabBarController;
[self.window makeKeyAndVisible];
return YES;
}
// FirstViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.title = #"One";
self.view.backgroundColor = [UIColor orangeColor];
UITableViewController *vc1 = [[UITableViewController alloc] init];
UITableViewController *vc2 = [[UITableViewController alloc] init];
UITableViewController *vc3 = [[UITableViewController alloc] init];
vc1.view.backgroundColor = [UIColor redColor];
vc2.view.backgroundColor = [UIColor blueColor];
vc3.view.backgroundColor = [UIColor greenColor];
self.viewControllers = #[vc1, vc2, vc3];
self.segmentTitles = #[#"Red", #"Blue", #"Green"];
self.segmentedControl = [[UISegmentedControl alloc] initWithItems: self.segmentTitles];
[self.segmentedControl addTarget: self
action: #selector(segmentClicked:)
forControlEvents: UIControlEventValueChanged];
self.navigationItem.titleView = self.segmentedControl;
self.segmentedControl.selectedSegmentIndex = 0;
// set the first child vc:
UIViewController *vc = self.viewControllers[0];
[self addChildViewController: vc];
vc.view.frame = self.view.bounds;
[self.view addSubview: vc.view];
self.currentVC = vc;
}
- (void)segmentClicked:(id)sender
{
if (sender == self.segmentedControl)
{
NSUInteger index = self.segmentedControl.selectedSegmentIndex;
[self loadViewController: self.viewControllers[index]];
}
}
- (void)loadViewController:(UIViewController *)vc
{
[self addChildViewController: vc];
[self transitionFromViewController: self.currentVC
toViewController: vc
duration: 1.0
options: UIViewAnimationOptionTransitionFlipFromBottom
animations: ^{
[self.currentVC.view removeFromSuperview];
vc.view.frame = self.view.bounds;
[self.view addSubview: vc.view];
} completion: ^(BOOL finished) {
[vc didMoveToParentViewController: self];
[self.currentVC removeFromParentViewController];
self.currentVC = vc;
}
];
}
So obviously my question is, why does this happen, and what can I do to fix it?
Edit: adding screenshots.
EDIT: Based on the answer below I changed the code in the animation block to:
[self.currentVC.view removeFromSuperview];
if ([vc.view isKindOfClass: [UIScrollView class]])
{
UIEdgeInsets edgeInsets = UIEdgeInsetsMake(self.topLayoutGuide.length, 0, self.bottomLayoutGuide.length, 0);
[UIView performWithoutAnimation: ^{
vc.view.frame = self.view.bounds;
((UIScrollView *)vc.view).contentInset = edgeInsets;
((UIScrollView *)vc.view).scrollIndicatorInsets = edgeInsets;
}];
}
else
{
vc.view.frame = self.view.bounds;
}
[self.view addSubview: vc.view];
Now it works. I'm going to try this with a custom UIViewController as well.
The issue is that you do not set the correct content inset to each table view. The system attempts to do it for you, but I guess your setup is too complex for it, and it only does it for the first tableview that is loaded in viewDidLoad. In your loadViewController: method, when replacing the currently displayed view, make sure to set both the contentInset and scrollIndicatorInsets to the values of the previous view. I think the system will manage to set the correct insets later, in case you rotate to landscape. Try it. If it doesn't, you will need to do it on your own in viewDidLayoutSubviews.

Can't pop iOS viewController. Not sure, but I think it's something with the Navigation Controller

I'm having trouble trying to pop a view
App Delegate
#implementation MAAppDelegate
#synthesize navController;
#synthesize detailViewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Init the navController for the Master Detail View of the grade cells
UINavigationController *navController = [[UINavigationController alloc] init];
detailViewController = [[UIViewController alloc] init]; //step6
navController = [[UINavigationController alloc] initWithRootViewController:[[MAController alloc] init]]; //step7
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = navController; //step8
[self.window makeKeyAndVisible];
// Set MAController as rootViewController
//self.window.rootViewController = [[MAController alloc] init];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
// Use the insanely cool TSMessages to show network alerts
[TSMessage setDefaultViewController: self.window.rootViewController];
return YES;
}
First part of viewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.navigationController setNavigationBarHidden:YES];
UIBarButtonItem *newBackButton = [[UIBarButtonItem alloc] initWithTitle:#"Home" style:UIBarButtonItemStyleBordered target:self action:#selector(home:)];
self.navigationItem.leftBarButtonItem=newBackButton;
Later, when I change the viewController
NSLog(#"Opened progress report");
UIViewController *detailViewControl = [[UIViewController alloc] init];
// Set progress report as the view controller
[self.navigationController pushViewController:detailViewControl animated:YES];
UIImage *background = [UIImage imageNamed:#"bg"];
// Add static image bg
self.backgroundImageView = [[UIImageView alloc] initWithImage:background];
self.backgroundImageView.contentMode = UIViewContentModeScaleAspectFill;
[self.view addSubview:self.backgroundImageView];
// Add blurred layer to image when tableView goes in front of it
self.blurredImageView = [[UIImageView alloc] init];
self.blurredImageView.contentMode = UIViewContentModeScaleAspectFill;
self.blurredImageView.alpha = 0;
[self.blurredImageView setImageToBlur:background blurRadius:10 completionBlock:nil];
[self.view addSubview:self.blurredImageView];
[self.navigationController setNavigationBarHidden:NO];
So I don't understand why that when I do this, a selector from the button (that I know fires, because I get Righthtere in my log):
-(void)home:(UIBarButtonItem *)sender {
NSLog(#"Righthtere");
// Set progress report as the view controller
[self.navigationController popToViewController:self animated:YES];
}
It doesn't go back to the initial view controller.
You seem to be confusing popToViewController and popViewControllerAnimated. popViewControllerAnimated removes the current view from the stack and brings the new stack top the active view controller. popToViewController pops the stack until the listed view controller is on top of the stack.
Since you are calling popToViewController with self, it will look and see that the requested view controller is already on top of the stack and do nothing. If you wish to go back one view controller then your call should be.
[self.navigationController popViewControllerAnimated:YES];
I use the below code to pop the previous viewcontroller in iOS 8.
[self presentModalViewController:viewcontroller animated:YES];

iOS App with multiple view controllers

My current application on appDelegate loads a main.xib screen which only contains an two images background and logo. This screen behind code determines if the user is logged on the system if not it will show the login else it will show the dashboard.
The application has been created as a single view application, sample code of the appDelegate:
// Override point for customization after application launch.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
{
self.Main = [[vcMain alloc] initWithNibName:#"vcMain" bundle:nil];
self.window.rootViewController = self.Main;
[self.window makeKeyAndVisible];
}
else
{
self.MainiPad = [[vcMain_iPad alloc] initWithNibName:#"vcMain_iPad" bundle:nil];
self.window.rootViewController = self.MainiPad;
[self.window makeKeyAndVisible];
}
On the Main.m I have the following on the viewDidLoad:
if (islogged)
{
vcDashboard *vcDash = [[vcDashboard alloc] initWithNibName:#"vcDashboard" bundle:nil];
_ncMain = [[UINavigationController alloc] initWithRootViewController:vcDash];
_ncMain.navigationBar.barStyle = UIBarStyleBlackOpaque;
_ncMain.view.frame = self.view.bounds;
[self.view addSubview:_ncMain.view];
ViewActive = vDash;
}
else
{
vcLogin *login = [[vcLogin alloc] initWithNibName:#"vcLogin" bundle:nil];
login.modalPresentationStyle = UIModalPresentationFormSheet;
login.view.frame = self.view.bounds;
[self presentViewController:login animated:YES completion:nil];
}
There is a menu button available on the Dashboard that presents the user with a series of options to select another screen and when pressed it will activate the following method:
- (void)displayView:(NSString *)strView Notification:(NSNotification *)notification{
if(_ncMain)
{
[_ncMain.view removeFromSuperview];
_ncMain = nil;
}
if ([strView isEqual: #"Dashboard"])
{
vcDashboard *vcDash = [[vcDashboard alloc] initWithNibName:#"vcDashboard" bundle:nil];
_ncMain = [[UINavigationController alloc] initWithRootViewController:vcDash];
_ncMain.navigationBar.barStyle = UIBarStyleBlackOpaque;
_ncMain.view.frame = self.view.bounds;
[self.view addSubview:_ncMain.view];
ViewActive = vDash;
}
else if ([strView isEqual: #"Catalog"])
{
vcCatalog *vcCat = [[vcCatalog alloc] initWithNibName:#"vcCatalog" bundle:nil];
_ncMain = [[UINavigationController alloc] initWithRootViewController:vcCat];
_ncMain.navigationBar.barStyle = UIBarStyleBlackOpaque;
_ncMain.view.frame = self.view.bounds;
[self.view addSubview:_ncMain.view];
ViewActive = vCatalog;
}
else if ([strView isEqual: #"News"])
{
vcNews *vcNew = [[vcNews alloc] initWithNibName:#"vcNews" bundle:nil];
_ncMain = [[UINavigationController alloc] initWithRootViewController:vcNew];
_ncMain.navigationBar.barStyle = UIBarStyleBlackOpaque;
_ncMain.view.frame = self.view.bounds;
[self.view addSubview:_ncMain.view];
ViewActive = vNews;
}
}
My doubt here is I don't seem to know if this is the correct way of changing between screens when an option is selected from this menu and if is right to always addSubview to the principal screen. Don't know if using the navigationcontroller template is a solution. I'm concern of the memory consumed by the app when doing all of this, also I'm currently using ARC on the project.
I recommend you that avoid the addSubview method if possible. UiNAvigationController offer you a good way to handle different viewControllers. If you make addSubview the changeRotation event, for example, is not called. And when you make a pop the viewController is dealloced.
Good luck!

UISplitViewController - detailView is not showing

In AppDelegate we create an DetailView, then a loginView is presented (UIModalPresentationFullScreen) on top of it. After logged in, loginView is dismissed.
DetailView is having a tableView and when you select a cell/row and 2nd detailView is pushed.
What I did so far:
In AppDelegate I ask for UI_USER_INTERFACE_IDIOM() and when idiom is iPad i create a splitView:
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
FirstDetailViewController* fdvc = [[FirstDetailViewController alloc] initWithNibName:#"FirstDetailViewController" bundle:nil];
SecondDetailViewController* sdvc = [[SecondDetailViewController alloc] initWithNibName:#"SecondDetailViewController" bundle:nil];
UINavigationController* fdvcNavigationController = [[UINavigationController alloc] initWithRootViewController:fdvc];
UINavigationController* sdvcNavigationController = [[UINavigationController alloc] initWithRootViewController:sdvc];
splitViewController = [[UISplitViewController alloc] init];
splitViewController.viewControllers = [NSArray arrayWithObjects:fdvcNavigationController, sdvcNavigationController, nil];
After login my LoginView is dismissed and my UISplitViewController is shown, with the 1st DetailView on the left side (master). So everything went fine here.
Now I go to the FirstDetailViewController.m and search for the didSelectRowAtIndexPath because there I find the pushView to SecondDetailViewController in the "iPhone version".
And here is where i am stuck. I have tried several SplitView tutorials and read problems of others regarding splitview.
But I think my problem is some kind of "basic" because I am new to programming / iOS in general and don't know all my tools.
Any help would be appreciated.
When writing apps with a table view and some other sort of "display" view for items in the table, I do this so it works on both devices:
// In App Delegate...
-(BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Create views
MyTableViewController* myTable = [[MyTableViewController alloc] init];
MyDetailViewController* myDetail = [[MyDetailViewController alloc] init];
UINavigationController* tableNav = [[UINavigationController alloc] initWithRootViewController:myTable];
UINavigationController* detailNav = [[UINavigationController alloc] initWithRootViewController:myDetail];
// Check device type
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
// Use a split view
UISplitViewController* splitView = [[UISplitViewController alloc] init];
split.viewControllers = #[tableNav, detailNav];
self.window.rootViewController = split;
} else {
// Use a single view for iPhone
self.window.rootViewController = tableNav;
}
}
.
// In table view
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Check device type
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
// Tell the already visible detail view to load something
MyData* data = ...; \\ Get your thing to display however you want
[[NSNotificationCenter defaultCenter] postNotificationName:#"DisplayMyData" object:data];
} else {
// No detail controller exists yet, so create it
MyDetailViewController* myDetail = [[MyDetailViewController alloc] init];
[self.navigationController pushViewController:myDetail animated:YES];
[[NSNotificationCenter defaultCenter] postNotificationName:#"DisplayMyData" object:data];
}
}
.
// In detail view
-(void) viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(displayData:) name:#"DisplayMyData" object:nil];
}
-(void) displayData:(NSNotification*)notify {
MyData* data = (MyData*) notify.object;
... // Display however...
}

Issue Presenting View modally after a UISplitView loads

I'm new to UISplitView development, so I'm sure there is something obvious I'm doing wrong. I have a basic UISplitView iPad app that loads up with two UITableView controllers when the app launches. This works just fine.
What I am trying to do is immediately upon launch, presenting an "authentication" view modally so that a user will need to login before continuing. Here is the code I have so far which compiles and works without breaking, but the view is not showing.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
MasterViewController *masterViewController = [[MasterViewController alloc] initWithNibName:#"MasterViewController" bundle:nil];
UINavigationController *masterNavigationController = [[UINavigationController alloc] initWithRootViewController:masterViewController];
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
UINavigationController *detailNavigationController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
self.splitViewController = [[UISplitViewController alloc] init];
self.splitViewController.delegate = detailViewController;
self.splitViewController.viewControllers = [NSArray arrayWithObjects:masterNavigationController, detailNavigationController, nil];
masterViewController.detailViewController = detailViewController;
masterViewController.managedObjectContext = self.managedObjectContext;
self.window.rootViewController = self.splitViewController;
[self presentAuthenticate];
[self.window makeKeyAndVisible];
applicationDidLaunch = YES;
return applicationDidLaunch;
}
- (void) presentAuthenticate {
AuthenticateViewController *loginController = [[AuthenticateViewController alloc] initWithNibName:#"AuthenticateViewController" bundle:nil];
[loginController setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[loginController setModalPresentationStyle:UIModalPresentationFormSheet];
if ([self.splitViewController respondsToSelector:#selector(presentViewController:animated:completion:)]) {
[self.splitViewController presentViewController:loginController animated:NO completion:nil];
} else {
[self.splitViewController presentModalViewController:loginController animated:NO]; //iOS 4 works fine with or without animation
}
}
I defined the AuthenticateViewController as a View with a few textfields in it and have it wired to the File's Owners view.
Thanks ahead of time!
A viewcontroller will not allow to push/present on anotherview unless and until the view is complete loading.
Simple saying we are not allow to call presentModalViewController/pushViewController in a viewcontroller viewDidLoad/viewWillAppear. we need to call this in viewDidAppear.
I had the same issue you said.
Some Solution I can say are,
Do the loading of AuthenticateViewController after [self.window makeKeyAndVisible]; and in a performSelctor (may be with a delay).
Move the code to display AuthenticateViewController in SplitView's DetailView controller viewDidAppear.
thanks,
Naveen Shan

Resources