I am having this issue where the content of my view controller is pushed down after pushing a view controller to the navigation controller and then popping that view controller.
The base view controller is a storyboard, and then I use that as a container for a view controller I created via XIB
This is how I set up the base view controller, in my viewDidLoad I call this function:
func loadScoresView(){
if scoresVC.parent != nil {
refresh()
return
}
scoresVC.view.removeFromSuperview()
scoresVC = LiveScoresViewController(nibName: "LiveScoresViewController", bundle: nil)
scoresVC.delegate = self
let collectionViewHeight:CGFloat = 80
scoresVC.view.frame = CGRect(x: view.bounds.minX, y: view.bounds.minY + collectionViewHeight, width: view.bounds.width, height: view.bounds.height - collectionViewHeight)
scoresVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.view.addSubview(scoresVC.view)
scoresVC.didMove(toParent: self)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(false, animated: animated)
navigationController?.navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
navigationController?.navigationBar.shadowImage = UIImage()
navigationController?.view.setNeedsLayout() // force update layout
navigationController?.view.layoutIfNeeded() // to fix height of the navigation bar
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
navigationController?.interactivePopGestureRecognizer?.delegate = nil
navigationController?.interactivePopGestureRecognizer?.isEnabled = false
}
Related
I currently working on an iOS app and I want to use a bottom navigation drawer from material-io. So I did it like it is explained in the examples on the site. But when I present the navigation Drawer the ViewController only gets a bit darker and the contentView of the drawer isn't shown.
Here is my Code:
import Foundation
import UIKit
import MaterialComponents
class CreateSubjectView: UIViewController, UITextFieldDelegate {
...
override func viewDidLoad() {
...
let bottomDrawerViewController = MDCBottomDrawerViewController()
self.modalPresentationStyle = .popover
let newViewController = self.storyboard?.instantiateViewController(withIdentifier: "TEST")
bottomDrawerViewController.contentViewController = newViewController
present(bottomDrawerViewController, animated: true, completion: nil)
...
}
...
}
Your view controller to be shown in drawer must have specified preferred content size.
Here is a demo of minimal controller. (Note: modalPresentationStyle = .popover has no effect on MDCBottomDrawerViewController)
Tested with Xcode 12
// button action in parent controller
#objc private func presentNavigationDrawer() {
let bottomDrawerViewController = MDCBottomDrawerViewController()
bottomDrawerViewController.contentViewController = DemoViewController()
present(bottomDrawerViewController, animated: true, completion: nil)
}
}
class DemoViewController: UIViewController {
override func loadView() {
super.loadView()
let view = UIView()
view.backgroundColor = .red
self.view = view
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// specify your content preferred height explicitly
self.preferredContentSize = CGSize(width: 0, height: 400) // required !!
}
#available(iOS 11.0, *)
override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
// specify your content preferred height explicitly
self.preferredContentSize = CGSize(width: 0, height: 400) // required !!
}
}
Move this to viewWillAppear/ viewDidAppear once as it's too early for viewDidLoad to present a vc
class CreateSubjectView: UIViewController, UITextFieldDelegate {
let bottomDrawerViewController = MDCBottomDrawerViewController()
var once = true
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if once {
let newViewController = self.storyboard?.instantiateViewController(withIdentifier: "TEST")
bottomDrawerViewController.contentViewController = newViewController
present(bottomDrawerViewController, animated: true, completion: nil)
once = false
}
}
}
When going back from view controller with:
navigationItem.largeTitleDisplayMode = .never
to view controller with:
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.largeTitleDisplayMode = .always
in viewDidLoad I have a strange animation glitch on navigation bar - a white stripe appears
It's appears only when searchBar is visible
How can I fix this?
I found problem in appear transition, there are some clear space between navigationbar and searchbar. They have independent animation. Here is it.
So, you can add common background for them throw transition in "viewWillAppear" method using transitionCoordinator. That its view.
edited: strong text add logic, when your transition from search table, and navigation is hide at this moment. (searchController.hidesNavigationBarDuringPresentation = true)
override func viewWillAppear(_ animated: Bool) {
let navigationBack = UIView()
if navigationItem.searchController?.isActive == false {
navigationBack.frame = self.navigationController?.navigationBar.frame ?? CGRect.zero
}
navigationBack.backgroundColor = navigationController?.navigationBar.barTintColor
let containerView = transitionCoordinator?.containerView
transitionCoordinator?.animateAlongsideTransition(in: containerView, animation: { (context) in
containerView?.addSubview(navigationBack)
navigationBack.frame.size.height += self.navigationItem.searchController?.searchBar.frame.height ?? 0
}, completion: { (context) in
navigationBack.removeFromSuperview()
})
super.viewWillAppear(animated)
}
Set background color of navigation controller view to the same color of navigation bar seems to work around the issue for me.
navigationController?.view.backgroundColor = navigationController?.navigationBar.barTintColor
What helped was to add this in viewController with largeTitles enabled:
override func viewWillAppear(_ animated: Bool) {
let navigationBack = UIView()
navigationBack.frame = (self.navigationController?.navigationBar.frame)!
navigationBack.frame.size.height = 44
navigationBack.backgroundColor = navigationController?.navigationBar.barTintColor
let containerView = transitionCoordinator?.containerView
transitionCoordinator?.animateAlongsideTransition(in: containerView, animation: { (context) in
containerView?.addSubview(navigationBack)
navigationBack.frame.size.height = (self.navigationItem.searchController?.searchBar.frame.height)! + (self.navigationController?.navigationBar.frame.height)!
}, completion: { (context) in
navigationBack.removeFromSuperview()
})
super.viewWillAppear(animated)
}
and this in viewController with largeTitles disabled:
override func viewWillAppear(_ animated: Bool) {
let navigationBack = UIView()
navigationBack.frame = self.navigationController?.navigationBar.frame ?? CGRect.zero
navigationBack.backgroundColor = navigationController?.navigationBar.barTintColor
let containerView = transitionCoordinator?.containerView
transitionCoordinator?.animateAlongsideTransition(in: containerView, animation: { (context) in
containerView?.addSubview(navigationBack)
navigationBack.frame.size.height = 44
}, completion: { (context) in
navigationBack.removeFromSuperview()
})
super.viewWillAppear(animated)
}
I have structure:
*- TabBarViewController (Root)
*-- NavigationViewController
*---- ChatViewController
*-- NavigationViewController
*---- MenuViewController
and while I'm switching tabbar items, viewWillAppear in (Chat, Menu) called only once, but in NavigationVC called every times i switch.
Is it possible to call automatically viewWillAppeare in Chat and Menu ViewControllers while switching items?
super.viewWillAppear is inside method.
my code looks like:
class TabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let v1 = storyboard!.instantiateViewController(withIdentifier: "ChatViewController")
let v2 = storyboard!.instantiateViewController(withIdentifier: "MenuViewController")
viewControllers = [v1,v2]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
class ChatViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print(self,#function)
}
}
class MenuViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print(self,#function)
}
}
It works on clear new project but on old ( where im working, and i have loot of funcionality, doesn't work)
StoryboardId is linked to NavigationViewController in Storyboard
I found issue:
In extension UINavigationController i have method
open override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if !UserDefaults.standard.isUserPresented {
navigationBar.barTintColor = .rgbColor(red: 43, green: 43, blue: 43, alpha: 1)
} else {
navigationBar.barTintColor = .rgbColor(red: 100, green: 100, blue: 100, alpha: 1)
}
}
and this block viewWillAppear in child view controllers in NavigationBar
Your question is not clear about adding viewcontroller to tabbarcontroller and navigation controller . I have created everything in storyboard . View will appear in view controller are :
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("Menu View will appear")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("Chat View will appear")
}
Am able to get below output when i switch :
Menu View will appear
Chat View will appear
Menu View will appear
Chat View will appear
I want to make a Navigation Bar that goes transparent in detail but with my current code the Bar doesn't return to it's non transparent state. How can this be fixed? I want to this code to also work in the MoreNavigationController from the UITabBarController.
The code that has been placed in the Detail ViewController.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.transitionCoordinator?.animate(alongsideTransition: { [weak self](context) in
self?.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
self?.navigationController?.navigationBar.shadowImage = UIImage()
}, completion: { context in
})
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.transitionCoordinator?.animate(alongsideTransition: { [weak self](context) in
self?.navigationController?.navigationBar.setBackgroundImage(nil, for: UIBarMetrics.default)
self?.navigationController?.navigationBar.shadowImage = nil
}, completion: { context in
})
}
Add the code below to DetailViewController.
It was confirmed that this code also works in the UINavigationController from the UITabBarController.
NextController.swift
class NextViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setNavigationBar()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
self.navigationController?.navigationBar.shadowImage = nil
self.navigationController?.navigationBar.isTranslucent = true
}
func setNavigationBar() {
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.isTranslucent = false
}
}
Preview
How can I hide a navigation bar from first ViewController or a particular ViewController in swift?
I used the following code in viewDidLoad():
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.isNavigationBarHidden = true
}
and also on viewWillAppear:
override func viewWillAppear(animated: Bool) {
self.navigationController?.isNavigationBarHidden = true
}
Both methods hide the navigation controller from all ViewControllers.
If you know that all other views should have the bar visible, you could use viewWillDisappear to set it to visible again.
In Swift:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.setNavigationBarHidden(false, animated: animated)
}
Swift 3
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Hide the navigation bar on the this view controller
self.navigationController?.setNavigationBarHidden(true, animated: animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Show the navigation bar on other view controllers
self.navigationController?.setNavigationBarHidden(false, animated: animated)
}
You can unhide navigationController in viewWillDisappear
override func viewWillDisappear(animated: Bool)
{
super.viewWillDisappear(animated)
self.navigationController?.isNavigationBarHidden = false
}
Swift 3
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: animated)
}
You could also create an extension for this so you will be able to reuse the extension without implementing this again and again in every view controller.
import UIKit
extension UIViewController {
func hideNavigationBar(animated: Bool){
// Hide the navigation bar on the this view controller
self.navigationController?.setNavigationBarHidden(true, animated: animated)
}
func showNavigationBar(animated: Bool) {
// Show the navigation bar on other view controllers
self.navigationController?.setNavigationBarHidden(false, animated: animated)
}
}
So you can use the extension methods as below
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
hideNavigationBar(animated: animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
showNavigationBar(animated: animated)
}
In Swift 3, you can use isNavigationBarHidden Property also to show or hide navigation bar
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Hide the navigation bar for current view controller
self.navigationController?.isNavigationBarHidden = true;
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Show the navigation bar on other view controllers
self.navigationController?.isNavigationBarHidden = false;
}
Ways to hide Navigation Bar in Swift:
self.navigationController?.setNavigationBarHidden(true, animated: true)
self.navigationController?.navigationBar.isHidden = true
self.navigationController?.isNavigationBarHidden = true
Ways to show Navigation Bar in Swift:
self.navigationController?.setNavigationBarHidden(false, animated: true)
self.navigationController?.navigationBar.isHidden = false
self.navigationController?.isNavigationBarHidden = false
private func setupView() {
view.backgroundColor = .white
navigationController?.setNavigationBarHidden(true, animated: false)
}
Alternative
in viewDidLoad use this settings
title = "Madman"
navigationController?.isNavigationBarHidden = false
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.largeTitleDisplayMode = .always
Check the constraints of Collectionview, scrollview or tableView
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
Want to Hide the NavigationBar on the First ViewController
override func viewWillAppear(animated: Bool) {
self.navigationController?.isNavigationBarHidden = true
}
Want to Show the NavigationBar on the Second ViewController
override func viewWillAppear(animated: Bool) {
self.navigationController?.isNavigationBarHidden = false
}
/*. Swift 5 */
let controller = self.storyboard?.instantiateViewController(withIdentifier: "sc_userNavigation") as! UserNavigationViewController
let navigationController = UINavigationController(rootViewController: controller)
navigationController.setNavigationBarHidden(true, animated: false)
navigationController.modalPresentationStyle = .fullScreen
self.present(navigationController, animated: false, completion: nil)
In IOS 8 do it like
navigationController?.hidesBarsOnTap = true
but only when it's part of a UINavigationController
make it false when you want it back
I use a variant of the above, and isolate sections of my app to be embedded in differing NavControllers. This way, i don't have to reset visibility. Very useful in startup sequences, for example.
Call the set hide method in view Will appear and Disappear. if you will not call the method in view will disappear with status false.It will hide the navigation bar in complete navigation hierarchy
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: true)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.setNavigationBarHidden(false, animated:true)
}
You can do it from the window controller (Swift3)
class WindowController: NSWindowController {
override func windowDidLoad() {
super.windowDidLoad()
window?.titleVisibility = .hidden
}
}