When I present a UINavigationController modally and then push (without animation) some UIViewcontrollers onto it, the finally shown navigationItem.title is different from what I would expect.
I tried to narrow my issue down and came up with the following short code to duplicate the issue:
#implementation DummyRootVC
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
UINavigationController* nc = [[UINavigationController alloc] initWithRootViewController:[[Dummy1VC alloc] init]];
[self presentViewController:nc
animated:false
completion:nil];
}
#end
#implementation Dummy1VC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor=[UIColor whiteColor];
self.navigationItem.title= #"DUMMY1";
[self.navigationController pushViewController:[[Dummy2VC alloc] init]
animated:false];
}
#end
#implementation Dummy2VC
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title= #"DUMMY2";
self.view.backgroundColor=[UIColor yellowColor];
[self.navigationController pushViewController:[[Dummy3VC alloc] init]
animated:false];
}
#end
#implementation Dummy3VC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor=[UIColor orangeColor];
self.navigationItem.title= #"DUMMY3";
[self.navigationController pushViewController:[[Dummy4VC alloc] init]
animated:false];
}
#end
#implementation Dummy4VC
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title= #"DUMMY4";
self.view.backgroundColor=[UIColor greenColor];
for (UIViewController* vc in self.navigationController.viewControllers) {
NSLog(#"VC Stack: %# Title:%#",vc,vc.navigationItem.title);
}
NSLog(#"End");
}
#end
In AppDelegate I set:
self.window.rootViewController = [[DummyRootVC alloc] init];
When running that code the displayed title is „Dummy 2“ and the back-button is named „Dummy 4“, while I actually would expect a title „Dummy 4“ and a back-button named „Dummy 3“.
The log shows as expected the correct ViewController-Stack and the backgroundColor is (as expected) green (and interestingly is still green after one "back" click):
VC Stack: Title:DUMMY1
VC Stack: Title:DUMMY2
VC Stack: Title:DUMMY3
VC Stack: Title:DUMMY4
End
I could work around that issue since it seems somewhat related to the non-animation setting in combination with presenting the navigation controller modally, but I would like to understand the underlying issue…
Can anyone provide me with some insight? Is this some bug or am I doing (as I suspect) just something the wrong way?
Thanks!
Put the push/present from viewDidLoad to viewWillAppear or viewDidAppear so that the viewController can init it's subview correctly to avoid problem :D
Related
I have 2 UIViewControllers and want to have UINavigationController with UINavigationBar & UINavigationItem on them. But my code doesn't working ..
here is my code :
#import "testView1.h"
#import "testView2.h"
#interface testView1 ()
#end
#implementation testView1
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor darkGrayColor];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:self];
testView2 *detail = [testView2 new];
[navController pushViewController:detail animated:YES];
}
Try to embed navigation controller in storyboard as below :
First select your testView1 in storyboard .
Select Navigation Controller
And Changes as below
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
/// testView2 *detail = [testView2 new];
testView2 *detail = [self.storyboard instantiateViewControllerWithIdentifier:#"testView2 Identifier"]; // if you have add controller in storyboard
[self.navigationController pushViewController:detail animated:YES];
}
You won't see the first View Controller because you already push the second view controller upon the navigation controller.
This is my code:
VideoDetailViewController *VideoDetailVC = [self.storyboard instantiateViewControllerWithIdentifier:#"VideoDetailViewController"];
UINavigationController * navigationController = (UINavigationController *) [[self tabBarController] selectedViewController];
[self.tabBarController setSelectedIndex:0];
[navigationController pushViewController:VideoDetailVC animated:YES];
I am writing this code in tabbar's second index. I want to directly go to the Video Detail screen, which is child view controller of tabbar's zero index. Everything works fine but what I see is: it shows a parent view controller which is tabbar's zero index. After a second it pushes to the Video Detail screen. I just don't want to show the zero index. What is the solution? Help will be appreciated.
EDITED:
#import "TWPhotoCollectionViewCell.h"
#implementation TWPhotoCollectionViewCell
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.imageView = [[UIImageView alloc] initWithFrame:self.bounds];
self.imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.imageView.layer.borderColor = [UIColor blueColor].CGColor;
[self.contentView addSubview:self.imageView];
}
return self;
}
- (void)setSelected:(BOOL)selected {
[super setSelected:selected];
self.imageView.layer.borderWidth = selected ? 2 : 0;
}
#end
Replace below line
[navigationController pushViewController:VideoDetailVC animated:YES];
With
[navigationController pushViewController:VideoDetailVC animated:NO];
It may solve your issue.
Setup: 'VC1' creates 'NavigationVC' with a root view controller 'VC2' and presents it modally with presentation style UIModalPresentationFormSheet. 'VC2' shows up inside the nav controller in the middle of the screen with the correct size.
Issue: As I continue to push view controllers onto the modal NavVC I would like them to resize. NSLog of my preferredContentSize in each view controller that is pushed verifies that my constraints are correct and the sizes are in fact different. However I have experimented extensively and have not figured out how to change the size of the modal after it has been presented.
#implementation VC1()
- (void) viewDidLoad{
VC1* vc1 = [self getNextVC];
NavVC* navVC = [[UINavigationViewController alloc] initWithRootViewController:vc1];
[navVC setModalPresentationStyle:UIModalPresentationFormSheet];
[self presentViewController:navVC animated:YES completion:nil];
}
#end
#implementation NavVC()
- (CGSize) preferredContentSize{
CGSize size = [[self topViewController] preferredContentSize];
return size;
}
#end
#implementation VC2()
- (CGSize) preferredContentSize{
CGSize size = [[self view] systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return size;
}
#end
I’ve spent some time dealing with the same problem. Pushing the view controller on modal navigation controller for iOS8 resulted in having the view controller with the same size as the root view controller, while for iOS7 this second view controller had the default size of modal form sheet (540.f, 620.f). The only working solution, I could come up with so far, for both iOS7 and iOS8 is as follows:
ViewController1.m
#interface ViewController1 ()
#end
#implementation ViewController1
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.navigationController.view.superview.bounds = CGRectMake(0, 0, VC1_WIDTH, VC1_HEIGHT);
}
- (void)nextTapped:(id)sender {
ViewController2 *vc2 = [storyboard instantiateViewControllerWithIdentifier:#"ViewController2"];
[self.navigationController pushViewController:vc2 animated:NO];
// call after push
self.navigationController.view.superview.bounds = CGRectMake(0, 0,VC2_WIDTH,VC2_HEIGHT);
}
#end
Presenting ViewController1:
ViewController1 *vc1 = [storyboard instantiateViewControllerWithIdentifier:#"ViewController1"];
UINavigationController *navVC = [[UINavigationController alloc] initWithRootViewController:vc1];
navVC.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:navVC animated:NO completion:nil];
ViewController2.m
#import "ViewController2.h”
#interface ViewController2 ()
#end
#implementation ViewController2
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.navigationController.view.superview.bounds = CGRectMake(0, 0, VC2_WIDTH, VC2_HEIGHT);
}
- (void)nextTapped:(id)sender {
ViewController3 *vc3 = [storyboard instantiateViewControllerWithIdentifier:#"ViewController3”];
[self.navigationController pushViewController:vc3 animated:NO];
self.navigationController.view.superview.bounds = CGRectMake(0, 0,VC3_WIDTH,VC3_HEIGHT);
}
- (void)backTapped:(id)sender {
self.navigationController.view.superview.bounds = CGRectMake(0, 0,VC1_WIDTH,VC1_HEIGHT);
[self.navigationController popViewControllerAnimated:NO];
}
#end
ViewController3.m
#import "ViewController3.h”
#interface ViewController3 ()
#end
#implementation ViewController3
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.navigationController.view.superview.bounds = CGRectMake(0, 0, VC3_WIDTH, VC3_HEIGHT);
}
- (void)backTapped:(id)sender {
self.navigationController.view.superview.bounds = CGRectMake(0, 0,VC2_WIDTH,VC2_HEIGHT);
[self.navigationController popViewControllerAnimated:NO];
}
#end
Please note, if you’re adding text input fields to those view controllers pushed on modal navigation controller, for iOS8, keyboard presentation and dismissal will automatically resize them to the wrong size. Unfortunately I still don’t have proper fix for this issue.
I have two xib files and for each it's own class which overrides UIViewController class. I have a button in first xib file which should take me to new UIViewController when it's pressed. I have managed to make that transition in different ways:
UIViewController* secondViewController = [[UIViewController alloc] initWithNibName:#"SecondViewControllerName" bundle:[NSBundle mainBundle]];
[self.view addSubview:secondViewController.view];
or
UIViewController* secondViewController = [[UIViewController alloc] initWithNibName:#"SecondViewControllerName" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:secondViewController animated:YES];
In second UIViewController xib file I have also one button which should take me back to first UIViewController. But I don't know how to navigate back to first UIViewController.
I tried:
[self.navigationController popViewControllerAnimated:YES];
for second way of navigating to second UIViewController, but I get unrecognized selector sent to instance error.
Many thanks in advance for any kind of help.
[edit #1: Adding source code]
Here's source code of first view controller .m file:
#import "ViewController.h"
#import "SecondViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (IBAction)switchToSecondPage:(id)sender
{
SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
[UIView beginAnimations:#"flipping view" context:nil];
[UIView setAnimationDuration:1];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationTransition: UIViewAnimationTransitionCurlDown forView:self.view cache:YES];
[self.view addSubview:secondViewController.view];
[UIView commitAnimations];
}
#end
Here's source code of second view controller .m file:
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
- (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.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (IBAction)switchToFirstPage:(id)sender
{
[UIView beginAnimations:#"flipping view" context:nil];
[UIView setAnimationDuration:1];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
[UIView setAnimationTransition: UIViewAnimationTransitionCurlUp forView:self.view.superview cache:YES];
[self.view removeFromSuperview];
[UIView commitAnimations];
}
#end
Here's my AppDelegate.m file:
#import "AppDelegate.h"
#import "ViewController.h"
#implementation AppDelegate
#synthesize window = _window;
#synthesize viewController = _viewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
}
- (void)applicationWillTerminate:(UIApplication *)application
{
}
#end
Entire project is made as Single View Application and targeted for iOS 5.1 in XCode 4.3. I have found sample project which does the same thing I am having trouble with. Here's URL for that project: http://www.devx.com/wireless/Article/42476/0/page/2
It is just written for earlier iOS version. Is anyone able to see what am I doing wrong, since I am really out of ideas?
PS: With this animations in code, or without - same problem occurs, so animations aren't problem, I've checked that.
[edit #2: Adding error info]
Here's error description. In main method on return line as listed below:
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
I get this error:
Thread 1: EXC_BAD_ACCESS (code=1, address=0xc00000008)
[edit #3: SOLUTION]
I would like to thank #btype for his solution below. I found out why code from edit #2 didn't work. Problem was doing this:
SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
INSIDE OF IBAction METHOD! Looks like ARC wiped my UIViewController as soon as end of scope was reached and that's why I was getting informations about sending messages to released instance. I made SecondViewController private field in my ViewController class and after that everything worked just fine.
If anyone thinks my explanation is wrong and knows what really bothered me, feel free to answer to this question and correct me.
Edit the code:
in AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
ViewController *viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController];
self.window.rootViewController = navController;
[self.window makeKeyAndVisible];
return YES;
}
FirstViewController.m
- (IBAction)switchToSecondPage:(id)sender
{
[self.navigationController pushViewController:secondViewController animated:YES];
}
SeconViewController.m
- (IBAction)switchToFirstPage:(id)sender
{
[self.navigationController popToRootViewControllerAnimated:YES];
}
This should help.
I'm trying to develop an app (game) with this architecture:
Main view is a naviagtioncontroller based with navbar hidden
in Main view I need a light info
button to show a options/credits
flipsideview
this flipsideview must have another
navigationcotroller with a right bar
button to a "Done" system button
The problem is that the flipsideview doesn't show the done button and it seems to show the Main navigation controller...
This is the code.
AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
return YES;
}
Main View (loaded from a XIB). Extract only of showInfo:
-(IBAction) showInfo:(id)sender {
FlipSideViewController *controller = [[FlipSideViewController alloc] initWithNibName:#"FlipSideView" bundle:nil];
controller.delegate = self;
controller.title = #"Info";
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller];
navController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
navController.navigationBar.barStyle = UIBarStyleBlackOpaque;
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonItemStyleDone
target:controller action:#selector(done:)];
navController.navigationItem.rightBarButtonItem = doneButton;
controller.navController = navController;
[self presentModalViewController:navController animated:YES];
[doneButton release];
[controller release];
[navController release];
}
- (void)flipsideViewControllerDidFinish:(FlipSideViewController *)controller {
[self dismissModalViewControllerAnimated:YES];
}
FlipSideView. In the XIB I've only a blank view with outlet linked to the UIViewController view.
#protocol FlipsideViewControllerDelegate;
#interface FlipSideViewController : UIViewController {
id <FlipsideViewControllerDelegate> delegate;
UINavigationController *navController;
}
#property (nonatomic,assign) id <FlipsideViewControllerDelegate> delegate;
#property (nonatomic,retain) UINavigationController *navController;
-(IBAction)done;
#end
#protocol FlipsideViewControllerDelegate
- (void)flipsideViewControllerDidFinish:(FlipSideViewController *)controller;
#end
}
Implementation file...
- (void)viewDidLoad
{
[super viewDidLoad];
self.navController.navigationItem.title = #"Pippo";
}
#pragma mark - User Methods
- (IBAction)done {
[self.delegate flipsideViewControllerDidFinish:self];
}
The result is:
Main View showing without navigation
bar
Click on info button
Flipsideview showing with animation
and navigation bar with title "Info" and not "pippo
and not "Done" button on the right...
Where am I wrong??
Don't you get two navigationBar with navigationItems on FlipsideViewController? I am fighting with this bug now.