viewDidLoad() method not called after simultaneous pop and push to same view - ios

I am using this code for the navigation view controller in the start
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
splashViewController *rootVC = [[splashViewController alloc]initWithNibName:#"splashViewController" bundle:nil];
self.navigator = [[UINavigationController alloc] initWithRootViewController:rootVC];
self.window.rootViewController = self.navigator;
[self.window makeKeyAndVisible];
after that i am using this simple method to push to next view
ScoreboardListViewController *SLvc = [[ScoreboardListViewController alloc]initWithNibName:#"ScoreboardListViewController" bundle:nil];
[self.navigationController pushViewController:SLvc animated:YES];
and using this to pop out from the view
[self.navigationController popViewControllerAnimated:YES];
but when i poppet and again push to the same view with different property values then viewdidload method did not run
it only runs if i just to any other view and then push to this view
i was not able to understand this abnormal behaviour. as when ever i push to any view then this viewdidload should be executed.....
for example i am doing this to get to the view
->
SLvc.trigger = #"sold";
SLvc.lblHeader.text = value;
[self.navigationController pushViewController:SLvc animated:YES];
-> then i popped out by back button by using this
[self.navigationController popViewControllerAnimated:YES];
-> then i push to the same view with different property
SLvc.trigger = #"earning";
SLvc.lblHeader.text = value;
[self.navigationController pushViewController:SLvc animated:YES]
and this time viewdidload didn't run.

This is because you are holding a strong pointer to SLvc controller. When you pop it, the view controller is retained. Initializing a new controller every time you push will solve your problem.

// below method called when pop out from the view
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}

You can try these in your viewcontroller:
-(void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
// your code here
}
or
-(void)awakeFromNib
{
[super awakeFromNib];
// your code here
}
viewDidLoad() -Called after init(coder:) when the view is loaded into memory.
Similar to viewWillAppear, this method is called just before the view disappears from the screen. And like viewWillAppear, this method can be called multiple times during the life of the view controller object. It’s called when the user navigates away from the screen – perhaps dismissing the screen, selecting another tab, tapping a button that shows a modal view, or navigating further down the navigation hierarchy

viewDidLoad is called exactly once, when the ViewController is first loaded into the memory. This is where you want to instantiate any instance variables and build any views that live for the entire lifecycle of this ViewController. However, the view is usually not yet visible at this point.
However, viewWillAppear gets called every time the view appears.

Related

viewDidAppear and viewWillAppear methods are not called

I have a programmatically created UIViewController named as "VC" and on top of that I need to load my existing UIViewController.
I used below code to do that, and it's working fine.
I can see my existing UIViewController on "VC" but not detecting any of viewDidAppear or viewWillAppear in existing view controller.
I am getting data from viewDidAppear and viewWillAppear so all the time my existing view controller collection view is empty.
ExistingViewController* presObj= [self.storyboard instantiateViewControllerWithIdentifier:#"oad"];
[vc.view addSubview:presObj.view];
[self addChildViewController:presObj];
[presObj didMoveToParentViewController:self];
Have you initialize the storyboard class?
self.storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
so all the time my existing ViewController collection view is empty --> are you using collection view in your code?
First you need to add the addChildViewController. Then add that childviewcontroller view into your main view.
Just add the [self addChildViewController:presObj]; before adding to the view subview.
ExistingViewController* presObj= [self.storyboard instantiateViewControllerWithIdentifier:#"oad"];
[self addChildViewController:presObj];
[vc.view addSubview:presObj.view];
[presObj didMoveToParentViewController:self];
Ok, If you are loading like this then your ViewDidLoad () of existing view controller will load, and you can put a condition (if needed) then you can call ViewDidAppear () ViewWillAppear() like below
existingviewcontroller *obj=[[existingviewcontroller alloc]init];
[obj viewWillAppear:YES];
[obj viewDidAppear:YES];
This will work

Go to second viewcontroller in NavigationViewController

I have a UINavigationController which points to a UITableViewController (a list of items) where there is a segue from a cell to another UITableViewController (a screen to edit an item).
On first run of the application, I'd like to skip the first list and immediately go to the second screen, to edit a new item.
The problem is I need to pass the first UITableViewController, as I need to be able to go back to that one (or is there a way to set the controller the back button is pointing to?).
Things I've tried and failed:
Set a boolean shouldPresentNewItem on the UINavigationController and in the viewDidLoad if it is set to true, present the first UITableViewController, also setting a boolean so I can go to the edit screen.
Using self.navigationController!.popToViewController(arr[index] as UIViewController, animated: true) in the UINavigationControllers viewDidLoad. This gave an error as self.navigationController was nil. (I don't get why this happens)
How can this be done?
In navigation controller set some boolean indicating that you're going to show edit screen and in viewDidLoad just push edit view controller without animation:
- (void) viewDidLoad {
[super viewDidLoad];
if (self.presentEditScreen) {
self.presentEditScreen = NO;
EditViewController *e = [[DetailViewController alloc] init];
[self pushViewController:e animated:NO];
}
}
simplest way will be. just push from second view to first view
firstViewController *objFirstViewController = [[firstViewController alloc]initWithNibName:#"firstViewController" bundle:nil];
[self.navigationController pushViewController:objFirstViewController animated:No];

can we use table based navigation in single view application?

I have created single view iOS application with some basic functionality. and added the table view in simple view controller now I wanted to use table view with row navigation like(pushcontroller and popcontroller) is that possible and if it is, how we can set that.
If you need full support for push/pop operation, I would use a navigation controller.
On the other hand, you could simply present your detail view controllers when a table row is tapped:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
MyViewController* detailController = <CREATE THE VIEW CONTROLLER>;
[self presentViewController:detailController animated:YES completion:NULL];
}
In this case, your detailController will need to implement a sort of navigation bar or other mechanism to let the user dismiss the controller (through dismissViewControllerAnimated:completion:) and go back to the table view. (Using a navigation controller would instead take care of this in a canonical way.)
One major drawback of the simpler solution based on presenting/dismissing is the fact that all the view controllers presented this way are dismissed at once, so you cannot have multiple levels of navigation.
EDIT:
To add a navigation controller to your app, simply do something like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
MyViewController *myViewController = [[MyViewController alloc] initWithNibName:nil bundle:nil];
...
self.navigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
self.window.rootViewController = self.navigationController;
[self.navigationController pushViewController:myViewController animated:YES];
...
[self.window makeKeyAndVisible];
return YES;
}
At the moment, your didFinishLaunchingWithOptions method should use the addSubview method to make your main view controller (named MyViewController in my example) visible. You can replace that call by the code above to instantiate the navigation controller and push on to it your main view controller.
Alternatively, you could create a new Xcode project using the navigation-based template and move all of your source files over.

Unbalanced calls to begin/end appearance transitions for <UITabBarController: 0x197870>

I read SO about another user encountering similar error, but this error is in different case.
I received this message when I added a View Controller initially:
Unbalanced calls to begin/end appearance transitions for
<UITabBarController: 0x197870>
The structure of the app is as follow:
I got a 5-tab TabBarController linked to 5 View Controllers. In the initial showing tab, I call out a new View Controller to overlay as an introduction of the app.
I use this code to call the introduction view controller:
IntroVC *vc = [[IntroVC alloc] init];
[self presentModalViewController:vc animated:YES];
[vc release];
After this IntroVC view controller shows up, the above error shows.
p.s. I am using xCode 4.2 & iOS 5.0 SDK, developing iOS 4.3 app.
Without seeing more of the surrounding code I can't give a definite answer, but I have two theories.
You're not using UIViewController's designated initializer initWithNibName:bundle:. Try using it instead of just init.
Also, self may be one of the tab bar controller's view controllers. Always present view controllers from the topmost view controller, which means in this case ask the tab bar controller to present the overlay view controller on behalf of the view controller. You can still keep any callback delegates to the real view controller, but you must have the tab bar controller present and dismiss.
I fixed this error by changing animated from YES to NO.
From:
[tabBarController presentModalViewController:viewController animated:YES];
To:
[tabBarController presentModalViewController:viewController animated:NO];
As posted by danh
You can generate this warning by presenting the modal vc before the app is done initializing. i.e. Start a tabbed application template app and present a modal vc on top of self.tabBarController as the last line in application:didFinishLaunching. Warning appears. Solution: let the stack unwind first, present the modal vc in another method, invoked with a performSelector withDelay:0.0
Try to move the method into the viewWillAppear and guard it so it does get executed just once (would recommend setting up a property)
Another solution for many cases is to make sure that the transition between UIViewControllers happens after the not-suitable (like during initialization) procedure finishes, by doing:
__weak MyViewController *weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf presentViewController:vc animated:YES];
});
This is general for also pushViewController:animated:, etc.
I had the same problem. I called a method inside viewDidLoad inside my first UIViewController
- (void)viewDidLoad{
[super viewDidLoad];
[self performSelector:#selector(loadingView)
withObject:nil afterDelay:0.5];
}
- (void)loadingView{
[self performSegueWithIdentifier:#"loadedData" sender:self];
}
Inside the second UIViewController I did the same also with 0.5 seconds delay. After changing the delay to a higher value, it worked fine. It's like the segue can't be performed too fast after another segue.
I had the same problem when I need to Present My Login View Controller from another View Controller If the the User is't authorized, I did it in ViewDidLoad Method of my Another View Controller ( if not authorized -> presentModalViewController ). When I start to make it in ViewDidAppear method, I solved this problem. I Think that ViewDidLoad only initialize properties and after that the actual showing view algorithm begins! Thats why you must use viewDidAppear method to make modal transitions!
If you're using transitioningDelegate (not the case in this question's example), also set modalPresentationStyle to .Custom.
Swift
let vc = storyboard.instantiateViewControllerWithIdentifier("...")
vc.transitioningDelegate = self
vc.modalPresentationStyle = .Custom
I had this problem because of a typo:
override func viewDidAppear(animated: Bool) {
super.viewWillAppear(animated)
instead of
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
It was calling "WillAppear" in the super instead of "DidAppear"
I had lot of problem with the same issue. I solved this one by
Initiating the ViewController using the storyboad instantiateViewControllerWithIdentifier method. i.e Intro *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"introVC"];
[self.tabBarController presentModalViewController : vc animated:YES];
I have the viewcontroller in my storyboard, for some reason using only [[introvc alloc] init]; did not work for me.
I solved it by writing
[self.navigationController presentViewController:viewController
animated:TRUE
completion:NULL];
I had this problem with a third party code. Someone forgot to set the super inside of viewWillAppear and viewWillDisappear in a custom TabBarController class.
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// code...
}
or
- (void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// code...
}
I had the same error. I have a tab bar with 3 items and I was unconsciously trying to call the root view controller of item 1 in the item 2 of my tab bar using performSegueWithIdentifier.
What happens is that it calls the view controller and goes back to the root view controller of item 2 after a few seconds and logs that error.
Apparently, you cannot call the root view controller of an item to another item.
So instead of performSegueWithIdentifier
I used [self.parentViewController.tabBarController setSelectedIndex:0];
Hope this helps someone.
I had the same problem and thought I would post in case someone else runs into something similar.
In my case, I had attached a long press gesture recognizer to my UITableViewController.
UILongPressGestureRecognizer *longPressGesture = [[[UILongPressGestureRecognizer alloc]
initWithTarget:self
action:#selector(onLongPress:)]
autorelease];
[longPressGesture setMinimumPressDuration:1];
[self.tableView addGestureRecognizer:longPressGesture];
In my onLongPress selector, I launched my next view controller.
- (IBAction)onLongPress:(id)sender {
SomeViewController* page = [[SomeViewController alloc] initWithNibName:#"SomeViewController" bundle:nil];
[self.navigationController pushViewController:page animated:YES];
[page release];
}
In my case, I received the error message because the long press recognizer fired more than one time and as a result, my "SomeViewController" was pushed onto the stack multiple times.
The solution was to add a boolean to indicate when the SomeViewController had been pushed onto the stack. When my UITableViewController's viewWillAppear method was called, I set the boolean back to NO.
I found that, if you are using a storyboard, you will want to put the code that is presenting the new view controller in viewDidAppear. It will also get rid of the "Presenting view controllers on detached view controllers is discouraged" warning.
In Swift 2+ for me works:
I have UITabBarViewController in storyboard and I had selectedIndex property like this:
But I delete it, and add in my viewDidLoad method of my initial class, like this:
override func viewDidLoad() {
super.viewDidLoad()
self.tabBarController?.selectedIndex = 2
}
I hope I can help someone.
This error will be displayed when trying to present an UINavigationController that is lazily initialized via a closure.
Actually you need to wait till the push animation ends. So you can delegate UINavigationController and prevent pushing till the animation ends.
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
waitNavigation = NO;
}
-(void)showGScreen:(id)gvc{
if (!waitNavigation) {
waitNavigation = YES;
[_nav popToRootViewControllerAnimated:NO];
[_nav pushViewController:gvc animated:YES];
}
}
As #danh suggested, my issue was that I was presenting the modal vc before the UITabBarController was ready. However, I felt uncomfortable relying on a fixed delay before presenting the view controller (from my testing, I needed to use a 0.05-0.1s delay in performSelector:withDelay:). My solution is to add a block that gets called on UITabBarController's viewDidAppear: method:
PRTabBarController.h:
#interface PRTabBarController : UITabBarController
#property (nonatomic, copy) void (^viewDidAppearBlock)(BOOL animated);
#end
PRTabBarController.m:
#import "PRTabBarController.h"
#implementation PRTabBarController
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (self.viewDidAppearBlock) {
self.viewDidAppearBlock(animated);
}
}
#end
Now in application:didFinishLaunchingWithOptions:
PRTabBarController *tabBarController = [[PRTabBarController alloc] init];
// UIWindow initialization, etc.
__weak typeof(tabBarController) weakTabBarController = tabBarController;
tabBarController.viewDidAppearBlock = ^(BOOL animated) {
MyViewController *viewController = [MyViewController new];
viewController.modalPresentationStyle = UIModalPresentationOverFullScreen;
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
[weakTabBarController.tabBarController presentViewController:navigationController animated:NO completion:nil];
weakTabBarController.viewDidAppearBlock = nil;
};
you need make sure -(void)beginAppearanceTransition:(BOOL)isAppearing animated:(BOOL)animated and -(void)endAppearanceTransition is create together in the class.
I had the same issue. When developing I wanted to bypass screens. I was navigating from one view controller to another in viewDidLoad by calling a selector method.
The issue is that we should let the ViewController finish transitioning before transitioning to another ViewController.
This solved my problem: The delay is necessary to allow ViewControllers finish transitioning before transitioning to another.
self.perform(#selector(YOUR SELECTOR METHOD), with: self, afterDelay: 0.5)
For me this error occurred because i didn't have UIWindow declared in the upper level of my class when setting a root view controller
rootViewController?.showTimeoutAlert = showTimeOut
let navigationController = SwipeNavigationController(rootViewController: rootViewController!)
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
Ex if I tried declaring window in that block of code instead of referencing self then I would receive the error
I had this problem when I had navigated from root TVC to TVC A then to TVC B. After tapping the "load" button in TVC B I wanted to jump straight back to the root TVC (no need to revisit TVC A so why do it). I had:
//Pop child from the nav controller
[self.navigationController popViewControllerAnimated:YES];
//Pop self to return to root
[self.navigationController popViewControllerAnimated:YES];
...which gave the error "Unbalanced calls to begin/end etc". The following fixed the error, but no animation:
//Pop child from the nav controller
[self.navigationController popViewControllerAnimated:NO];
//Then pop self to return to root
[self.navigationController popViewControllerAnimated:NO];
This was my final solution, no error and still animated:
//Pop child from the nav controller
[self.navigationController popViewControllerAnimated:NO];
//Then pop self to return to root, only works if first pop above is *not* animated
[self.navigationController popViewControllerAnimated:YES];
I encountered this error when I hooked a UIButton to a storyboard segue action (in IB) but later decided to have the button programatically call performSegueWithIdentifier forgetting to remove the first one from IB.
In essence it performed the segue call twice, gave this error and actually pushed my view twice. The fix was to remove one of the segue calls.
Hope this helps someone as tired as me!
Swift 5
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
//Delete or comment the below lines on your SceneDelegate.
// guard let windowScene = (scene as? UIWindowScene) else { return }
// window?.windowScene = windowScene
// window?.makeKeyAndVisible()
let viewController = ListVC()
let navViewController = UINavigationController(rootViewController: viewController)
window?.rootViewController = navViewController
}

