I have two ViewControllers, first and second. They are connected with show (push) segue. I click button on firstViewController to go to secondViewController. Then using automatically added navigation controller <First , I go back to firstViewController. However, here I would like to get alert message when navigation controller to firstViewContoller is pressed. How do I do it?
What you are looking for is UINavigationControllerDelegate.
I believe the method that gives you the message you need is
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;
And
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated;
In your CustomViewController, you are going to want to conform to the UINavigationControllerDelegate protocol like this:
#interface CustomViewController : UIViewController <UINavigationControllerDelegate>
And then override the delegate methods above to get the messages you are looking for.
Here is an complete implementation in Swift:
import UIKit
class ViewController: UIViewController, UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
println(viewController)
}
}
class FirstViewController: ViewController {
}
class SecondViewController: ViewController {
}
You can work on the viewWillDisappear method on your second view controller like this:
- (void)viewWillDisappear:(BOOL)animated
{
if(self.isMovingFromParentViewController){
NSLog(#"Controller being popped");
}
}
In this case, self.isMovingFromParentViewController will be true if the controller is being popped.
You can also check for self.isMovingToParentViewController on viewWillAppear for example, to check that the controller is being pushed.
Also self.isBeingDismissed and self.isBeingPresented are available and refer to when a controller is being presented/dismissed (modally).
Related
I am implementing the navigationControllerDelegate func:
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
print("callin the Navigation Controller Delgate")
if viewController === self {
print("Calling the Navigation Controller delegate because is self and going to call tapButton")
//want to know who was previously on top of navigation.
}
}
I want to know here which was the viewController that is being removed form the stack since apple docs says that the
viewController
The view controller whose view and navigation item properties are being shown.
This means that this assumption is true:
viewController == navigationController.topViewController
or this one:
viewController == navigationController.visibleViewController
If not then one of this are the the viewControllers that is going to be removed.
It's not cleat for me since the func parameter name is willShow viewController, or is just a fancy name and the will show is the already shown.
So if not how from the delegate I may know which VC is being removed from the Navigation Stack.
It would have been nice if the delegate provided the viewController being removed, but I found it's just straight forward just to keep a reference.
weak var lastRemovedViewController: UIViewController? = nil
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
defer { lastRemovedViewController = viewController }
print("Navigation Removed \(lastRemovedViewController.debugDescription)")
}
yes is the answer, the viewController passed in the delegate func is the same as the topViewController, also is equal to navigationController.visibleViewController. so there is no way to know which was the removed viewController from the stack.
This Apple Doc looks promising, https://developer.apple.com/reference/uikit/uinavigationcontrollerdelegate?language=objc but they are not getting called.
I have a CustomNavigationController that inherits from UINavigationController. I need to know when a VC has been pushed or popped from my CustomNavC.
I can conform to UINavigationControllerDelegate, but this seems to be more for ViewControllers. Not for the CustomNavC to do a self.delegate = self.
How can this be achieved?
UPDATE: False alarm. Sorry all. I was testing on device and had OS_ACTIVITY_MODE disabled. The UINavigationControllerDelegate functions work just fine.
One option is to override the pushViewController and popViewController methods.
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
[super pushViewController:viewController animated:animated];
// Handle push as needed
}
There are several pop... methods you can override as well.
You may also wish to override the setViewControllers:animated: method which is called when the whole stack is replaced.
Overriding these methods leaves the delegate free to be used by other classes.
Maybe you can implement UINavigationControllerDelegate in your CustomNavigationController and add these functions:
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
}
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
}
I've got a custom UIStoryboardSegue that 'zooms' a UIImageView. Now I also need a custom UIStoryboardSegue that 'zooms out' when a user presses the back button in the UINavigationController. I've been trying to do this for some days now, but without success.
I've subclassed UINavigationController and added the code below to it:
- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier {
UIStoryboardSegue *theSegue;
NSLog(#"Unwind called");
if ([fromViewController isKindOfClass:[SetDetailViewController class]]) {
theSegue = [ZoomOutSegue segueWithIdentifier:identifier source:fromViewController destination:toViewController performHandler:^(void){}];
} else {
theSegue = [super segueForUnwindingToViewController:toViewController fromViewController:fromViewController identifier:identifier];
}
return theSegue;
}
However, this isn't called. What am I doing wrong here?
I'm assuming you are still switching views with your zooming
I'm a Swiftie so my help will be in Swift
You should be able to capture these events by making your class a delegate of your navigation controller and then using the willShowViewController method of your navigation controller
Add UINavigationControllerDelegate to your class when you define it
Under your viewDidLoad() function add self.navigationController?.delegate = self
Now you should be able to use a function that will run just before the navigation controller switches to a new view controller (so yes it will also run when the back button is pressed). In Swift it looks like this:
func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
//if the view controller we are going to is one we want to trigger this action
if let _ = viewController as? ViewController {
//perform the thing you want here
}}
Currently I have a Tab Bar Controller that is connected to a tableview controller. I'm trying to go to the top of the tableview when I press the tab bar item. I know how to get to the top of the tableview. I just don't know how to do an action when the item is pressed.
You should use UITabBarDelegate with method didSelectItem. Use it as any standard delegate:
class yourclass: UIViewController, UITabBarDelegate {
func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
//This method will be called when user changes tab.
}
}
And do not forget to set your tab bar delegate to self in view controller.
Here is an answer to this question
Basically you do this:
Make sure your view controller is subscribed to the UITabBarDelegate
Set tags in IB for each tab bar item
Implement the didSelectItem method, something like this:
-(void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item {
if(item.tag == 1) {
// Code for item 1
}
else if(item.tag == 2) {
// Code for item 2
}
}
This will give you access to each tab item tapped event. Hope it helps!
In Swift:
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
if(item.tag == 1) {
// Code for item 1
} else if(item.tag == 2) {
// Code for item 2
}
}
SWIFT 3
class yourclass: UIViewController, UITabBarDelegate {
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
print("Test")
}
}
And do not forget to set your tabBar delegate to self in viewDidLoad
override func viewDidLoad(){
super.viewDidLoad()
<YOUR TAB BAR NAME>.delegate = self
}
I was having trouble implementing the other answers here. This is a fuller answer. It assumes you are using a UITabBarController (the default if you create a new Tabbed App). This solution will print a message every time a view controller tab button is tapped.
Code
Create a new Swift file called MyTabBarController.swift. Paste in the following code.
import UIKit
class MyTabBarController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// tell our UITabBarController subclass to handle its own delegate methods
self.delegate = self
}
// called whenever a tab button is tapped
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if viewController is FirstViewController {
print("First tab")
} else if viewController is SecondViewController {
print("Second tab")
}
}
}
Interface Builder
On your storyboard select the Tab Bar Controller. Then in the Identity inspector, set the class name to MyTabBarController (that is, the name of the class in the code above).
That's all. You can run your app now and be notified whenever the user taps a tab bar item.
Notes
If you need to run a method on a tap, then you can do something like the following in didSelect method.
if let firstVC = viewController as? FirstViewController {
firstVC.doSomeAction()
}
You could do make the FirstViewController implement the delegate and handle everything there. That way you wouldn't need to make any custom UITabBarController subclass and set it in IB. However, having a child do the parent's work seems like the wrong place to do it. Anyway, here is is:
class FirstViewController: UIViewController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
tabBarController?.delegate = self
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
// ...
}
}
The didSelect method above gets called no matter which tab is tapped.
UITabBarControllerDelegate documentation
An alternate solution is to just do something in viewDidAppear in whichever View Controller the tab shows.
First Tab View Controller
import UIKit
class FirstViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("First tab")
}
}
Second Tab View Controller
import UIKit
class SecondViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("Second tab")
}
}
class TestViewController: UIViewController,UITabBarDelegate {
#IBOutlet weak var tabbar: UITabBar!
override func viewDidLoad() {
super.viewDidLoad()
tabbar.delegate = self
}
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
print(tabBar.items![1]) // The number is tab index
}
}
Is it possible in iOS 6 to know when a UIStoryboardSegue has finished its transition? Like when i add a UIStoryboardSegue from UIButton to push another UIViewController on the navigationcontroler, i want to to something right after the push-transition is finished.
You can use the UINavigationControllerDelegate protocol and then define:
– navigationController:didShowViewController:animated:
In case you don't want to use the viewDidAppear: method, you could create a custom segue. In the perform method you would use an animation for the transition, and that can have a completion block. You can add the code there after the animation is complete.
In Swift, from a UIViewController subclass you can get the UINavigationController instance and set the delegate, in order to be informed about the completion of segues, as shown. Another logical place to track segues might be the AppDelegate.
Example of doing it from a view controller (VC for short):
class MyViewControllerSubclass : UIViewController, UINavigationControllerDelegate {
func viewDidLoad() {
self.navigationController.delegate = self
}
func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated: Bool) {
println("Did show VC: \(viewController)")
}
}
But that only shows you when the segue to the VC is complete,
as would viewWillAppear() or viewDidAppear() delegate methods in the VC being presented; however, they don't inform about when the target VC is un-presented. It will also only work if your View Controller is part of a Navigation Controller stack.
In the VC you're tracking, you could add the following to detect when the VC (and its memory) are deallocated, or override the viewWillDisappear() method.
deinit {
println(__FUNCTION__, "\(self)")
}
You can use - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
This method will be called right before a segue is performed in the source UIViewController. If you want to do some code in the destination UIViewController you can get the destination viewcontroller of segue.
You can also add this code in the viewdidAppear in the desintation viewController.
you can call a method of destination UIViewController in prepareForSegue method.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(#"prepareForSegue: %#", segue.identifier);
if ([segue.identifier isEqualToString:#"Happy"]) {
[segue.destinationViewController setHappiness:100];
} else if ([segue.identifier isEqualToString:#"Sad"]) {
[segue.destinationViewController setHappiness:0];
}
}
here setHappiness method is of destination Controller and here 100 is passing there. so you can write a method in destination controller and call it here