Calling popViewControllerAnimated from an UIView subclass - ios

I have created a button in a UIView subclass . I need to call popViewControllerAnimated via this button , but nothing work ! and I cannot see the viewController push back to rootViewController . here is my code :
- (void)SomeFunction {
backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[backButton showsTouchWhenHighlighted];
[backButton addTarget:self
action:#selector(backToMainMenu)
forControlEvents:UIControlEventTouchUpInside];
}
- (void)backToMainMenu {
[self.window.rootViewController.navigationController popViewControllerAnimated:YES];
NSLog(#"back");
}
I change the code to this :
UINavigationController *vc = self.window.rootViewController.navigationController;
[vc.navigationController popViewControllerAnimated:YES];
but nothing happens .

I think you need to use the proper target format which takes the button as an argument. So add target function like this:
[backButton addTarget:self
action:#selector(backToMainMenu:)
forControlEvents:UIControlEventTouchUpInside];
and the target should look like this:
- (void) backToMainMenu:(UIButton *) sender{
[self.navigationController popViewControllerAnimated:YES];
}

A better option is to use Delegate pattern because in your current logic you are breaking the MVC architecture and guidelines.
Create a Protocol in your subview class. The receiver of this delegate would be the view controller class from which you are showing your view. In event handling of the button, call the delegate method and from the view controller you would be able to call popViewControllerAnimated successfully.

I believe your fundamental problem (besides design) is in (void)backToMainMenu ... self.window.rootViewController.navigationController will be nil, so this method does nothing
from UIViewController class reference:
If the receiver or one of its ancestors is a child of a navigation controller, this property contains the owning navigation controller. This property is nil if the view controller is not embedded inside a navigation controller.
so you see, the rootViewController cannot be embedded inside a nav controller can it, it is the bottommost one..
why don't you test this:
{
UINavigationController *vc = self.window.rootViewController.navigationController;
if (vc==nil){
NSLog(#"nav controller is nil, this will never work");
}
[vc.navigationController popViewControllerAnimated:YES];
}
I also fully agree with #rory's answer there about design..
PS did you actually create a UINavigationController in order to push this viewController?

Related

viewDidDisappear called because of termination? [duplicate]

When a view loads, i want to see if it's because the user pressed the back button. How can i check this?
The best solution I've found to detect a UINavigationController's back button press (pre-iOS 5.0) is by verifying that the current view controller is not present in the in the navigation controller's view controller stack.
It is possibly safer to check this condition in - (void)viewDidDisappear:(BOOL)animated as logically, by the time that method is called it would be extremely likely that the view controller was removed from the stack.
Pre-iOS 5.0:
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if (![[self.navigationController viewControllers] containsObject:self]) {
// We were removed from the navigation controller's view controller stack
// thus, we can infer that the back button was pressed
}
}
iOS 5.0+ you can use -didMoveToParentViewController:
- (void)didMoveToParentViewController:(UIViewController *)parent
{
// parent is nil if this view controller was removed
}
in your viewWillDisappear method check
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if ([self isMovingFromParentViewController]) {
//specific stuff for being popped off stack
}
}
This is only for post iOS 5
UINavigationController has a delegate property that issues delegate callbacks. Please see the iOS reference here.
The delegate doesn't have a "back button pressed" callback, but instead it tells you when something is going to appear on the navigation stack. When you press back, you are "popping" the top view controller off the stack, so it will tell you that the view is about to appear. I think this is the callback you'd be looking for.
You could have some simple logic to check if it's the view controller that's "interested", and then you could send a notification, et al.
For the sake of completeness, mix of two most upvoted answers (1, 2) in Swift:
override func willMoveToParentViewController(parent: UIViewController?) {
super.willMoveToParentViewController(parent)
if parent == nil {
// view controller is popping
}
}
This is a slightly different scenario, but I thought the solution might help others out.
In my situation, I had a UINavigationController within a UIPopoverController. I needed to detect whether the user clicked the back button, or clicked outside of the popover. To do this I checked the visibleViewController property in viewWillDisappear. If the view controller is still the visibleViewController when closing, then the popover is being closed by another means. If the view controller is not the visibleViewController when closing, then the back button was pressed.
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (self.navigationController.visibleViewController != self) {
<Do something since we're closing using something else>
} else {
<Do something since we're closing because of the back button>
}
}
I tried using zach's solution, but isMovingFromParentViewController returns true for both cases.
I verified this works in iOS 5+
I hope this helps.
Create a custom back bar button and set the target,
Step 1: Add these methods to your class
- (void)backButtonClicked :(id)sender{
[self.navigationController popViewControllerAnimated:YES];
}
- (void)addBackBarButton{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(0, 0, 55, 35);
[button setTitle:#"back" forState:UIControlStateNormal];
[button addTarget:self action:#selector(backButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *customBarItem = [[UIBarButtonItem alloc] initWithCustomView:button];
self.navigationItem.leftBarButtonItem = customBarItem;
}
Step 2: Call [self addBackBarButton]; in viewDiDLoad method
You will get the action in backButtonClicked method. You can play around with it the way you want.
Cheers!
The only way to do this so you know for sure that it was the back button is to create a custom button. If you don't know how to do that, check out this tutorial. It won't look exactly like the normal back button, but close. If you need more help, post a comment

Triggering a Method from another ViewController using a button in a UIView

The issue I am having is that I want to call a method in a navigationController by clicking a button inside a UIView (footer). When I press the button, it should call the method I'm trying to access to open the Video recorder in the code below.
I was told I could implement a delegate method or use a NSNotification. Below is what I have:
My footer (ESPhotoDetailsFooterView.m) has my button that I created. My footer only contains a UIView.
The method I'm trying to access in my footer resides in (ESTabBarController.m)
This is what I am trying to trigger when pressing my button:
RecorderViewController *viewController = [[RecorderViewController alloc] init];
[viewController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self.navController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self.navController pushViewController:viewController animated:NO];
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:self.navController animated:YES completion:nil];
});
I am new to Objective C and understand the basics. I cannot figure out what I need to do to accomplish this. Any help would be much appreciated.
The code for the button is as follows:
// Create a standard UIButton programmatically using convenience method
UIButton *camButton = [UIButton buttonWithType:UIButtonTypeCustom];
// Set the location (x,y) and size (width,height) of the button
camButton.frame = CGRectMake(9.0f, 8.0f, 35.0f, 35.0f);
// Create UIImages from image resources in your application bundle
// using convenience methods (no need to release)
UIImage *normal = [UIImage imageNamed:#"BingComm"];
UIImage *highlighted = [UIImage imageNamed:#"BingCommClick"];
// Set the button's background to an image
[camButton setBackgroundImage:normal forState:UIControlStateNormal];
[camButton setBackgroundImage:highlighted forState:UIControlStateHighlighted];
// Add the target-action for the touch event
#pragma GCC diagnostic ignored "-Wundeclared-selector"
[camButton addTarget:self action:#selector(btnClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.mainView addSubview:camButton];
Yes, in this case delegation is a good choice, basically you will need a delegate object in your footer view. Then you set the delegate to TabBarController at runtime.
When user clicks the button, call you delegate with your method [delegate method]
This is a very important design pattern objective-c, it would be very helpful if you can follow this tutorial to fully understand delegation.
http://www.alexefish.com/post/522641eb31fa2a0015000002
I agree that delegation is important and very much worth learning, but another way to solve the same problem would be to the following:
In your method definition for btnClicked, call the following code
if([self tabBarController]) {
[[self tabBarController] methodToCall];
}
Since your view controller should be embedded within a tab bar controller, it will have this property set. You can then call any public methods contained within the tabBarController class.

UINavigationController back button return to FIrstTableViewController

So I was create three UITableViewControllers with UINavigationController. I want a back button on 3rd UITableViewController, what returns my view to first UITableViewController instead of second.
How can I do that? That must be a real backButton, not a image or something else. Will be perfect to do this only with storyboard.
UPDATE
Perhaps I poorly explained what I want.
I don't want use any button with action on it. I just want something like as setting "address" of 1st TableViewController on my default back button. There is any way to do it?
add a button and connect it to following action
- (IBAction)backToFirstView:(UIButton *)sender
{
[self.navigationController popToRootViewControllerAnimated:YES];
(or)
[self.navigationController popToViewController:yourFirstViewControllerObject animated:YES];
}
There are different ways to navigate from DetailViewController to other view controllers.
We will go through the cases one by one.
First of all I would like to clear that if its your default
navigation bar's back button, then it must return to the last most
view controller only which is actually a default behavior of a
navigation controller.
Second, If you would like to go back to the
last most view controller on the tap of a button placed by you, you
should write the following code
[self.navigationController popToViewController:NAME_OF_A_VIEWCONTROLLER animated:YES];
Third, If you would like to go to the first view controller from where you
started, you should write the following code
[self.navigationController popToRootViewControllerAnimated:YES];
Ok, I found a way to resolve my problem. Thanks for your answers guys, they was very helpful.
So for resolve this problem you just need use link what give me Kumar KL upper, and wrote next method in your UITableVIewController
-(void) viewWillDisappear:(BOOL)animated {
if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
// Navigation button was pressed. Do some stuff
[self.navigationController popViewControllerAnimated:NO];
}
[super viewWillDisappear:animated];
}
Now you got a backButton what redirect you to your viewController, BUT title of this button is wrong. Let's resolve that unfair.
Create new class CustomSegueцрфе inherited from UIStoryboardSegue with next code in CustomSegue.m :
- (void)perform
{
UIViewController *sourceView = (UIViewController *) self.sourceViewController;
UIViewController *destinationView = (UIViewController *) self.destinationViewController;
[[destinationView navigationItem] setTitle:#"TitleOfYourViewController" ] ;
[sourceView.navigationItem setTitle:#"TitleOfButton"] ;
[sourceView.navigationController pushViewController:destinationView animated:YES];
}
Now you can go to storyboard and connect 2nd ViewController with 3rd with custom segue.
Like you see UINavigationController uses Title of previous ViewController for button title, so you just need change it.

Ios How to a add a button to my TabBarController then trigger the presentation of a modal view from it

I have a UITabBarController bassed app. I'm instantiating it from the app delegate and adding a custom button in the tab bar controller. when that button is clicked, I want to present another view modally, but I cant seem to figure out how to do it. to add the button I'm basically doing this
UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];
[self.tabBarController.view addSubview:button];
[button addTarget:self action:#selector(showModalViewController:) forControlEvents:UIControlEventTouchUpInside];
and also in the app delegate I have a method
- (void) showModalViewController {
DummyViewController *addController = [[DummyViewController alloc]
initWithNibName:#"DummyViewController" bundle:nil];
//addController.delegate = self;
self.tabBarController.selectedViewController = [self.tabBarController.viewControllers objectAtIndex:0];
// Create the navigation controller and present it modally.
[self.tabBarController.selectedViewController presentModalViewController:addController animated:YES];
// The navigation controller is now owned by the current view controller
}
I keep getting unrecognized seletor
Your selector looks for a method named showModalViewController: but your actual method is named showModalViewController. Change one or the other.
Either change the selector to #selector(showModalViewController) to match the existing method, or change the method to:
- (void)showModalViewController:(UIButton *)button {
Don't change both.

ios - addtarget to a button in another view controller

Why doesn't this work? I want the closeBtn in the new view controller to call a method called dismiss: in the current view controller.
NewViewController *newVC = [[NewViewController alloc] initWithNibName:#"NewViewController" bundle:[NSBundle mainBundle]];
[newVC.closeBtn addTarget:self action:#selector(dismiss:) forControlEvents:UIControlEventTouchUpInside];
The dismiss: method is never called in the current view controller. closeBtn is correctly set up as a property in NewViewController and linked in the .xib file.
Create the object of the another controller and specify it in addTarget. Give the name of the method in the action parameter.
i.e anotherController *obj;
[button addTarget:obj action:#selector(MethodName) forControlEvents:UIControlEventTouchDown];
Generally, that is not a good way to deal with methods and UI elements.
However, you can do something like this, though it is ugly.
[yourButton addTarget:self action:#selector(yourButtonPressed:) forControlEvent:UITouchUpInside];
- (void) yourButtonPressed:(id)sender {
OtherVC* otherVC = [OtherVC alloc] initWithNibName#"NewViewController"] ...
[otherVC theDesiredMethod];
[otherVC release];
}
This works but is not really good you are probably better off moving the function into the proper VC.
Hope that helps.

Resources