I have a problem that a screen gets pushed twice. The problem is I don't know which pushViewController that is doing that, and I have so many of them. So I want to add a breakpoint to navigationController.pushViewController() function so I can see what is the previous call stack that call that function. I don't need to see the inside of pushViewController. I just need that the breakpoint stop whenever the function pushViewController at navigationController gets called. But I can't put breakpoint on the open func pushViewController() at navigationController code. How can I do that?
You can extend UINavigationController and override pushViewController function.
class MyNavigationViewController : UINavigationController {
override func pushViewController(_ viewController: UIViewController, animated: Bool) {
super.pushViewController(viewController, animated: animated)
}
}
And then you if you are using storyboard, change the class of your navigation controller to MyNavigationViewController and also in the code if you creating navigation controller from code.
Related
I have a storyboard with two view controllers. First one, VC_1, has one button that opens 2nd one - VC_2.
VC_2 also has a button that opens VC_1.
Both controllers have almost identical code:
class VC_1: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
print(“VC_1 loaded")
}
override func viewDidAppear(_ animated: Bool){ print(“VC_1 appeared") }
override func viewDidDisappear(_ animated: Bool){ print(“VC_1 disappeared") }
#IBAction func btnShowVC_2(_ sender: UIButton)
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
secondVC = storyboard.instantiateViewController(identifier: “VC_2”)
secondVC.modalPresentationStyle = .fullScreen
show(secondVC, sender: self)
}
}
The difference is only in "VC_2" instead of "VC_1" in the 2nd controller code.
I have seen this View Controller creation code in Apple documentation and many other examples around the Internet.
When I press the button on the VC_1, I see in the debug window, that VC_2 is loaded and appeared, and VC_1 is disappeared. And same, of course, happens when I press the button on VC_2 - it disappears, and VC_1 is loaded again.
My questions are:
what happens with View Controller object after "viewDidDisappear" has been called? Does it really disappear from memory, or "disappear" only means "you cannot see it on the screen?". I do not see "viewDidUnload" in the documentation...
I suppose that "viewDidLoad" means that new View Controller object was created in memory. Is there any way to load the View Controller object only once, and then hide and show it without causing "viewDidLoad" to be called? I tried to do it with global variable "secondVC" but got "Application tried to present modally an active controller" error.
viewDidDisappear: called after the view is removed from the windows’
view hierarchy. No, View controller object just left the view property. By the way the amount of memory used by view controllers is negligible. So dont think about too much. If you want to catch when Your View controller object release from the memory put
deinit { print("vc deallocated") }
viewDidUnload, it has been deprecated since the iOS
6, which used to do some final cleaning.
Partly true. Keep in mind ViewDidload called one time for the life cycle of view controller. There is a method called before viewdidload but this is not related with your question.
In addition to "There is a method before viewdidload" -> loadView( ) is a method managed by the viewController. The viewController calls it when its current view is nil. loadView( ) basically takes a view (that you create) and sets it to the viewController’s view (superview).
ViewDidDisapear and ViewWillDisappear are still called if another View Controller is above the (say, for example, you push a view controller on top of it). Is there a function that is only called once the view controller is removed from the navigation stack? Adding a function to the back button works, but what if the user decides to to the edge pan gesture to dismiss the view? Is there an action that accounts for both events?
Yes, I can think of few ways to do this off the top of my head
One option would be to add some code to a dealloc method of the UIViewController.
If you don't expect the view controller controller to get deallocated when it leaves the stack you can also set a UINavigationControllerDelegate for the UINavigationController and define
func navigationController(_ navigationController: UINavigationController,
didShow viewController: UIViewController,
animated: Bool) {
guard let poppedViewController =
navigationController.transitionCoordinator?.viewController(forKey: .from)
<Do something with the popped VC>
It's easier to show you a drawing and then explain.
Dashboard Storyboard
I have 2 separate UIViewControllers (i've included just one in the drawing, the other is irrelevant) embedded in container view called ContainerViewController.
Post Storyboard
NewPostViewController shows a UIButton that presents TextPostViewController. As you can see, all of them are embedded in UINavigationControllers. Now, once the completion block of the new post is being called, I have to present the ContainerViewController and it needs to handle it's own logic. The problem is that it's embedded in UINavigationController and once I present it, the UITaBbar is hidden.
I tried to do this:
self.performSegue(withIdentifier: "TextPostToNavContainerVC", sender: nil)
The transition is successful but I'm losing the UITabBar, even though in the DashboardViewController and the ContainerViewController I called:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
tabBarController?.tabBar.isHidden = false
}
What am I doing wrong or is there are better way to do that?
You should instantiate the tab bar controller. not the view controller.
Imagine you're putting a initial view controller ahead of your tab bar controller. Making your tab bar not being pushed
If I undestand it correctly.
You are doing this
Segue connect to a view controller
But you should actually do this Segue connected to a tab bar controller
You can try to add it as a child to control it's frame like this
let textPost = self.storyboard?.instantiateViewController(withIdentifier: "containerID") as! TextPostToNavContainerVC
textPost.view.frame = CGRect(x:20,y:0,width:self.view.frame.width,height:self.view.frame.height-50)
self.view.addSubview(nvc.view)
self.addChildViewController(textPost)
textPost.didMove(toParentViewController: self)
In iOS app, there may be several view controllers. They may perform segues from one to another. the question is how to detect each view controller about whether it is dismissed or not when implementing segue. Thanks.
You have access to:
override func viewWillDisappear(animated: Bool) {
}
override func viewDidDisappear(animated: Bool) {
}
// Called when the view controller will be removed from memory.
deinit {
}
Which can help you managed things based on that state of a view controller.
I'm not sure if you can detect whether or not it was dismissed, but you can set a variable "viewControllerDismissed = true" in performSegueWithIdentifier that will be detected in the VC behind the one being dismissed.
I have created 5 Tabs in my application. In Tab1 i have UITableView. On didSelectRowAtIndexPath i am navigating to another UIView in which I am showing my all 5 Tabs. And I also play song in that navigated view.
Now when I click Back button in navigation and i again go to my original view, i am able to call viewWillDisappear (as expected and normal situation).
But when I click directly another tab then viewWillDisappear is not called in the navigated View. Why this Happens??
I have just thought in a way that when I directly clicks the another Tab then the view in Tab1 will call viewWillDisappear. But the navigated view will not call that method.
So what could be possible solutions?? kindly give some hints...
I got the viewWillDisappear to work by calling
self.definesPresentationContext = true
in viewDidLoad()
Otherwise, viewWillDisappear wasn't getting called. And this is what I have in it:
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.searchController.active = false
tableView.reloadData()
}
Hope this helps.
I think that you need to catch the event when you switch between tabs. When you switch from Tab1 to Tab2, as you expect, viewWillDisappear of Tab1 will not be called. Instead, the viewWillAppear of Tab2 will be called.
Else if you want to catch the event when you switch tabs, check this link.
That is because the you have created tabBarController and you are pushing it as a viewController from the mainView.
So whole TabBarController is treated as one viewController.
Hope this helps you.
if you want to call this method create the nsnotification center object viewWillDisappear
and when you want to call this method post this notification.
That's how I got it to work.
// UITabBarControllerDelegate
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
print("Selected view controller")
// do stuff...
self.navigationController?.setNavigationBarHidden(false, animated: false)
return true
}