UIPageViewController' s Child View Controller has wrong lifecycle - ios

When i close to UiPageVC, after UiPageVC's viewWillDisappear call, ChildVC's viewWillAppear and viewDidAppear function calls. At the end, UiPageVC's viewDidDisappear works.
I need to work ChildVC like normal way. When i dismiss to UiPageVC, viewWillDisappear and viewDidDisappear of ChildVC should call.

According to this answer, viewWillDisappear(_:) & viewDidDisappear(_:) might not get called in child view controller.
Following Apple's Doc, override viewWillDisappear(_:) & viewDidDisappear(_:) in UIPageViewController subclass.
override func viewWillDisappear(_ animated: Bool) {
super.viewWillAppear(animated)
children.forEach { $0.beginAppearanceTransition(false, animated: true) }
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
children.forEach { $0.endAppearanceTransition() }
}

Related

NotificationCenter: How to add a notification in BaseController without it being added multiple times?

I have a BaseViewController and all the view controllers in my app inherits from this BaseViewController class. I want to listen to a custom notification in some of my view controllers. I added the following code in viewWillAppear and viewWillDisappear methods of BaseViewController
override func viewWillAppear(_ animated: Bool) {
NotificationCenter.default.addObserver(self, selector: #selector(self.actOnNotification), name: NSNotification.Name("MyNotification"), object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(NSNotification.Name("MyNotification"))
}
My app is designed in such a way that the main page gets loaded after a custom splash view controller and an another view controller (both of these inherit from BaseViewController too). Basically, it is the third view controller in the stack.
Now, the actOnNotification method gets called three times when the home page of my app loads. Is there a way that I can have it called only for once, when the home page loads?
It obviously works if I listen to notification directly in home page of the app.
You can used a navigationController for mainViewController after that you implement addObserver in navigationController
class MainNavigationViewController: UINavigationController {
override func viewWillAppear(_ animated: Bool) {
NotificationCenter.default.addObserver(self, selector: #selector(self.actOnNotification), name: NSNotification.Name("MyNotification"), object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(NSNotification.Name("MyNotification"))
}
}
Also you can get currentViewcontroller
guard let currentViewcontroller = self.viewControllers.last() else { return}

How to handle the sequence about cancel and call async API in viewcontroller life cycle

I have a tabbarVC embed two viewcontrollers, VC1 and VC2.
In VC viewWillAppear I will call the API, and viewWillDisappear I will cancel the API .
I face a problem, when I in VC1 switch to VC2, and VC2's API be canceled.
How to handle viewcontroller life cycle have sequence in this situation?
I use Moya and Alamofire in this project.
VC1:
func viewWillAppear(_ animated: Bool) {
print("VC1 call API")
callAPI()
}
func viewWillDisappear(_ animated: Bool) {
print("VC1 cancel API")
cancelAPI()
}
VC2:
func viewWillAppear(_ animated: Bool) {
print("VC2 call API")
callAPI()
}
func viewWillDisappear(_ animated: Bool) {
print("VC2 cancel API")
cancelAPI()
}
Log: VC1 -> VC2 (API be canceled)
VC2 call API
VC1 cancel API

popViewController(animated: true) animation works slow

I have a tap gesture recognizer added on the imageView, so when user taps on the image below function is called.
#objc func handleTap(_ sender: UITapGestureRecognizer) {
self.navigationController?.popViewController(animated: true)
}
When I am pushing to detail view controller animation looks fine and works smooth, but when I am using the above method to popViewController(animated: true) animation is not so smooth and might even freeze for a second.
I am also using viewWillAppear and viewWillDisappear methods to hide navigation bar in detail view controller. I have read that this could cause animation to work slow, but still could not manage to find answer how to solve this.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.isNavigationBarHidden = true
}
override func viewDidDisappear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.isNavigationBarHidden = false
}
Try this, I am not sure but it may work
DispatchQueue.main.async {
self.navigationController?.popViewController(animated: true)
}
In your override of viewDidDisappear(_ animated: Bool) you call super.viewWillAppear(animated). As you have probably figured out by now, you should be calling super.viewDidDisappear(animated) instead. Your corrected code should read:
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
navigationController?.isNavigationBarHidden = false
}

can't update navigationbar when popping to root view controller

I have a UITabBar. In one tab is a UINavigationController. Let's say the 2nd or 3rd UIViewController in the stack has this:
class ChildVC: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: false)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.setNavigationBarHidden(false, animated: false)
}
}
If you click the current tab it will popToRootViewController() on the navigation controller. The problem is, in viewWillDisappear(:) of my current tab the navigationController is nil. So the navigationBar remains hidden.
What's the proper way to handle this? Should I just set the navigation bar to visible in the root view controller's viewDidAppear? That seems hacky.
If anybody else sees this, I don't know why the reference to self.navigationController gets set to nil before viewWillDisappear when you popToRootViewController() but a workaround I found was just to store your own reference to it.
class ChildVC: UIViewController {
private weak var navCtrl: UINavigationController?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navCtrl = navigationController
navCtrl?.setNavigationBarHidden(true, animated: false)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navCtrl?.setNavigationBarHidden(false, animated: false)
}
}
You should override the viewWillAppear in the rootViewController and setNavigationBarHidden from there. navigationController is nil at viewDidDisappear because it has already been popped off the navigation stack.

UINavigationControllerDelegate‘s didShowViewController method was called twice

class ViewController: UIViewController, UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
navigationController!.delegate = self
}
func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated: Bool) {
print("showViewController")
}
func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
print("sss")
}
func update() {
let vc = SecondViewController()
navigationController!.pushViewController(vc, animated: true)
}
}
this is the first controller of my demo , and in console:
sss
showViewController
showViewController
the "didShowViewController" was called twice.
I'm not sure what's going on
-----------------some test----------------------
I add some log in these method of controller : loadView,viewDidLoad ,viewWillAppear,viewDidAppear , and the order of these log is:
loadView
viewDidLoad
viewWillAppear
will:<NaviDemo.ViewController: 0x7fe8c9533050>
<NaviDemo.ViewController: 0x7fe8c9533050>
viewDidAppear
<NaviDemo.ViewController: 0x7fe8c9533050>
I hit the same issue in my code. I was able to work around it by waiting until viewDidAppear to set the navigation delegate instead of setting it in viewDidLoad. To translate it to your example:
override func viewDidLoad() {
super.viewDidLoad()
}
// ...
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
navigationController!.delegate = self
}
didShowViewController is called twice because the first time it is called when the navigation controller transitions to showing the view controller. And then it is called again by the navigation controller's own viewDidAppear when it appears on screen, using the topViewController as the controller param which in this case is the same as the controller the first time it was called.
The UINavigationController has displayed two instances of a UIViewController
From the UINavigationControllerDelegate documentation
Called just after the navigation controller displays a view
controller’s view and navigation item properties.
Instead of logging "showViewController", log the UIViewController instance to see what's going on
func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated: Bool) {
print(viewController)
}

Resources