I've got a pretty simple question. In my app, there are some screens that have a navigation bar, and some that don't have it. So, what I did was to flag this manually between screen in prepareForSegue: using this line self.navigationController?.setNavigationBarHidden(true/false, animated: false) . Now, how do I do it if I want to go back from a view controller with a navigation bar to a view controller that doesn't have it by clicking on the back button? I tried putting it in the prepareForSegue: of the child view controller but it doesn't work.
Thanks.
It depends how you are maintaining your flags for hiding/unhiding the navigation Bar but you can use UINavigationControllerDelegate for the same
#available(iOS 2.0, *)
optional public func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool)
#available(iOS 2.0, *)
optional public func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool)
Alternatively, you can try to put setNavigationBarHidden in viewWillAppear and viewWillDisappear for each viewController.
you can also use a better approach with unwind segue
override func didMoveToParentViewController(parent: UIViewController?) {
if (parent == nil) {
println("Back Button Pressed!")
}
}
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'm trying to implement a custom transition between two viewControllers. (Swift 3.0)
Between my two viewControllers I have a UISegue with the kind show (animated = true).
So I set the delegate methods of UIViewControllerTransitioningDelegate in the extension of my first view controller :
extension DocumentsViewController : UIViewControllerTransitioningDelegate { ... }
And I also have implemented the required methods by the protocol :
animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
...
}
public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
...
}
Now when the segue is perform, in the firstViewController I'm using the delegate method prepareForSegue to finally set the transitioningDelegate to my `secondViewController, see below :
public override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if let destination = segue.destination as? DocumentDetailViewController {
destination.transitioningDelegate = self
}
}
I check with breakpoints, the delegate is well setted to my firstViewController.
But the delegate methods of transitioningDelegate in my firstViewController are never fired, I don't know why.
Any ideas ?
PS : In my storyboard, my segue have Animated to true, so this should work, but it doesn't.
Thanks.
SOLVED : A mix of MadreDeDios, Matt and Tushar answers.
1 : As I want to keep the navigation in my app, I have to make conform my first viewController to UINavigationControllerDelegate instead of UIViewControllerTransitioningDelegate. (see MadreDeDios answer's).
2 : According to this protocol, I have implemented the following delegate method :
public func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
...
}
3 : I set the delegate earlier at the viewDidLoad() of my firstViewController (see Matt's answer) :
override public func viewDidLoad() {
super.viewDidLoad()
//...
self.navigationController?.delegate = self
}
4 : I'm using a manual push instead of a segue to display my secondViewController (see Tushar's answer).
Now this works, thank you.
The problem is that you are setting the transitioningDelegate too late. Way too late. It needs to be set very early in the lifetime of the view controller. I advise setting this property in the view controller's initializer.
Because you are using a push segue, I assume you are using a navigation controller as well.
When you are using an UINavigationController, it becomes the reference for every transition, animation, and even your app's orientation.
My advice would be to use your navigation controller as the manager for all your animations. All you need to do is to add these few things:
extension MyNavigationController: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
//Check for the good animation
return MyAnimation()
}
}
And inside your MyNavigationController class
override func viewDidLoad() {
super.viewDidLoad()
//This is the key
self.delegate = self
//Only if you want to animate the presentation of your navigation controller itself, the first time it appears:
self.transitioningDelegate = self
}
Try removing the transition of storyboard and execute the view presentation code manually and explicitly mention 'true' for the animated parameter :
presentViewController(documentVC, animated: true, completion: nil)
I have implemented UINavigationControllerDelegate method:
public func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { }
because I had to set application state and only "event" that was able to capture Pop transition of my UINavigationController was this method. Everything is working properly but as far as that functionality is concerned, but now my swipe back action does not work (only pressing back button works).
At the end of this method I have returned nil because I had no idea what its doing but apparently it affects that swipe to back functionality.
Is there any default UIViewControllerAnimatedTransitioning that I can returned to have that swipe back functionality?
Thanks.
I switched to this delegate method:
public func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
if self.viewControllerCount - 1 == self.rootController.viewControllers.count {
// Pop action, do additional logic
}
}
and customized it so that I can know for sure if its pop action (tracking last number of view controllers.
I have an UINavigationController and it has an UITableViewController as its root controller. I reload the data for the table view in viewWillAppear. When I select an item in the table view it will push an new UIViewController with which the user could edit the data that will be shown in the table view, and then I clicked Back of the navigation, viewWillAppear of the UITableViewController was just not called and the data was not reloaded.
So in this case where could I reload the data for the table view?
Thanks.
The easiest way to do that is to use UINavigationControllerDelegate. There are two methods available:
func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated animated: Bool)
func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated animated: Bool)
In your UITableViewController(root) you should add UINavigationControllerDelegate and method navigationControllerWillShowViewController will be called when you are going back. Here's an example:
class RootTableViewController: UITableViewController, UINavigationControllerDelegate {
...
func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated animated: Bool) {
//Will run
}
...
}
viewWillAppear is called when you hit the back button. Just put a log in there if you don't believe me! Most likely the table is not reloading the way you want it to because there is something wrong with the table datasource.