UINavigationController setViewControllers autorotation issue - ios

I present a UINavigationController with two view controllers in stack and present the last view controller first. And tapping the back button obviously goes back to the first view controller.
navCtrl = [[UINavigationController alloc] init];
ViewController1 *vc1 = [[ViewController1 alloc] init];
ViewController2* vc2 = [[ViewController2 alloc] init];
[navCtrl setViewControllers:[NSArray arrayWithObjects:vc1, vc2, nil] animated:NO];
[self presentViewController:navCtrl animated:YES completion:^{
}];
The problem is that when I push the back button on the navigation controller in landscape mode, view controller vc1 frame is incorrect. The frame is (0,0,320,568) and is laid out in landscape mode. I am running iOS 7. The autorotation code is not invoked on pressing the back button.
Whereas, If I present the navigation controller with the natural order vc1,vc2, I don't see any issue.
EDIT: FYI, here are the -viewWillAppear and -viewDidAppear calls in vc1 :
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.navigationController.navigationBar.barStyle = UIBarStyleBlackTranslucent;
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackTranslucent];
[[UIApplication sharedApplication] setStatusBarHidden:NO
withAnimation:UIStatusBarAnimationNone];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
NSLog(#"Frame = %#", NSStringFromCGRect(self.view.frame));
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Get status bar height if visible
if (![UIApplication sharedApplication].statusBarHidden) {
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
CGFloat statusBarHeight = MIN(statusBarFrame.size.height, statusBarFrame.size.width);
// Set navigation bar frame
CGRect navBarFrame = self.navigationController.navigationBar.frame;
[self.navigationController setNavigationBarHidden:YES animated:NO];
[self.navigationController setNavigationBarHidden:NO animated:NO];
/*
navBarFrame.origin.y = statusBarHeight;
self.navigationController.navigationBar.frame = navBarFrame;
*/
UIEdgeInsets e = UIEdgeInsetsMake(statusBarHeight + navBarFrame.size.height, 0, navBarFrame.size.height + 12, 0);
[_tableView setScrollIndicatorInsets:e];
[_tableView setContentInset:e];
}
_tableView.rowHeight = 75;
self.spinner = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite] autorelease];
spinner.frame = CGRectMake(145, 200, 30, 30);
[self.view addSubview:spinner];
[spinner startAnimating];
[self performSelector:#selector(refreshData) withObject:nil afterDelay:0.f];
}

I found the problem. For some old iOS bug workaround which I don't remember I had the following line in viewDidLoad
[self.navigationController.view setFrame: [self.navigationController.view bounds]];

There are a few things you need to check here:
You said you already checked, but lets be safe - check the Auto-Resizing on the view.
Do your view controllers implement the following method?
- (NSUInteger)supportedInterfaceOrientations;
Are you presenting the navigation controller as a child view controller? When doing that make sure the following method is returning its default YES
- (BOOL)shouldAutomaticallyForwardRotationMethods;
Check you are not adding child view controllers to the view of a UINavigationController as it doesn't forward the appearance methods, its best to handle that from within the view controllers on the navigation controller.
Failing all of the above you should put a breakpoint in viewWillAppear of vc1 and check its frame, if the frame is correct, but the navigationContoller.view frame is incorrect then you must check your auto-resizing masks.

Related

Long delay before view controller appears when using GCD and NSNotificationCenter

