Know What UINavigationController the User is Inside UITabBarController - objective-c

I have a UITabBarController that has 3 UINavigationControllers in my AppDelegate and I want to know how to determine the active UINavigationController where the user is. How can I determine this?

UITabBarController has a property called selectedViewController. Just check this for the appropriate value (e.g. by checking its class or a tag of its main view).

I figured it out on my own.
I just used this code to make my code work. It may seem a bit far from my original question but this made it to work as I just simply want to display my custom notification view on my current view.
if (self.tabBarController.selectedIndex == 0)
{
[self displayNotificationWhenOnline:self.navController1.visibleViewController.view];
}
else if (self.tabBarController.selectedIndex == 1)
{
[self displayNotificationWhenOnline:self.navController2.visibleViewController.view];
}

You can get perticuler view controller using this code,
[(UINavigationController*)[[(AppDelegate*)[[UIApplication sharedApplication]delegate] tabBarController] selectedViewController]visibleViewController]

Related

Getting this warning "while a presentation is in progress" (Xcode)

In a project I'm writing I get this error when I present a new view controller:
Attempt to present.... while a presentation is in progress!
I think it happens because I first present a new view controller, and then in that view I present another view controller.
- (void)loadLabelSettings {
LabelSettingsViewController *labelSettings =
[[LabelSettingsViewController alloc] init];
labelSettings.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:labelSettings animated:YES completion:nil];
}
The program doesn't crash or anything it runs just fine, and there is no errors or warnings in my code. So my question is: Is it something I should be concerned with and if yes how do I solve it?
Thanks in advance :)
It is, like you said, probably caused by presenting two view controllers at the same time. Wait with presenting the second view controller until the first one has been fully presented. A good location would be to do this in viewDidAppear.
In my case, I connected a UIViewControllers UIButton with a second UIViewController by a UIStoryboardSegue. Inside my code a called it a second time programmatically. So pressing the UIButton caused presenting the specified view two times.
I figured out my problem, as Scott wrote it was because I was presenting 2 view controllers at the same time. It happened because I had a button that had a UILongPressGestureRecognizer, that showed the new view controller. The problem was that when using a UILongPressGestureRecognizer, the method that is being called, is called twice. First when the long press is detected and when your finger is released from the screen. So the presentViewController method of the same view, was called twice. I fixed this by only reacting to the first detection. Here is the code :
- (void)loadButtonSettings:(UILongPressGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateBegan) {
}
}

How do I properly use NSUserDefaults Class to bypass a View?