Navigation Controller is null

I have a split-view app that allows a user to select and display a thumbnail of a chosen image. I have placed a UIButton in the detailViewController using Interface Builder. When this button is pressed, I would like to have it change to a full screen view of the image. I have set up a new View Controller, called FullViewController and thought I had everything connected. The problem is that the navigation controller is null. I adjusted the AppDelegate.m to the following:
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after app launch.
// Set the split view controller as the window's root view controller and display.
self.window.rootViewController = self.splitViewController;
UINavigationController *nvcontrol =[[UINavigationController alloc] initWithRootViewController:fullViewController];
[window addSubview:nvcontrol.view];
[self.window makeKeyAndVisible];
return YES;
}
This is the function in the DetailViewController.m which is called when the button is pressed. The navigation controller comes up null in here.
//Function called when button is pressed - should bring up full screen view
- (IBAction) pressFullViewButtonFunction: (id) sender{
//viewLabel.text = #"Full View";
if (fullViewController == nil){
FullViewController *fullViewController = [[FullViewController alloc] initWithNibName:#"FullViewController" bundle:[NSBundle mainBundle]];
NSLog(#"fullViewController is %#", fullViewController);
self.fullViewController = fullViewController;
}
NSLog(#"self.navigationController is %#",self.navigationController);//this is null
[self.navigationController pushViewController:self.fullViewController animated:YES];
}
I'm not sure how to fix this. I've tried adding in the couple lines in the AppDelegate, but when it runs, the table in the root view doesn't show up and it no longer properly switches between portrait and landscape views.
I have the rest of the code readily available if that would help clarify. Just let me know!
Thanks.
From the code you post it is not possible to identify the problem, but two common reasons for self.navigationController to be nil are:
you did not push the object behind self on to the navigation controller in the first place; indeed it seems so, since the navigation controller is added as a subview of the split view controller; possibly you mean the opposite... not sure...
(sub-case of 1) you showed the object behind self using presentViewControllerModally.
When I say "the object behind self" I mean the instance of the class where pressFullViewButtonFunction is defined.
If you need more help, post the code where you push your controllers on to the navigation controller...
On a side note, if you do:
UINavigationController *nvcontrol =[[UINavigationController alloc] initWithRootViewController:fullViewController];
and nvcontrol is not an ivar, then you have a leak.
Hope this helps...

Resources