Having odd behavior that I just can't figure out - when I'm pushing my second view controller onto a UINavigationController, there is a 5-10 second delay before the new view appears. The second view controller is being pushed based on an NSNotificationCenter notification.
The oddness: the delay only occurs when the event is posted from a background thread and not on the main thread (see comments in sample code below).
Why is there such a delay? This occurs on both simulator and device.
Here's the console output. As you can see, all logs appear in a timely manner, just not the 2nd view controller which suddenly appears after about 5-10 seconds.
2015-04-27 06:58:47.973 DelayedViews[49845:5005806] Background thread started
2015-04-27 06:58:49.978 DelayedViews[49845:5005806] Pushing second view controller
2015-04-27 06:58:49.979 DelayedViews[49845:5005806] Done pushing second view controller
2015-04-27 06:58:49.980 DelayedViews[49845:5005806] In completion block
2015-04-27 06:58:49.980 DelayedViews[49845:5005783] Back on main thread
And here's my simple example AppDelegate.m which demonstrates the issue.
#import "AppDelegate.h"
#interface AppDelegate ()
#property (nonatomic, strong) UINavigationController *navController;
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
self.navController = [[UINavigationController alloc] init];
self.window.rootViewController = self.navController;
UIViewController *vc = self.viewControllerOne;
[self.navController pushViewController:vc animated:YES];
// register for notification center
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(pushSecondViewController) name:#"PushSecondVC" object:nil];
// start background thread that posts notification and calls completion block
void (^completion)() = ^void() {
NSLog(#"In completion block");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Back on main thread");
// If I do the notification here, the 2nd view controller is pushed immediately
// [[NSNotificationCenter defaultCenter] postNotificationName:#"PushSecondVC" object:self];
});
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(#"Background thread started");
[NSThread sleepForTimeInterval:2]; // simulate some work
// if the notification is posted here, there is a 5-10 second delay on simulator before second VC appears
// even though the 2nd VC was pushed onto navigationcontroller.
[[NSNotificationCenter defaultCenter] postNotificationName:#"PushSecondVC" object:self];
// call completion block now
completion();
});
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
- (UIViewController*)viewControllerOne {
UIViewController *vc = [[UIViewController alloc] init];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(8, 100, 100, 100)];
label.text = #"View #1";
[vc.view addSubview:label];
return vc;
}
- (UIViewController*)viewControllerTwo {
UIViewController *vc = [[UIViewController alloc] init];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(50, 200, 100, 100)];
label.text = #"View #2";
[vc.view addSubview:label];
return vc;
}
- (void)pushSecondViewController {
NSLog(#"Pushing second view controller");
UIViewController *vc = self.viewControllerTwo;
[self.navController pushViewController:vc animated:YES];
NSLog(#"Done pushing second view controller");
}
#end
Thanks, A-Live, that appears to be it.
I changed the pushSecondViewController to the following and it seems to work perfectly now. I wonder what is going on under-the-covers and why it takes so long for the view to appear?
- (void)pushSecondViewController {
if (![[NSThread currentThread] isMainThread]) {
dispatch_sync(dispatch_get_main_queue(), ^{
[self pushSecondViewController];
});
return;
}
NSLog(#"Pushing second view controller");
UIViewController *vc = self.viewControllerTwo;
[self.navController pushViewController:vc animated:NO];
NSLog(#"Done pushing second view controller");
}

In iOS 7, using UIModalPresentationCurrentContext for an animated modal presentation doesn't animate

In iOS 8, when presenting a modal (lets say with a transparent background), we need to set the segue (or the modal) to use the presentation style of UIModalPresentationOverCurrentContext. This is working as expected.
To accomplish the same thing for iOS 7, you instead, need to set the presenting view controller to have a modal presentation style of UIModalPresentationCurrentContext. Here's where I'm having an issue. I present the modal with animation, but it doesn't animate. After it is presented, everything works fine, even animating the dismissal. Further, if I change the presentation style to UIModalPresentationFullScreen, it animates correctly.
I've searched around and read other posts, but I can't find the cause of this or a solution.
ViewController *vcObj = [[ViewController alloc] initWithNibName:NSStringFromClass([ViewController class]) bundle:nil];
UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:vcObj];
if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
navCon.providesPresentationContextTransitionStyle = YES;
navCon.definesPresentationContext = YES;
navCon.modalPresentationStyle = UIModalPresentationOverCurrentContext;
[self presentViewController:navCon animated:NO completion:nil];
}
else {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[self presentViewController:navCon animated:NO completion:^{
[navCon dismissViewControllerAnimated:NO completion:^{
appDelegate.window.rootViewController.modalPresentationStyle = UIModalPresentationCurrentContext;
[self presentViewController:navCon animated:NO completion:nil];
appDelegate.window.rootViewController.modalPresentationStyle = UIModalPresentationFullScreen;
}];
}];
}

UIDeviceOrientationPortrait , ios 6 & 7 , InterfaceOrientation

i have this code i want to go to another uiview but i want to have showing this view (second view) in portrait mode only and the notification in first view doesn't work in second view ;
is there any way to change the for this
[[NSNotificationCenter defaultCenter] removeObserver:self];
view2 *viewController = [[[view2 alloc] init] autorelease];
UINavigationController *navController = [[[UINavigationController alloc] initWithRootViewController:viewController] autorelease];
viewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:navController animated:NO];
i want exactly one function same this code in ios >6
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return NO;
}
Try subclassing the UINavigationController and override the following methods :
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}

UINavigationController, how to hide tabbar in second level viewController then show tabbar in third level viewController

here is a piece of my code, but in this way, when I push the third level view controller, the tabbar won't show.
//at first level
SecondLevelViewController *_2vc = [[SecondLevelViewController alloc]initWithNibName:#"SecondLevelViewController" bundle:nil];
_2vc.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:_2vc animated:YES];
//at second level
ThirdLevelViewController *_3vc = [[ThirdLevelViewController alloc]initWithNibName:#"ThirdLevelViewController" bundle:nil];
_3vc.hidesBottomBarWhenPushed = NO;
[self.navigationController pushViewController:_3vc animated:YES];
// Load the view
AddViewController *aController = [[AddViewController alloc] init];
// Set the view title
aController.title = #"Add View";
// hide tabbar
aController.hidesBottomBarWhenPushed = YES;
// add it to stack.
[[self navigationController] pushViewController:aController animated:YES];
-(void)viewWillAppear: (BOOL)animated
{
[super viewWillAppear:animated];
[self.tabBarController.tabBar setHidden:YES];
}
-(void)viewWillDisappear: (BOOL)animated
{
[super viewWillDisappear:animated];
[self.tabBarController.tabBar setHidden:NO];
}
Instead of setting the values of hidesBottomBarWhenPushed when you initialise the view controllers, you should instead handle the hiding mechanism in the -(void)viewWillAppear:(BOOL)animated in the view controllers instead.
An example of this implementation would be:
In SecondLevelViewController.m
-(void)viewWillAppear:(BOOL)animated
{
[_bottomBar setHidden:YES];
}
In ThirdLevelViewController.m
-(void)viewWillAppear:(BOOL)animated
{
[_bottomBar setHidden:NO];
}

Orientation Problem while using insertSubview

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];

Resources