I am trying to do a pretty simple thing: support only horizontal orientation in my app, except for 1 stack of view controllers. For simplicity, lets say I have two UIViewControllers. Lets call them maskLandscapeVC and maskAllVC. Each is separately embedded in its own instance of a custom UINavigationController. Here is the code for the navigation controller.
#import "MPTLoginNav.h"
#interface MPTLoginNav ()
#end
#implementation MPTLoginNav
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark
#pragma Interface Orientaiton Methods
- (NSUInteger)supportedInterfaceOrientations
{
if (self.isMaskAllStack)
return UIInterfaceOrientationMaskAll;
return UIInterfaceOrientationMaskLandscape;
}
- (BOOL)shouldAutorotate
{
return YES;
}
#end
On maskLandscapeVC, this code works fine and only the two horizontal orientations can be used.
Users can navigate from maskLandscapeVC to maskAllVC. The following code takes care of that
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Storyboard" bundle:[NSBundle bundleForClass:[self class]]];
self.maskAllVC = [storyboard instantiateViewControllerWithIdentifier:#"maskAllVC"];
[self.navigationController pushViewController:self.maskAllVC animated:YES];
At maskAllVC, the code also works great and all four orientations are supported.
Once on maskAllVC, the user switches to VERTICAL orientation. maskLandscapeVC is a delegate of maskAllVC. While in the vertical orientation on maskAllVC, the user presses some button. The button does a call to the delegate (maskLandscapeVC), based on some conditions, maskLandscapeVC decides to dismiss maskAllVC. It uses the following code
[self.maskAllVC.navigationController popViewControllerAnimated:YES];
[self.maskAllVC.navigationController dismissViewControllerAnimated:YES completion:nil];
Now back on maskLandscapeVC, the orientation is still vertical and of course my view is completely messed up because of it.
I think because you pop it to go back it doesn't really refresh the whole ViewController like it does the first time. Using viewDidLoad instead of viewWillAppear might be the problem. viewDidLoad is only called once.
Do you do a complete layoutSubview refresh if you set breakpoints? Does it call into supportedInterfaceOrientations at all when you pop? What happens if you move your viewDidLoad code to viewWillAppear?
Related
I'm trying to push a view controller with a visible navigation bar from a view controller with a hidden navigation bar.
I tried all sorts of combinations of [[self navigationController] setNavigationBarHidden:YES animated:NO]; in viewWillAppear, viewDidAppear, viewWillDisappear... etc.
// First View Controller
#implementation FirstViewController
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[self navigationController] setNavigationBarHidden:YES animated:NO];
NSLog(#"[%# viewWillAppear]", self);
}
#end
// Second View Controller
#implementation SecondViewController
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[self navigationController] setNavigationBarHidden:NO animated:NO];
NSLog(#"[%# viewWillAppear]", self);
}
#end
Nothing worked. I also tried custom code to "animate" a push and pop, which works, BUT I lose the edge swipe and view panning. Before I dig deeper, just want to make sure I'm not reinventing the wheel.
The Starbucks app is what I'm trying to mimic.
The root view controller of the app (the dark background view) is full screen and notice how it doesn't have a UINavigationBar. But when you tap on one of the buttons, it pushes on a view controller (the light background view) WITH a UINavigationBar. From there, if you tap the "back" arrow, it view controller pops with the navigation bar. Interactive pop swipe gesture also works.
It is possible without hacking together a solution by yourself. Here is what you do:
Your root viewController:
#implementation ViewController
....
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES animated:animated];
}
#end
And the pushed viewController:
#implementation SecondViewController
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:NO animated:animated];
}
#end
This will do. It also keeps the interactive transition working ;)
I find it disturbing, however, that this type of functionality is not documented at all by apple. - You can also hide and show toolbars with these 'call-points' (inside viewWillAppear:)
EDIT
I just realized that this is the same code you wrote in your question. Please test it again. I am 100% sure that this works - I used this functionality in one of my apps, too.
Please also note that my code does use animated:animated instead of your animated:NO. This may be the crucial point here :)
I just set up two view controllers to test this back and forth.
#interface VC1 ()
#end
#implementation VC1
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.navigationController.navigationBarHidden = YES;
}
#end
and a second
#import "ViewControllerTwo.h"
#interface ViewControllerTwo ()
#end
#implementation ViewControllerTwo
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.navigationController.navigationBarHidden = NO;
}
#end
VC1 is embedded in a navigationController (which is the root controller for the app), with a button that navigates to ViewControllerTwo. I have a push segue from VC1 -> ViewControllerTwo, this method works. When I tap on the button, the view controller is visible on ViewControllerTwo, when I press back, the navigationBar is gone.
I'm currently working on a project that is tab bar based and out of my four tabs three display just table data or images. My problem is stemming from trying to use a tab to display a PDF file with vfr. I can click the tab when the program first loads and everything appears to be working as it should. When I click done the Reader view controller is dismissed but the underlying controller is still sitting there empty. Since the view is still in place clicking back and forth through my tabs never allows vfr to reload its view since viewDidLoad is whats I'm using to call it. How do I dismiss the underlying controller? Or is there a better method to call vfr in a story board tab based app?
my controller code that calls vfr
#implementation testViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
NSString *file = [[NSBundle mainBundle] pathForResource:#"emsformulary" ofType:#"pdf"];
ReaderDocument *document = [ReaderDocument withDocumentFilePath:file password:nil];
if (document != nil) {
ReaderViewController *readerViewController =[[ReaderViewController alloc] initWithReaderDocument:document];
readerViewController.delegate = self;
readerViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
readerViewController.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:readerViewController animated:YES completion:nil];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)dismissReaderViewController:(ReaderViewController *)viewController
{
[self dismissViewControllerAnimated:NO completion:nil];
}
#end
dismissReaderViewController is called by the done button in vfr. Once called vfr pdf view is gone but a blank view remains.
Figured it out, I was calling my controller code with viewDidLoad. Once I dismissed it, it was never called again. Moving the code to viewWillAppear fixed my controller presentation issue. As for the not dismissing the underlying view, I found the method to call a view by its tabbarcontroller array index when I pressed the done button inside vfr.
This question already has an answer here:
Navigation bar overlapped by status bar
(1 answer)
Closed 9 years ago.
Note: This problem seems to occur after modal views are dismissed.
I am subclassing my UINavigationController to make it so that it can't be rotated when on a view that contains navigation. Here is what the code looks like... very simple. I just set my UINavigationController class in Interface Builder to NavViewController:
#import "NavViewController.h"
#interface NavViewController ()
#end
#implementation NavViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationPortrait;
}
- (BOOL)shouldAutorotate
{
return NO;
}
#end
The problem is, this is causing my top bar position to get messed up when coming back from a modal view. For example, here is what it looks like when I navigate to the view (Just FYI: the black on the top status bar is built into the background image for the Nav bar):
Then on my view controller with the Nav bar on it, I call this code to get a picture from the photo album:
- (IBAction)goToPhotoAlbum
{
UIImagePickerController *mypicker = [[UIImagePickerController alloc] init];
mypicker.delegate = self;
mypicker.allowsEditing = NO;
mypicker.wantsFullScreenLayout = YES;
mypicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[self presentViewController:mypicker animated:YES completion:nil];
}
and then I dismiss it:
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
[self dismissViewControllerAnimated:YES completion:nil];
}
When the view reappears, the Nav bar now looks like this:
When I comment out the following code from my Nav subclass, like so, everything seems to work fine again and the bar is positioned correctly:
/*
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationPortrait;
}
- (BOOL)shouldAutorotate
{
return NO;
}
*/
Anyone know what is going on here and how I can maybe fix this?
Answering my own question:
This is actually a duplicate of Navigation bar overlapped by status bar
The fix was to change my code in the subclass to this:
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (BOOL)shouldAutorotate
{
return YES;
}
I'm trying to get my app to go to the next screen. I eneded up creating a simple test app with 2 screens
On the first vue I have a button connected to the following code to call up the next screen
- (IBAction)atest:(id)sender {
if (mHome == nil)
mHome = [[cHome alloc] initWithNibName:#"cHome" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:mHome animated:YES];
}
I put a break point to make sure the code exicutes, it does but..... the next screen does not come up.
complete code
#import <UIKit/UIKit.h>
#interface cHome : UIViewController{
}
- (IBAction)atest:(id)sender;
#end
#interface cHome ()
#end
#implementation cHome
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (IBAction)atest:(id)sender
{
[self.navigationController popToRootViewControllerAnimated:YES];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
Just like to add onto Unkn0wn and demonstrate how I presented certain views in an app.
Add a storyboard id for your view controller
click on your view controller in your storyboard hierarchy
click the identity inspector
add a storyboard id as a string in the storyboard id box
I then did something like this in my code:
[self presentViewController:[self.storyboard nstantiateViewControllerWithIdentifier:#"Paint"] animated:NO completion:nil];
obviously, your identifier would not be "Paint" but that's what I used. This was my simplist approach, hope it helps good luck!
NOTE: I did not use navigation controllers in my app.
EDIT
would like to point out, that this method kind of makes the controller appear out of nowhere with no animation (I had special animation for my controls as a transition between view controllers so I didn't need to animate the controller itself)
If you want to push a view controller, you can easily create a push segue, from your first view controller to your second view controller, and then push using
[self performSegueWithIdentifier:#"yourSegueIdentifier" sender:self];
"push segues" can be created both via code or in your storyboard file.
You have a mistake in your code:
mHome = [[cHome alloc] initWithNibName:#"cHome" bundle:[NSBundle mainBundle]];
I think it's the nib name. It should be mHome not cHome.
mHome = [[cHome alloc] initWithNibName:#"mHome" bundle:[NSBundle mainBundle]];
You basically cannot push two of the same view controller on one navigation controller.
I'm new to iOS development, and I know I'm missing something simple but I'm not sure what it is... I have a simple "Hello World" app, and I'm trying to get my initial view controller to load.
Steps taken:
I created an empty project
I added a custom view controller class that extends UIViewController:
#import "WebViewController.h"
#interface WebViewController ()
#end
#implementation WebViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
I added a Storyboard using Apple's wizard.
I added a view controller to that storyboard.
I set my custom controller as the "Custom Class" of that view controller.
I checked the "Is Initial View Controller" checkbox.
But when I run the app, my controller does not load (viewDidLoad is never called). What am I missing?
Sorry about the noob question...
Enlarge the image. Where it says "Main Interface", select the storyboard you want to launch with from this menu. You may to shake Xcode a bit before your storyboard shows up in the list. If it's now showing up, try things like saving the storyboard file as well as cleaning/building your project. Image is borrowed from raywenderlich.com.
In your - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions { method in your app delegate .m file:
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:
#"MainStoryboard" bundle:nil];
UIViewController *initViewController = [storyBoard
instantiateInitialViewController];
[self.window setRootViewController:initViewController];
in storyboard you dont need - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil; As your storyboard the WebViewController is inital view controller so you dont need any thing in appDelegate. The WebViewController will auto call