I have a project in xcode that uses storyboards. The first view that loads is an "accept terms and conditions" view in which the user must click an accept button to proceed. After clicking it, it segues to the next view. After the user clicks accept the first time the program launches, I never want them to see that view again - I want it to go straight to the next view. I have some code but its not working. Here is what I have exactly:
In app delegate: (inside applicationDidFinishLaunchingWithOptions)
if([[NSUserDefaults standardUserDefaults] boolForKey:#"TermsAccepted"]!=YES)
{
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:#"TermsAccepted"];
}
Inside the accept terms and conditions view implementation: (viewDidLoad)
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"TermsAccepted"]){
[self.navigationController pushViewController: self animated:YES];
//I want it to go to the next screen
}
else {
//I want to show this screen, but I don't know what goes here
}
Also Inside the accept terms and conditions view implementation (in the accept button)
- (IBAction)acceptButton:(id)sender {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"TermsAccepted"];
}
I run it and get the error: 'Pushing the same view controller instance more than once is not supported'. How do I fix this?
In your first code snippet, you basically say "if TermsAccepted is not YES (so it is NO), then set it to NO. This does not make sense
In your 2nd code snippet, you wrote [self.navigationController pushViewController:self animated:YES];. So basically you ask the current UIViewController (self) to push itself on its own navigationController… which does not make sense either.
That's why you have this error. You try to push the current viewController self whereas it is already on screen in your navigationController. So you try to push the same instance (self) twice on the same navigationController.
You obviously meant to push another viewController (probably an instance of a TermsAndConditionViewController or something that shows the terms and conditions of your app) on the navigation controller, and not the current viewController itself, which doesn't make sense.
First, you want to have the next view controller, the one you always want to show, be the root view controller of your window. In that controller's viewDidLoad method, put your if clause to show the accept terms and conditions controller -- you can show that one using presentModalViewController. The if clase can be like this:
If([[NSUserDefaults standardUserDefaults] BoolForKey:#"TermsAccepted"] !=YES) {
// instantiate your terms and conditions controller here
// present the controller
}
Then, in the method where you dismiss the terms and conditions controller, set the value of that key to YES.

How do I check if an UIViewController is currently being displayed?

How do I check if an UIViewController is currently being displayed?
My UIViewControllers are listening for NSNotifications - even if when they are not displayed (ie not shown). So I could have 10 UIViewController in the background observing NSNotifications from NSNotificationCenter. When an NSNotification is posted and received by the UIViewController, I'd like to find out if it is currently being shown. If it is not, I will just set a boolean so that it will processed when the View is presented. If it currently being display, I will do more things like update tables immediately, and so forth...
You need to check if your viewcontroller is on top of the stack of navigationcontroller's viewcontroller array. A sample code is,
if (self.navigationController.topViewController == self) {
//the view is currently displayed
}
You can use this inside the viewWillAppear method to check whether the current view is visible.
Check to see if it's attached to the window. If it's not nil it's in hierarchy which is attached to the screen (of course it could be off the bounds of the screen, covered by some other view or have the hidden flag set)
if (myViewController.view.window) {
// I'm attached to the window
} else {
// not attached to the window
}
You can use flags in viewWillAppear and viewWillDisappear methods for this.
Why don't you remove the notification listener in viewWillDisappear and add it in viewWillAppear?
Edit: misread his question, sorry.
Suggested answer: set your own flag (BOOL) in viewDidDisappear and viewDidAppear.
Specify title to each ViewController and then get the title of current ViewController by the code given bellow.
NSString *currentController = self.navigationController.visibleViewController.title;
Then check it by your title like this
if([currentController isEqualToString:#"myViewControllerTitle"]){
//write your code according to View controller.
}
I think that checking of viewController.view.superview should works.
It's too late to replay on this question.
To check the instance of a UIViewController is currently on the top of the screen or to check if it is showing on screen, you can put a check like:
// Get the topmost view showing on the screen as below
UIViewController * currentVC = ((UINavigationController*)app.window.rootViewController).visibleViewController;
// Now check whether the viewcontroller you want to show is the same as the currently showing view controller.
if (currentVC.class == myVC.class) { // Here myVC is any/new instance of the viewcontroller you would like to check or show (if not shown).
// If both are same then it returns true and executes this block of code.
}
One more alternative which is based on checking window property
if viewController.viewIfLoaded?.window != nil {
// visible
}

How do I know if a view is visible or not?

Say I have two view controllers: xVC and yVC. I have used the shake API and and have used the methods -(void)motionBegan,-(void)motionEnded: and -(void)motionCancelled in xVC. What happens is when the device is shaken, it fires a simple animation. Now the thing is that this animation is fired even when the I have yVC open that is, when yVS.view has been added as the subview. What I am looking for is some if condition which I can use in -(void)motionEnded: like this:
if(yVC == nil)
{
//trigger animation
}
By that I mean that the shake shouldn't work when yVC is visible. How do I do that? Please help.
The general advice I have seen and used is to ask a view if it has a non-nil window property:
if( ! yVC.view.window) {
// trigger animation
}
But note that this doesn't always equate with being visible; though in most apps it's about as good as you can performantly get (the basic case where it's not accurate is when a different view completely obscures it, but this may still satisfy your needs)
Add this to both of your view controllers:
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
visible = YES;
}
-(void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
visible = NO;
}
Now, just check the variable isVisible of both the view controllers and trigger your animation likewise.
The previous answers all work to some degree, but fail to take modally presented view controllers into account. If view controller A presents view controller B, of the previous answers will tell you that A is still visible. If you, like me, want to know whether or not the view is actually visible (and not just a part of the view hierarchy), I would suggest also checking the presentedViewController property:
if (self.isViewLoaded && [self.view window] && !self.presentedViewController) {
// User is looking at this view and nothing else
}
This works since presentedViewController will be non-nil whenever the current view controller OR any of its ancestors are currently presenting another view controller.

Get current view

I have an app which has split view inside a tab bar, and these split views often have navigation hierarchy and then sometimes modal views are presents on top of them, and it all works fine, but...
I am trying to display a passcode lock whenever the app goes into background, so I put
[self.window.rootViewController presentModalViewController:lockView animated:YES];
in my AppDelegate's method
- (void)applicationWillResignActive:(UIApplication *)application
...which works fine unless a modal view is displayed.
the passcode does not display if a modal view is open.
Is there a way to retrieve the currently active view controller so I can present this lock view?
Thanks in advance
Cheerio
Code that worked was as follows:
BOOL hasKids = YES;
UIViewController *topViewController = (UIViewController*)[[(UITabBarController*)self.window.rootViewController viewControllers] objectAtIndex:((UITabBarController*)self.window.rootViewController).selectedIndex];
while (hasKids) {
if (topViewController.presentedViewController) {
hasKids = YES;
topViewController = topViewController.presentedViewController;
} else {
hasKids = NO;
}
}
[topViewController presentModalViewController:lockView animated:YES];`
I think the easiest way is to keep track of which tab is currently active (there are a number of ways to do this, but I'd recommend implementing the UITabBarControllerDelegate and handling its tabBarController:didSelectViewController: method).
Once that's done, you'll probably need to manage a property in each view controller that holds any modal view controllers you present. If, however, you're on iOS 5 or higher, look into the UIViewController property presentedViewController. It appears that this is a new way to do exactly what you want.