How to do some stuff in viewDidAppear only once? - ios

I want to check the pasteboard and show an alert if it contains specific values when the view appears. I can place the code into viewDidLoad to ensure it's only invoked once, but the problem is that the alert view shows too quickly. I know I can set a timer to defer the alert's appearance, but it's not a good work-around I think.
I checked the question iOS 7 - Difference between viewDidLoad and viewDidAppear and found that there is one step for checking whether the view exists. So I wonder if there's any api for doing this?
Update: The "only once" means the lifetime of the view controller instance.

There is a standard, built-in method you can use for this.
Objective-C:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if ([self isBeingPresented] || [self isMovingToParentViewController]) {
// Perform an action that will only be done once
}
}
Swift 3:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if self.isBeingPresented || self.isMovingToParentViewController {
// Perform an action that will only be done once
}
}
The call to isBeingPresented is true when a view controller is first being shown as a result of being shown modally. isMovingToParentViewController is true when a view controller is first being pushed onto the navigation stack. One of the two will be true the first time the view controller appears.
No need to deal with BOOL ivars or any other trick to track the first call.

rmaddy's answers is really good but it does not solve the problem when the view controller is the root view controller of a navigation controller and all other containers that do not pass these flags to its child view controller.
So such situations i find best to use a flag and consume it later on.
#interface SomeViewController()
{
BOOL isfirstAppeareanceExecutionDone;
}
#end
#implementation SomeViewController
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if(isfirstAppeareanceExecutionDone == NO) {
// Do your stuff
isfirstAppeareanceExecutionDone = YES;
}
}
#end

If I understand your question correctly, you can simply set a BOOL variable to recognize that viewDidAppear has already been called, ex:
- (void)viewDidAppear {
if (!self.viewHasBeenSet) { // <-- BOOL default value equals NO
// Perform whatever code you'd like to perform
// the first time viewDidAppear is called
self.viewHasBeenSet = YES;
}
}

