I have MapViewController embedded in a UINavigationController. I push to multiple view controllers from this view controller, pop to MapViewController from these view controllers.
In MapViewController I have google map its top position starts from safeAreaLayoutGuide. I hide and show the navigation controller based on multiple conditions. For example when I show subview1 I hide the navigation bar, when I show subview2 I show the navigation bar, etc...
Now I want to change the mapview top position whenever the navigation bar is visibility is changed.
How to solve this?
I checked UINavigationControllerDelegate. There is no delegate method called when navigation bar is shown/hidden where I can update the mapview top position.
viewDidLayoutSubviews is not called when navigation bar visibility changed.
I want to do this in MapViewController only. Not in pushed view controller from this one.
You can add a property observer to the isHidden property of navigationBar and then observe any changes made, i.e.
var observer: NSKeyValueObservation?
override func viewDidLoad() {
super.viewDidLoad()
self.observer = self.navigationController?.navigationBar.observe(\.isHidden, options: [.new], changeHandler: { (navigationBar, changes) in
if let newValue = changes.newValue {
print(newValue)
topConstraint.constant = newValue ? 0 : self.view.safeAreaInsets.top
}
})
}
When any changes are made in the isHidden property of navigationBar, we'll get a callback in the closure and act accordingly.
Related
I have 2 ViewController for both implemented:
let searchBar = UISearchBar()
func viewDidLoad() {
navigationItem.titleView = searchBar
}
When I push second view controller and try go back with swipe gesture, all my navigation items disappearing irrevocably.
Maybe someone has an idea how to fix it?
Images:
SearchBar First VC
https://imgur.com/QJxflWP.png
SearchBar Second VC
https://imgur.com/FUBo0t6.png
NavigationBar When starting back swipe
https://imgur.com/G2FXrnq.png
A navigation item is associated with the view controller. So if you want search bar in both view controllers you have to set the search bar for both the view controllers.
Also, you can use searchController property to show the search bar in the navigation controller. So that you can show the title and search bar in the navigation bar.
A navigation item is associated with the view controller. So if you want to show search bar in both view controllers you have to add it separately in the viewDidLoad method.
And also you can use searchController property to show the search bar in the navigation bar. So that you can show both title and search bar in the navigation bar.
I finally solve this problem by handling UINavigationBarDelegate methods.
Use your own UINavigationController because UINavigationController is the default delegate of UINavigationBar, i don't need to reassign delegate to other class to handle.
implement these code and get the searchField inside of UISearchBar to minor change its frame.
extension YourNavigationController: UINavigationBarDelegate {
func navigationBar(_ navigationBar: UINavigationBar, shouldPop item:
UINavigationItem) -> Bool {
if let inSearchBar = item.titleView as? UISearchBar,
let searchField = inSearchBar.value(forKey: "searchField") as? UIView {
searchField.frame = CGRect(x: 0, y: 0, width: searchField.frame.width + 1, height: 36)
}
}
}
Why i do this is because i found that if the frame of searchField don't change, then the search bar would disappear. It is not a good way to do but a kind of workaround to show the correct view. In iOS 13, i believe we can check to use searchTextField to replace searchField, a property provided by Apple doc.
I want the green view to move forward from the container view as follows.
However, when I add a tab bar controller, the green view is cut off as follows.
I tried the following codes so that the green view is not cut off. But it did not work.
containerView.clipsToBounds = false
containerView.layer.zPosition = 100
self.view.bringSubview(toFront: containerView)
The problem seems to be not in the container view. Because when tab bar controller was added, green view started to be cut off.
When I add a tab bar controller, how can I prevent the green view from being cut off?
The problem is that UITransitionView in your UITabBarController clips all subviews. You can fix this easily if you remove clipsSubview from every subview in your TabBarController. I make this with custom TabBarController. Here is my code
class CustomTabBarViewController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
for item in self.view.subviews{
item.clipsToBounds = false
}
self.view.clipsToBounds = false
}
}
How do I have the navigation bar specific to each view controller? On the right side, the navigation bar stays with the view controller, and the left view controller has its own navigation controller. Should I just make a custom transition? Any ideas ?
Add this code in your first viewController
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.setNavigationBarHidden(false, animated: false)
}
Then add navigationBar in your viewcontrollers from storyboad. Or you can add one in viewDidLoad programmatically
I have two viewcontrollers. The first viewcontroller is collection view controller and i set self.navigationController?.hidesBarsOnSwipe = true in viewDidLoad().
When I push the second viewController from the visible cell of collectionView, the navigation bar is showing in the second viewController but if I scroll the collectionView cell and when push the navigation is not showing.
Can anyone tell me what the problem is?
scrolling is done via swipe gesture, so it triggers your code:
self.navigationController?.hidesBarsOnSwipe = true
because navigation controller is shared between all view controllers presented on top of it, it's properties (like hidden bar) preserves pushing / popping.
Common pattern is to change it's state in overrided lifecycle methods, eg:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.hidesBarsOnSwipe = false
self.navigationController?.setNavigationBarHidden(false, animated: true)
}
and reverting those state in viewWillDisappear
When this property is set to true, an upward swipe hides the navigation bar and toolbar. A downward swipe shows both bars again. If the toolbar does not have any items, it remains visible even after a swipe. The default value of this property is false. (get it from apple)
See the doc https://developer.apple.com/documentation/uikit/uinavigationcontroller/1621883-hidesbarsonswipe
It means when you swipe up it will hide and when swipe down it will shown. That's the reason.
To fix it you can add following code to the other controller
[self.navigationController setNavigationBarHidden:NO animated:YES];
Don't get much insight what exactly you implemented, but try to unhide navigation bar in second view controller.
Add below code in viewDidLoad method of second View controller.
self.navigationController?.isNavigationBarHidden = false
Put this self.navigationController?.hidesBarsOnSwipe = false and this self.navigationController?.setNavigationBarHidden(false, animated: true) in your second view controller.
You might want to move your self.navigationController?.hidesBarsOnSwipe = true from viewDidLoad to viewWillAppear in your first view controller.
I have the hidesBottomBarWhenPushed = true set for one of my UIViewController's (call it ViewControllerA) that is pushed onto my UINavigationController stack. I also opt to show the bottomBar when I push a new ViewController ontop of ViewControllerA. Therefore I have:
class ViewControllerA: UIViewController {
override func viewWillDisappear(animated: Bool) {
self.hidesBottomBarWhenPushed = false
}
override func viewWillAppear(animated: Bool) {
self.hidesBottomBarWhenPushed = true
}
This all works fine.
When I push ViewControllerA, the bottom bar hides.
When I push any other ViewController, the bottom bar shows.
However, when I am traveling backwards in the navigation stack (aka hitting the UIBarButtonItemBack button), I cannot get the bottomBar to hide when I pop the navigation stack to reveal ViewControllerA.
What am I missing? Thanks!
Got it! Here's what worked:
class ViewControllerCustom: UIViewController {
init() {
self.hidesBottomBarWhenPushed = true
}
override func viewDidAppear(animated: Bool) {
self.hidesBottomBarWhenPushed = false
}
}
And then in every UIViewController's custom implementation of BarButtonItemBack pressed I check to see if the previous view controller (that will be popped to needs to hide the tab bar). Granted I abstracted this out into a general function so I didn't need to repeat code, but here's the concept. Thanks for the help figuring this out though!
func barButtonItemBackPressed(button: UIButton) {
var viewControllers = self.navigationController!.viewControllers as! [UIViewController]
if ((viewControllers[viewControllers.count - 2]).isKindOfClass(ViewControllerCustom.self)) {
(viewControllers[viewControllers.count - 2] as! ViewControllerCustom).hidesBottomBarWhenPushed = true
}
self.navigationController?.popViewControllerAnimated(true)
}
I believe the intended use of this property is to hide the bar when pushed. So, when your view controller appears after the top-most one is popped, it wasn't pushed on the stack, so it doesn't change the tab bar's appearance.
This leaves you with two options:
1) Keep the bottom bar for all view controllers. When text is being entered, the keyboard covers the bottom bar.
2) Hide the bottom bar for View Controller A, as well as any other view controller that is pushed on top of A.