NavigationController.topViewController is equal to viewController in navigationController delegate - ios

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.

Related

How to do 'prepareSegue' for back button?

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!")
}
}

How to override popToRootViewController to append a method?

I subclassed UINavigationController and through its didShow delegate, which is called whenever a new view controller is pushed onto the stack, I update an instance variable called previousViewController (to be able to perform some custom work).
The instance variable:
class SectionNavigationController: UINavigationController {
var previousViewController: UIViewController?
...
The delegate where it is updated:
// nav controller delegate method (did show)
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
if currentIndex! > 0 {
previousViewController = navigationController.viewControllers[currentIndex! - 1]
}
}
As a consequence, however, whenever popToRootViewController is called, the view controller underneath the top view controller (previousViewController) is not deinitialized because of this reference. How can I override popToRootViewController in such a way that all it does is append the deinitialization of previousViewController?
check this apple documentation
You resolve strong reference cycles by defining some of the
relationships between classes as weak or unowned references instead of
as strong references.
you should declare your var as weak

Setting common values for viewControllers in UITabBarController with moreNavigationController

I have 'n' ViewControllers, all taking a common variable 'cv1', I would like to set this cv1 before the controllers are loaded.
Everything is fine till I set only 4 controllers, (i.e) without More tab item. and once I introduce more controllers, delegate "tabBarController:didSelectViewController" doesn't trigger for the modules in UIMoreNavigationController.
So I tried setting the UINavigationController delegate for my TabBarController Class and handled the other module tabItem selection like
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
if navigationController != self.moreNavigationController
{
//navigation happening in some other tab Item
return
}
if navigationController.viewControllers.count != 2
{
//The first controller of the more tabItem will be MoreViewController, followed by our desired controller in the navigation stack
return
}
guard let selectedController:MyTabItemViewController = self.getTopViewController(from: viewController) else {
print("***WARNING*** Selected controller doesn't seem to expect info :\(String(describing: selectedViewController))")
return
}
selectedController.sp = self.sp
selectedController.sb = self.sb
}
I am able to get the ViewController instance, but the viewDidLoad method gets called before UINavigationController's willShow method, I would like to set the variables before the viewDidLoad method as the UI structuring and data fetch depends on this variable.

How can I check if my NavigationController subclass has had VC's pushed & popped?

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) {
}

Reloading data when clicking back bar button of an UINavigationController

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.

Resources