This solution will call viewDidAppear only once throughout the life cycle of the app even if you create the multiple object of the view controller this won't be called after one time. Please refer to the rmaddy's answer above
You can either perform selector in viewDidLoad or you can use dispatch_once_t in you viewDidAppear. If you find a better solution then please do share with me. This is how I do the stuff.
- (void)viewDidLoad {
[super viewDidLoad];
[self performSelector:#selector(myMethod) withObject:nil afterDelay:2.0];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
static dispatch_once_t once;
dispatch_once(&once, ^{
//your stuff
[self myMethod];
});
}

By reading other comments (and based on #rmaddy 's answer), I know this is not what OP asked for, but for those who come here because of title of the question:
extension UIViewController {
var isPresentingForFirstTime: Bool {
return isBeingPresented() || isMovingToParentViewController()
}
}
UPDATE
You should use this method in viewDidAppear and viewWillAppear. (thanks to #rmaddy)
UPDATE 2
This method only works with modally presented view controllers and pushed view controllers. it's not working with a childViewController. using didMoveToParentViewController would be better with childViewControllers.

You shouldn't have issues in nested view controllers with this check
extension UIViewController {
var isPresentingForFirstTime: Bool {
if let parent = parent {
return parent.isPresentingForFirstTime
}
return isBeingPresented || isMovingFromParent
}
}

Try to set a BOOL value, when the situation happens call it.
#interface AViewController : UIViewController
#property(nonatomic) BOOL doSomeStuff;
#end
#implementation AViewController
- (void) viewWillAppear:(BOOL)animated
{
if(doSomeStuff)
{
[self doSomeStuff];
doSomeStuff = NO;
}
}
in somewhere you init AViewController instance:
AddEventViewController *ad = [AddEventViewController new];
ad.doSomeStuff = YES;
Not sure why you do this in ViewDidAppear? But if you want doSomeStuff is private and soSomeStuff was called only once, here is another solution by notification:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doSomeStuff) name:#"do_some_stuff" object:nil];
- (void) doSomeStuff
{}
Then post when somewhere:
[[NSNotificationCenter defaultCenter] postNotificationName:#"do_some_stuff" object:nil];

swift 5
I've tried isBeingPresented() or isMovingToParent.
But It doesn't work.
So I tried below code. and It's work for me!
override func viewDidAppear(_ animated: Bool) {
if (self.isViewLoaded) {
// run only once
}
}

You can use this function in ViewDidLoad method
performSelector:withObject:afterDelay:
it will call that function after delay. so you don't have to use any custom timer object.
and For once you can use
dispatch_once DCD block.Just performSelector in the dispatch_once block it will call performSelector only once when ViewDidLoad is called
Hope it helps

Related

In my viewDidAppear, how do I know when it's being unwound by a child?

When my child performs an unwind segue, my controller's viewDidAppear gets called.
In this method (and this method alone, I need to know whether it was from an unwind or not)
Note: the child is unwinding to the very first view controller, so this is an intermediate view controller, not the true root.
You should be able to use the following to detect in each controller if the exposure of the view controller was as a result of being pushed/presented, or as a result of being exposed as a result of pop/dismiss/unwind.
This may or may be enough for your needs.
- (void) viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
// Handle controller being exposed from push/present or pop/dismiss
if (self.isMovingToParentViewController || self.isBeingPresented){
// Controller is being pushed on or presented.
}
else{
// Controller is being shown as result of pop/dismiss/unwind.
}
}
If you want to know that viewDidAppear was called because of an unwind segue as being different from a conventional pop/dismiss being called, then you need to add some code to detect that an unwind happened. To do this you could do the following:
For any intermediate controller you want to detect purely an unwind in, add a property of the form:
/** BOOL property which when TRUE indicates an unwind occured. */
#property BOOL unwindSeguePerformed;
Then override the unwind segue method canPerformUnwindSegueAction:fromViewController:withSender: method as follows:
- (BOOL)canPerformUnwindSegueAction:(SEL)action
fromViewController:(UIViewController *)fromViewController
withSender:(id)sender{
// Set the flag indicating an unwind segue was requested and then return
// that we are not interested in performing the unwind action.
self.unwindSeguePerformed = TRUE;
// We are not interested in performing it, so return NO. The system will
// then continue to look backwards through the view controllers for the
// controller that will handle it.
return NO;
}
Now you have a flag to detect an unwind and a means to detect the unwind just before it happens. Then adjust the viewDidAppear method to include this flag.
- (void) viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
// Handle controller being exposed from push/present or pop/dismiss
// or an unwind
if (self.isMovingToParentViewController || self.isBeingPresented){
// Controller is being pushed on or presented.
// Initialize the unwind segue tracking flag.
self.unwindSeguePerformed = FALSE;
}
else if (self.unwindSeguePerformed){
// Controller is being shown as a result of an unwind segue
}
else{
// Controller is being shown as result of pop/dismiss.
}
}
Hopefully this meets your requirement.
For docs on handling the unwind segue chain see: https://developer.apple.com/library/ios/technotes/tn2298/_index.html
Here is a simple category on UIViewController that you can use to track whether your presented view controller is in the midst of an unwind segue. I suppose it could be flushed out more but I believe this much works for your case.
To use it you need to register the unwind segue from your unwind action method on the destination view controller:
- (IBAction) prepareForUnwind:(UIStoryboardSegue *)segue
{
[self ts_registerUnwindSegue: segue];
}
That's it. From your intermediate view controller, you can test if you are in the midst of an unwind segue:
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear: animated];
BOOL unwinding = [self ts_isUnwinding];
NSLog( #"%#:%#, unwinding: %#", self.title, NSStringFromSelector(_cmd), unwinding ? #"YES" : #"NO" );
}
There's no need to clean anything up; the segue will self-deregister when it ends.
Here's the full category:
#interface UIViewController (unwinding)
- (void) ts_registerUnwindSegue: (UIStoryboardSegue*) segue;
- (BOOL) ts_isUnwinding;
#end
static NSMapTable* g_viewControllerSegues;
#implementation UIViewController (unwinding)
- (void) ts_registerUnwindSegue: (UIStoryboardSegue*) segue
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
g_viewControllerSegues = [NSMapTable weakToWeakObjectsMapTable];
});
for ( UIViewController* vc = segue.sourceViewController ; vc != nil ; vc = vc.presentingViewController )
{
[g_viewControllerSegues setObject: segue forKey: vc];
}
}
- (BOOL) ts_isUnwinding
{
return [g_viewControllerSegues objectForKey: [self ts_topMostParentViewController]] != nil;
}
- (UIViewController *)ts_topMostParentViewController {
UIViewController *viewController = self;
while (viewController.parentViewController) {
viewController = viewController.parentViewController;
}
return viewController;
}
#end
Your question was really interesting to me, because I never used IB and segues before (don't judge me for that) and wanted to learn something new. As you described in your comments:
viewDidAppear will be called on B when C rewinds to A
So I come up with an easy custom solution to this:
protocol ViewControllerSingletonDelegate: class {
func viewControllerWillUnwind(viewcontroller: UIViewController, toViewController: UIViewController)
}
class ViewControllerSingleton {
static let sharedInstance = ViewControllerSingleton()
private var delegates: [ViewControllerSingletonDelegate] = []
func addDelegate(delegate: ViewControllerSingletonDelegate) {
if !self.containsDelegate(delegate) {
self.delegates.append(delegate)
}
}
func removeDelegate(delegate: ViewControllerSingletonDelegate) {
/* implement any other function by your self :) */
}
func containsDelegate(delegate: ViewControllerSingletonDelegate) -> Bool {
for aDelegate in self.delegates {
if aDelegate === delegate { return true }
}
return false
}
func forwardToDelegate(closure: (delegate: ViewControllerSingletonDelegate) -> Void) {
for aDelegate in self.delegates { closure(delegate: aDelegate) }
}
}
class SomeViewController: UIViewController, ViewControllerSingletonDelegate {
let viewControllerSingleton = ViewControllerSingleton.sharedInstance
func someFunction() { // some function where you'll set the delegate
self.viewControllerSingleton.addDelegate(self)
}
/* I assume you have something like this in your code */
#IBAction func unwindToSomeOtherController(unwindSegue: UIStoryboardSegue) {
self.viewControllerSingleton.forwardToDelegate { (delegate) -> Void in
delegate.viewControllerWillUnwind(unwindSegue.sourceViewController, toViewController: unwindSegue.destinationViewController)
}
/* do something here */
}
// MARK: - ViewControllerSingletonDelegate
func viewControllerWillUnwind(viewcontroller: UIViewController, toViewController: UIViewController) {
/* do something with the callback */
/* set some flag for example inside your view controller so your viewDidAppear will know what to do */
}
}
You also could modify the callback function to return something else, like controller identifier instead the controller itself.
I do everything programmatically, so please don't judge me for that too. ;)
If this code snippet won't help you, I'd still love to see some feedback.
Suppose the segue navigation is ViewController -> FirstViewController -> SecondViewController. There is an unwind from SecondViewController to ViewController. You can add in the intermediary FirstViewController the following code to detect unwind actions.
import UIKit
class FirstViewController: UIViewController {
var unwindAction:Bool = false
override func viewDidAppear(animated: Bool) {
if unwindAction {
println("Unwind action")
unwindAction = false
}
}
override func viewControllerForUnwindSegueAction(action: Selector, fromViewController: UIViewController, withSender sender: AnyObject?) -> UIViewController? {
self.unwindAction = true
return super.viewControllerForUnwindSegueAction(action, fromViewController: fromViewController, withSender: sender)
}
}
EDIT
After giving this some thought, I decided the solution to this depends on the kind of complexity that you are dealing with here. What exactly do you do when you do the unwind segue? The solutions given here are viable and they work -- only if you want to detect whether it is an unwind action. What if you want to pass the data between the point where the unwind is happening to the root? What if there is a complex set of preparations that you wanna do in one of the intermediate view controllers? What if you want to do both of these?
In such complex scenarios, I would immediately rule out overriding the unwind methods of the view controller. Doing such operations there will work, but it won't be clean. A method will be doing what it isn't supposed to do. Smell that? That's code smell.
What if, somehow a view controller could inform the next view controller in the hierarchy of the event happening? Better yet, how do we do this without tightly coupling these two?
Protocol.
Have a protocol definition something like:
protocol UnwindResponding {
prepareForUnwindSegue(segue:UISegue , formViewController:UIViewController, withImportantInfo info:[String,AnyObject])
}
Using protocol you will keep the relationship between the objects -- the hierarchy of view controllers in this case -- explicit. At the point of occurrence of a particular event, you will delegate the call to the next controller in the hierarchy informing about the happening of a particular event in another view controller. Here is an example:
override func prepareForSegue(segue:UIStoryboardSegue, sender:AnyObject?) {
if let unwindResponder = self.presentingViewController as? UnwindResponding where segue.identifier = "unwindSegue" {
unwindResponder.prepareForUnwindSegue(segue:UISegue, fromViewController:self,info:info)
}
}
In the intermediary view controller you can do something like:
extension IntermediaryViewController : UnwindResponding {
prepareForUnwindSegue(segue:UISegue , fromViewController:UIViewController, withImportantInfo info:[String,AnyObject]) {
if let unwindResponder = self.presentingViewController {
unwindResponder.prepareForUnwindSegue(segue,fromViewController:fromViewController, info:info)
}
unwindSegue = true
}
}
Granted, you wouldn't wanna do this if you just want to detect unwind segues. Maybe you do, you'll never know what will happen in the future. Never hurts to keep your code clean.
Add method in your parent view controller
#IBAction func unwindToParent(unwindSegue: UIStoryboardSegue) {
if let childViewController = unwindSegue.sourceViewController as? ChildViewController {
println("unwinding from child")
}
}
As an exemple if the unwind segue is related to a button, in the storyboard link your button to it's view controller exit
It will propose to link to unwindToParent method
Then each time the unwind segue is performed, the unwindToParent method will be called
You can override the function unwindForSegue:towardsViewController:, which is called when the ViewController is on the path of an unwind segue. It's meant to be used to reconfigure the ViewController.
Swift example:
override func unwind(for unwindSegue: UIStoryboardSegue, towardsViewController subsequentVC: UIViewController) {
}

-[PreviewViewController applicationWillSuspend]: message sent to deallocated instance 0x1806d9e0

My Application is getting crashed with the following error.
-[PreviewViewController applicationWillSuspend]: message sent to deallocated instance 0x1806d9e0
My application have two view controllers one is HomeViewController and other one is PreviewViewController.
In home view controller i am displaying a table view. When selecting the row of table view i am presenting the preview view controller.
I selected one row then preview view controller is presented.
PreviewViewController *previewController = [[PreviewViewController alloc]initWithPreviewImage:[[kfxKEDImage alloc] initWithImage:imgCaptured] withSourceofCapture:_typeOfCapture typeOfDocumentCaptured:PHOTO];
[self presentViewController:previewController animated:YES completion:nil];
Dismissed the preview view controller.
[self dismissViewControllerAnimated:YES completion:nil];
Application goes into background then it is not crashed.
I selected two rows one after another. Application goes into background then it is crashed. I don't know why it is behaving like that. If anyone know the solution please tell me.
Thanks In Advance
I had this problem, it was caused by someone overriding 'dealloc' in a UIViewController category.
https://github.com/taphuochai/PHAirViewController/issues/13
#chrishulbert
Remove this:
- (void)dealloc
{
self.phSwipeHander = nil;
}
Replace dealloc with this:
/// This is so that phSwipeGestureRecognizer doesn't create a swipe gesture in *every* vc's dealloc.
- (BOOL)phSwipeGestureRecognizerExists {
return objc_getAssociatedObject(self, SwipeObject) ? YES : NO;
}
- (void)ph_dealloc
{
if (self.phSwipeGestureRecognizerExists) {
self.phSwipeHander = nil;
}
[self ph_dealloc]; // This calls the original dealloc.
}
/// Swizzle the method into place.
void PH_MethodSwizzle(Class c, SEL origSEL, SEL overrideSEL) {
Method origMethod = class_getInstanceMethod(c, origSEL);
Method overrideMethod = class_getInstanceMethod(c, overrideSEL);
if (class_addMethod(c, origSEL, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod))) {
class_replaceMethod(c, overrideSEL, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
} else {
method_exchangeImplementations(origMethod, overrideMethod);
}
}
/// Swizzle dealloc at load time.
+ (void)load {
SEL deallocSelector = NSSelectorFromString(#"dealloc"); // Because ARC won't allow #selector(dealloc).
PH_MethodSwizzle(self, deallocSelector, #selector(ph_dealloc));
}

Execute function to call performSegueWithIdentifier from another class

I've spent a few hours on this trying to work it out myself but I give up!
I have a master-detail arrangement where the user input screen needs to call a function on another class to post to a web service. Upon completion of the asynchronous call, the class will then call a specified function. In this case, I'm just testing and all I want to do is go back to the main screen after the user input is accepted by the web service.
When the uses taps a button on the input screen (SetLocationViewController), the asynchronous operation is called in the class APIPostClass. After it is complete, I want SetLocationViewController to segue back to MasterViewController.
In APIPostClass.m in (called after the asynchronous op finishes)
-(void)callWhenDone {
NSLog(#"callWhenDone loaded.");
SetLocationViewController *SLVClassInstance = [[SetLocationViewController alloc] init];
[SLVClassInstance doSegue];
}
In SetLocationViewController.m
-(void) doSegue {
NSLog(#"doSegue loaded");
[self performSegueWithIdentifier:#"SetLocationViewControllerManualUnwind" sender:self];
}
Calling doSegue from an action on SetLocationViewController.m does work so I know my segue is ok but the above doesn't work. I get the error reason: 'Receiver () has no segue with identifier 'SetLocationViewControllerManualUnwind''
I'm guessing the reason is because of the alloc init way of initialising of the VC, but I don't know any better. Thus, how can I call a function on another class as if it was being called by it's own class?
Create a delegate it would be much more reliable and fast than Notifications.
#protocol APIPostDelegate <NSObject>
#required
-(void)OnRequestSucess;
#end
In your APIPost add new property for delegate
#interface APIPost : NSObject
#property (weak) id<APIPostDelegate> delegate;
In SetLocationViewController implement APIPostDelegate
SetLocationViewController.h
SetLocationViewController :NSObject<APIPostDelegate>
SetLocationViewController.m
-(void)OnRequestSucess
{
[self doSegue];
}
before you make call to method on APIPost, assign self to delegate property.
APIPost *apipost=[[APIPost alloc]init];
apipost.delegate=self;
[apipost <your api method>];
APIPost.m
[self.delegate OnRequestSucess];
Hope this helps.
There are a few methods to make it happens:-
Use Delegate
Use NSNotification.
The way described by Artur above (For SplitViewController Only - iPad)
You should use delegate whenever it is possible but it might not be too straight forward. NSNotification is more straight forward but it is not a good practice and not a good programming style.
I will only share the NSNotification method as it is easier to implement.
In SetLocationViewController.m
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doSegue) name:#"calldoSegue" object:nil];
}
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter]removeObserver:self name:#"calldoSegue" object:nil];
}
-(void) doSegue {
NSLog(#"doSegue loaded");
[self performSegueWithIdentifier:#"SetLocationViewControllerManualUnwind" sender:self];
}
In APIPostClass.m
-(void)callWhenDone {
NSLog(#"callWhenDone loaded.");
[[NSNotificationCenter defaultCenter]postNotificationName:#"calldoSegue" object:nil];
}
The above code should work but again, this is not a good practice. You should try to learn the Delegate method.
The answer is here: Performing segue from another class
In my APIPostClass.h, I setup the view controller:
#interface APIPostClass : NSObject {
SetLocationViewController *setLocationViewController;
}
#property(nonatomic, strong) SetLocationViewController *setLocationViewController;
#end
In my APIPostClass.m, I synthesize it:
#synthesize setLocationViewController;
then, instead of this (as in my question):
-(void)callWhenDone {
NSLog(#"callWhenDone loaded.");
SetLocationViewController *SLVClassInstance = [[SetLocationViewController alloc] init];
[SLVClassInstance doSegue];
}
I have:
-(void)callWhenDone {
NSLog(#"callWhenDone loaded");
[self.setLocationViewController doSegue];
}
Over in SetLocationViewController.m, the segue method remains unchanged:
-(void) doSegue {
NSLog(#"doSegue loaded");
[self performSegueWithIdentifier:#"SetLocationViewControllerManualUnwind" sender:self];
}
But when I call my API, I need to "attach" (forgive my terminology) the view controller to it. This is what I had:
- (IBAction)btnTestAPICall:(id)sender {
NSLog(#"User tapped API button");
APIPostClass *APIPostClassInstance = [[APIPostClass alloc] init];
[APIPostClassInstance APICall: ... ....
}
But this is what works after bringing all of the above:
- (IBAction)btnTestAPICall:(id)sender {
NSLog(#"User tapped API button");
APIPostClass *APIPostClassInstance= [[APIPostClass alloc] init];
UIViewController *currentVC=self;
APIPostClassInstance.setLocationViewController = currentVC;
[APIPostClassInstance APICall: ... ...
I hope this will help someone else!

Xcode IOS - How to get the scene/view that is currently being viewed in appdelegate

Okay I am kind of new to IOS development, but I am writing an application where I am using a timer class to time out the user if they idle too long on any particular scene in my storyboard and it bumps the user back to the original scene/view. I have a single story board that is made up of several scenes/views(not sure what the correct word here is), and each scene has its own view controller.
I accomplish the timeout via the appdelegate class. See code below.
So I have the code working and it works great, but I am trying to make it so that it will ignore the timer if we are on the main scene.
I have googled this, read copious amounts of documentation, and have tried many things but so far I haven't been able to figure out how to get the currently viewed scene in the applicationDidTimeout method.
If I can get the name of the currently viewed scene/view, then I can choose to ignore the timer or not.
Does anyone know how to do this?
Thank you for your time.
#import "StoryboardAppDelegate.h"
#import "TIMERUIApplication.h"
#implementation StoryboardAppDelegate
#synthesize window = _window;
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// applicaiton has timed out
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidTimeout:) name:kApplicationDidTimeoutNotification object:nil];
return YES;
}
-(void)applicationDidTimeout:(NSNotification *) notif
{
NSLog (#"time exceeded!!");
UIViewController *controller = [[UIStoryboard storyboardWithName:#"Main" bundle:NULL] instantiateViewControllerWithIdentifier:#"StoryboardViewController"];
UINavigationController * navigation = [[UINavigationController alloc]initWithRootViewController:controller];
[self.window setRootViewController:navigation];
navigation.delegate = self;
navigation.navigationBarHidden = YES;
if (controller) {
#try {
[navigation pushViewController:controller animated:NO];
} #catch (NSException * ex) {
//“Pushing the same view controller instance more than once is not supported”
//NSInvalidArgumentException
NSLog(#"Exception: [%#]:%#",[ex class], ex );
NSLog(#"ex.name:'%#'", ex.name);
NSLog(#"ex.reason:'%#'", ex.reason);
//Full error includes class pointer address so only care if it starts with this error
NSRange range = [ex.reason rangeOfString:#"Pushing the same view controller instance more than once is not supported"];
if ([ex.name isEqualToString:#"NSInvalidArgumentException"] &&
range.location != NSNotFound) {
//view controller already exists in the stack - just pop back to it
[navigation popToViewController:controller animated:NO];
} else {
NSLog(#"ERROR:UNHANDLED EXCEPTION TYPE:%#", ex);
}
} #finally {
//NSLog(#"finally");
}
} else {
NSLog(#"ERROR:pushViewController: viewController is nil");
}
[(TIMERUIApplication *)[UIApplication sharedApplication] resetIdleTimer];
}
#end
I'm assuming you've written the logic for the timer somewhere. Can you just invalidate the timer when you've popped back to the rootViewController?
Also instead of pushing a viewController onto the navigationViewController and handling the errors, you should check to see if the controller you're pushing is already in the stack like so:
if (![navigation.viewControllers containsObject:viewController] {
// push onto the stack
}
You could also check to see how many levels are currently in the navigationController by checking the count of the viewControllers array like so:
if ([navigation.viewControllers count] == 0) {
// I know we're on the main screen because no additional viewControllers have been added to the stack.
}
If you are not using modal controllers anywhere then the simplest solution would be
UINavigationController* nav = (UINavigationController*)self.window.rootViewController; // You could just save the nav as part of your app delegate
if (nav.viewControllers.count > 1){
[nav popToRootViewControllerAnimated:YES];
}
This is different then your current code because your main page will not be deleted and recreated every time the timer goes off
Okay I figured out how to do this. I was making this way too complicated.
To solve this I simply made a property and method in the app delegate class where I could set a scene name.
Then in each view controller header file I import the header file for the app delegate class and define a reference to it. Then in the load event for each view I simply set the scene name in the app delegate class using this line of code.
[myAppDelegate setSceneName:self.title];
Easy peasy!

Call viewdidload from appDelegate

I want to call the viewDidLoad in my view controller from my appDelegate. How do I do so?
- (void)applicationDidBecomeActive:(UIApplication *)application
{
TCAViewController *uiTCA = [[TCAViewController alloc] init];
if(uiTCA.failed == 1){
//Here I want to call viewDidLoad
// I thought something like this would work but I get an error
// [self uiTCA.viewDidLoad];
}
}
You don't call viewDidLoad, the system calls it when all the outlets have been set on the view controller.
Create a method - (void)doSomething; in your TCAViewController class and call that in your AppDelegate.
[uiTVA doSomething];

Resources