UINavigationBar Large Title doesn't appear when scroll view up - ios

I have implemented a feature, when you press on a UITabBar icon and viewController1 scrolls up using its UIScrollView. It works perfectly, but if I scroll view down and stop somewhere, then switch to another viewController2, then get back to viewController1 and press tabBar icon - the viewController1 will scroll up, but Large Title will never be showed, and I should press tabBar icon one more time to show it:
The code I use for scroll up the VC1:
private var biggestTopSafeAreaInset: CGFloat = 0
override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
self.biggestTopSafeAreaInset = max(view.safeAreaInsets.top, biggestTopSafeAreaInset)
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if tabBarController.selectedIndex == 0 {
let navigationVC = viewController as? UINavigationController
let firstVC = navigationVC?.viewControllers.first as? CurrencyViewController
guard let scrollView = firstVC?.view.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView else { return }
if traitCollection.verticalSizeClass == .compact {
scrollView.setContentOffset(CGPoint(x: 0, y: -view.safeAreaInsets.top, animated: true)
} else {
scrollView.setContentOffset(CGPoint(x: 0, y: -biggestTopSafeAreaInset, animated: true)
}
}
}
I tried to track biggestTopSafeAreaInset in different stages of VC1 life, but it always has the same number - 196.0. But then why it doesn't scroll till the Large Title after viewControllers switch?

in your tableView set contentInsetAdjustmentBehavior to never
tableView.contentInsetAdjustmentBehavior = .never
in controller update the ui of navigation bar again
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.main.async { [weak self] in
self?.navigationController?.navigationBar.sizeToFit()
}
}
here is the navigation controller
class BaseNavigationController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 15.0, *) {
let scrollAppearance = UINavigationBarAppearance()
scrollAppearance.shadowColor = .white
scrollAppearance.backgroundColor = .white
let navigationBarAppearance = UINavigationBarAppearance()
navigationBarAppearance.configureWithDefaultBackground()
navigationBarAppearance.backgroundColor = .white
navigationBarAppearance.largeTitleTextAttributes = [
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 26),
NSAttributedString.Key.foregroundColor: UIColor.black
]
navigationBarAppearance.titleTextAttributes = [
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 17),
NSAttributedString.Key.foregroundColor: UIColor.black
]
UINavigationBar.appearance().backIndicatorImage = UIImage(named: "back-arrow")
UINavigationBar.appearance().standardAppearance = navigationBarAppearance
UINavigationBar.appearance().compactAppearance = navigationBarAppearance
UINavigationBar.appearance().scrollEdgeAppearance = scrollAppearance
navigationBar.tintColor = .black
navigationBar.prefersLargeTitles = true
navigationBar.isTranslucent = false
navigationItem.largeTitleDisplayMode = .automatic
} else {
navigationBar.largeTitleTextAttributes = [
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 26),
NSAttributedString.Key.foregroundColor: UIColor.black
]
navigationBar.titleTextAttributes = [
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 17),
NSAttributedString.Key.foregroundColor: UIColor.black
]
navigationBar.tintColor = .black
navigationBar.prefersLargeTitles = true
navigationBar.isTranslucent = false
navigationItem.largeTitleDisplayMode = .automatic
navigationBar.barTintColor = .white
}
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .darkContent
}
}
here is the Tabbar Controller
class TabbarController:UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let c1 = C1()
let c2 = C2()
let c3 = C3()
c1.tabBarItem = UITabBarItem(title: "Home", image: UIImage(named: "home786"), tag: 0)
c1.tabBarItem.tag = 0
let nav1 = BaseNavigationController(rootViewController: c1)
c2.tabBarItem = UITabBarItem(title: "Setting", image: UIImage(named: "home786"), tag: 0)
c2.tabBarItem.tag = 1
let nav2 = BaseNavigationController(rootViewController: c2)
c2.tabBarItem = UITabBarItem(title: "User", image: UIImage(named: "home786"), tag: 0)
c2.tabBarItem.tag = 2
let nav3 = BaseNavigationController(rootViewController: c3)
viewControllers = [nav1,nav2,nav3]
selectedViewController = nav1
tabBarController?.viewControllers?.first?.view.backgroundColor = .red
}
}

Try to add this in viewDidLoad:
view.addSubview(UIView())
this single line block large title navigation Bar... I don't Know why, but this trick fix momentarily the issue...

After some research I found out what can fix my problem. If you call this method with a small delay in tabBarController didSelect then it will be possible to see a Large Title after switching viewControllers. But I still can't figure out exactly why it happened...
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
navigationVC?.navigationBar.sizeToFit()
}

Related

How do you remove a navigation bar's large title bottom border before iOS 13?

I want to hide/remove navigation bar large title bottom border in iOS 12. In newer versions it works fine.
func setupTransparentNavigationBar() {
let textAttributes = [NSAttributedString.Key.foregroundColor: UIColor.colorWhite()]
navigationController?.navigationBar.tintColor = UIColor.colorWhite()
navigationController?.navigationBar.backgroundColor = UIColor.clear
navigationController?.navigationBar.isTranslucent = true
if #available(iOS 13, *) {
let appearance = UINavigationBarAppearance()
appearance.configureWithTransparentBackground()
appearance.backgroundColor = .clear
appearance.titleTextAttributes = textAttributes
// Customizing our navigation bar
navigationItem.standardAppearance = appearance
navigationItem.scrollEdgeAppearance = appearance
} else {
navigationController?.navigationBar.titleTextAttributes = textAttributes
navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
navigationController?.navigationBar.shadowImage = UIImage()
}
}
Is there any way to remove this? Or is it native iOS behavior?
Try to you use this extension:
extension UINavigationController {
func hideHairline() {
if let hairline = findHairlineImageViewUnder(navigationBar) {
hairline.isHidden = true
}
}
func restoreHairline() {
if let hairline = findHairlineImageViewUnder(navigationBar) {
hairline.isHidden = false
}
}
func findHairlineImageViewUnder(_ view: UIView) -> UIImageView? {
if view is UIImageView && view.bounds.size.height <= 1.0 {
return view as? UIImageView
}
for subview in view.subviews {
if let imageView = self.findHairlineImageViewUnder(subview) {
return imageView
}
}
return nil
}
}
use it in viewWillAppear:
navigationController?.hideHairline()

Interactive push view controller

I am looking to have an interactive push view controller. So if the user pans from the right edge of the screen, it will pop to the next view controller. I have found this CocoaPods: https://github.com/rickytan/RTInteractivePush, but it is written in Objective-C, so I am unsure how to use it. On my own I have been able to come up with a pan gesture that pushes a view, however it is not interactive:
swipeGesture = UIPanGestureRecognizer(target: self, action:#selector(swiped(_:)))
swipeGesture.delegate = self
view.addGestureRecognizer(swipeGesture)
#objc func swiped(_ gestureRecognizer: UIPanGestureRecognizer) {
let newView = View()
self.navigationController?.pushViewController(newView, animated: true)
}
Any help would be greatly appreciated!
You can do it programmatically with UIPageViewController:
Set your UIPageViewController class:
import UIKit
class MyControllerContainer: UIPageViewController {
// set UIPageViewController transition style
override init(transitionStyle style: UIPageViewController.TransitionStyle, navigationOrientation: UIPageViewController.NavigationOrientation, options: [UIPageViewController.OptionsKey : Any]? = nil) {
super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
print("init(coder:) has not been implemented")
}
var pages = [UIViewController]()
var pageControl = UIPageControl()
let initialPage = 0
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setup()
style()
layout()
}
}
Now set style, setup, and layout func:
extension MyControllerContainer {
func setup() {
dataSource = self
delegate = self
pageControl.addTarget(self, action: #selector(pageControlDragged(_:)), for: .valueChanged)
// create an array of viewController
let page1 = ViewController1()
let page2 = ViewController2()
let page3 = ViewController3()
pages.append(page1)
pages.append(page2)
pages.append(page3)
// set initial viewController to be displayed
setViewControllers([pages[initialPage]], direction: .forward, animated: true, completion: nil)
}
func style() {
pageControl.translatesAutoresizingMaskIntoConstraints = false
pageControl.currentPageIndicatorTintColor = .white
pageControl.pageIndicatorTintColor = UIColor(white: 1, alpha: 0.3)
pageControl.numberOfPages = pages.count
pageControl.currentPage = initialPage
}
func layout() {
view.addSubview(pageControl)
NSLayoutConstraint.activate([
pageControl.widthAnchor.constraint(equalTo: view.widthAnchor),
pageControl.heightAnchor.constraint(equalToConstant: 20),
view.bottomAnchor.constraint(equalToSystemSpacingBelow: pageControl.bottomAnchor, multiplier: 1),
])
}
}
set how we change controller when pageControl Dragged:
extension MyControllerContainer {
// How we change controller when pageControl Dragged.
#objc func pageControlDragged(_ sender: UIPageControl) {
setViewControllers([pages[sender.currentPage]], direction: .forward, animated: true, completion: nil)
}
}
after that set UIPageViewController delegate and datasource:
// MARK: - DataSources
extension MyControllerContainer: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let currentIndex = pages.firstIndex(of: viewController) else { return nil }
if currentIndex == 0 {
return nil // stop presenting controllers when swipe from left to right in ViewController1
} else {
return pages[currentIndex - 1] // go previous
}
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let currentIndex = pages.firstIndex(of: viewController) else { return nil }
if currentIndex == 2 {
print("Last index...")
}
if currentIndex < pages.count - 1 {
return pages[currentIndex + 1] // go next
} else {
return nil // stop presenting controllers when swipe from right to left in ViewController3
}
}
}
// MARK: - Delegates
extension MyControllerContainer: UIPageViewControllerDelegate {
// How we keep our pageControl in sync with viewControllers
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
guard let viewControllers = pageViewController.viewControllers else { return }
guard let currentIndex = pages.firstIndex(of: viewControllers[0]) else { return }
pageControl.currentPage = currentIndex
}
}
Now add your viewControllers, in my case 3:
// MARK: - ViewControllers
class ViewController1: UIViewController {
let mylabel1: UILabel = {
let label = UILabel()
label.text = "Controller 1"
label.textAlignment = .center
label.textColor = .white
label.font = .systemFont(ofSize: 20, weight: .semibold)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemRed
view.addSubview(mylabel1)
mylabel1.heightAnchor.constraint(equalToConstant: 50).isActive = true
mylabel1.widthAnchor.constraint(equalToConstant: view.frame.width).isActive = true
mylabel1.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
mylabel1.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
}
class ViewController2: UIViewController {
let mylabel2: UILabel = {
let label = UILabel()
label.text = "Controller 2"
label.textAlignment = .center
label.textColor = .white
label.font = .systemFont(ofSize: 20, weight: .semibold)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemGreen
view.addSubview(mylabel2)
mylabel2.heightAnchor.constraint(equalToConstant: 50).isActive = true
mylabel2.widthAnchor.constraint(equalToConstant: view.frame.width).isActive = true
mylabel2.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
mylabel2.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
}
class ViewController3: UIViewController {
let mylabel3: UILabel = {
let label = UILabel()
label.text = "Controller 3"
label.textAlignment = .center
label.textColor = .white
label.font = .systemFont(ofSize: 20, weight: .semibold)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBlue
view.addSubview(mylabel3)
mylabel3.heightAnchor.constraint(equalToConstant: 50).isActive = true
mylabel3.widthAnchor.constraint(equalToConstant: view.frame.width).isActive = true
mylabel3.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
mylabel3.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
}
This is the result:
The current code is perfect it you have only one viewcontroller up next.
But if you have to 2 or more viewController up next then interactive push is unuseful technique. Vies versa for the interactive pop controller we just have to pop top view form the navigation stack which make sense. Please have a look the the image below which describe the scenario for both push and pop.

How to set multi line Large title in navigation bar? ( New feature of iOS 11)

I am in process of adding large title in navigation bar in one of the application. The issue is title is little long so I will require to add two lines in large title. How can I add large title with two lines in navigation bar?
This is not about default navigation bar title! This is about large title which is introduced in iOS 11. So make sure you add suggestions by considering large title. Thanks
Based in #krunal answer, this is working for me:
extension UIViewController {
func setupNavigationMultilineTitle() {
guard let navigationBar = self.navigationController?.navigationBar else { return }
for sview in navigationBar.subviews {
for ssview in sview.subviews {
guard let label = ssview as? UILabel else { break }
if label.text == self.title {
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.sizeToFit()
UIView.animate(withDuration: 0.3, animations: {
navigationBar.frame.size.height = 57 + label.frame.height
})
}
}
}
}
In the UIViewController:
override func viewDidLoad() {
super.viewDidLoad()
self.title = "This is a multiline title"
setupNavigationMultilineTitle()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
setupNavigationMultilineTitle()
}
And for setting font and color on the large title:
navigation.navigationBar.largeTitleTextAttributes = [NSAttributedStringKey.foregroundColor: .red, NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 30)]
Get a navigation item subviews and locate UILabel from it.
Try this and see:
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationController?.navigationItem.largeTitleDisplayMode = .automatic
self.title = "This is multiline title for navigation bar"
self.navigationController?.navigationBar.largeTitleTextAttributes = [
NSAttributedStringKey.foregroundColor: UIColor.black,
NSAttributedStringKey.font : UIFont.preferredFont(forTextStyle: .largeTitle)
]
for navItem in(self.navigationController?.navigationBar.subviews)! {
for itemSubView in navItem.subviews {
if let largeLabel = itemSubView as? UILabel {
largeLabel.text = self.title
largeLabel.numberOfLines = 0
largeLabel.lineBreakMode = .byWordWrapping
}
}
}
Here is result:
The linebreak solution seems to be problematic when there's a back button. So instead of breaking lines, I made the label auto adjust font.
func setupLargeTitleAutoAdjustFont() {
guard let navigationBar = navigationController?.navigationBar else {
return
}
// recursively find the label
func findLabel(in view: UIView) -> UILabel? {
if view.subviews.count > 0 {
for subview in view.subviews {
if let label = findLabel(in: subview) {
return label
}
}
}
return view as? UILabel
}
if let label = findLabel(in: navigationBar) {
if label.text == self.title {
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.7
}
}
}
Then it needs to be called in viewDidLayoutSubviews() to make sure the label can be found, and we only need to call it once:
private lazy var setupLargeTitleLabelOnce: Void = {[unowned self] in
if #available(iOS 11.0, *) {
self.setupLargeTitleAutoAdjustFont()
}
}()
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let _ = setupLargeTitleLabelOnce
}
If there's any navigationController pop event back to this controller, we need to call it again in viewDidAppear(). I haven't found a better solution for this - there's a small glitch of label font changing when coming back from a pop event:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if #available(iOS 11.0, *) {
setupLargeTitleAutoAdjustFont()
}
}
You could try:
Create a custom UINavigationController
Add the protocol UINavigationBarDelegate to the class definition
Override the function navigationBar(_:shouldPush:)
Activate two lines mode using hidden variable item.setValue(true, forKey: "__largeTitleTwoLineMode")
Make navigationController.navigationBar.prefersLargeTitles = true
(Edit 7/13: I notice that this solution is not support scrollView, so now I'm in research)
I found a perfect solution on Swift5
but sorry for my poor English because I'm Japanese🇯🇵Student.
In case of 2 lines In case of 3 lines
At first, set navigation settings for largeTitle normally in viewDidLoad
//Set largeTitle
navigationItem.largeTitleDisplayMode = .automatic
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationBar.largeTitleTextAttributes = [.font: UIFont.systemFont(ofSize: (fontSize + margin) * numberOfLines)]//ex) fontSize=26, margin=5, numberOfLines=2
//Set title
title = "multiple large\ntitle is working!"
It is most important point of this solution that font-size at largeTitleTextAttributes equals actual font-size(+margin) multiplied by number of lines.
Description image
Because, default specification of navigationBar attributes may be able to display only 1 line largeTitle.
Although, somehow, I did notice that in case of label-settings(the label which subview of subview of navigationBar) on direct, it can display any number of lines in 1 line of in case of navigationBar attributes.
So, we should do set big font in navigationbar attributes, and set small font in the label(subview of subview of navigationBar), and take into consideration the margins.
Do label settings direct in viewDidAppear like this:
//Find label
navigationController?.navigationBar.subviews.forEach({ subview in
subview.subviews.forEach { subsubview in
guard let label: UILabel = subsubview as? UILabel else { return }
//Label settings on direct.
label.text = title
label.font = UIFont.systemFont(ofSize: fontSize)
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.sizeToFit()
}
})
Therefore, in short, the solution at minimum code is given like this:
import UIKit
class ViewController: UIViewController {
private let fontSize: CGFloat = 26, margin: CGFloat = 5
private let numberOfLines: CGFloat = 2
override func viewDidLoad() {
super.viewDidLoad()
setUpNavigation()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
setMultipleLargeTitle()
}
private func setUpNavigation() {
//Set largeTitle
navigationItem.largeTitleDisplayMode = .automatic
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationBar.largeTitleTextAttributes = [.font: UIFont.systemFont(ofSize: (fontSize + margin) * numberOfLines)]
//Set title
title = "multiple large\ntitle is working!"
}
private func setMultipleLargeTitle() {
//Find label
navigationController?.navigationBar.subviews.forEach({ subview in
subview.subviews.forEach { subsubview in
guard let label: UILabel = subsubview as? UILabel else { return }
//Label settings on direct.
label.text = title
label.font = UIFont.systemFont(ofSize: fontSize)
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.sizeToFit()
}
})
}
}
thank you for reading :)
Swift 4 : Multi line even though the sentence is only short
title = "You're \nWelcome"
for navItem in(self.navigationController?.navigationBar.subviews)! {
for itemSubView in navItem.subviews {
if let largeLabel = itemSubView as? UILabel {
largeLabel.text = self.title
largeLabel.numberOfLines = 0
largeLabel.lineBreakMode = .byWordWrapping
}
}
}
If anyone looking for Title Lable Not Large Title, then below code is working.
Swift 5.X
func setMultilineNavigationBar(topText: String, bottomText : String) {
let topTxt = NSLocalizedString(topText, comment: "")
let bottomTxt = NSLocalizedString(bottomText, comment: "")
let titleParameters = [NSAttributedString.Key.foregroundColor : UIColor.white,
NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16, weight: .semibold)]
let subtitleParameters = [NSAttributedString.Key.foregroundColor : UIColor.white,
NSAttributedString.Key.font : UIFont.systemFont(ofSize: 13, weight: .regular)]
let title:NSMutableAttributedString = NSMutableAttributedString(string: topTxt, attributes: titleParameters)
let subtitle:NSAttributedString = NSAttributedString(string: bottomTxt, attributes: subtitleParameters)
title.append(NSAttributedString(string: "\n"))
title.append(subtitle)
let size = title.size()
let width = size.width
guard let height = navigationController?.navigationBar.frame.size.height else {return}
let titleLabel = UILabel(frame: CGRect.init(x: 0, y: 0, width: width, height: height))
titleLabel.attributedText = title
titleLabel.numberOfLines = 0
titleLabel.textAlignment = .center
self.navigationItem.titleView = titleLabel
}
SWIFT 5 This UIViewController extension helped me. Scenario that I have is mixed with enabling and disabling large titles so FIRST ENABLE large title and then call this method. Call it in viewDidLoad, I have found bug with peeking back with swipe and then releasing touch, for some reason current navigation title become previous navigation title
extension UIViewController {
/// Sets two lines for navigation title if needed
/// - Parameter animated: used for changing titles on one controller,in that case animation is off
func multilineNavTitle(_ animated:Bool = true) {
if animated {
// setting initial state for animation of title to look more native
self.navigationController?.navigationBar.transform = CGAffineTransform.init(translationX: .screenWidth/2, y: 0)
self.navigationController?.navigationBar.alpha = 0
}
//Checks if two lines is needed
if self.navigationItem.title?.forTwoLines() ?? false {
// enabling multiline
navigationItem.setValue(true,
forKey: "__largeTitleTwoLineMode")
} else {
// disabling multiline
navigationItem.setValue(false,
forKey: "__largeTitleTwoLineMode")
}
// laying out title without animation
UIView.performWithoutAnimation {
self.navigationController?.navigationBar.layoutSubviews()
self.navigationController?.view.setNeedsLayout()
self.navigationController?.view.layoutIfNeeded()
}
if animated {
//animating title
UIView.animate(withDuration: 0.3) {
self.navigationController?.navigationBar.transform = CGAffineTransform.identity
self.navigationController?.navigationBar.alpha = 1
}
}
}
}
fileprivate extension String {
/// Checks if navigation title is wider than label frame
/// - Returns: `TRUE` if title cannot fit in one line of navigation title label
func forTwoLines() -> Bool {
let fontAttributes = [NSAttributedString.Key.font: SomeFont]
let size = self.size(withAttributes: fontAttributes)
return size.width > CGFloat.screenWidth - 40 //in my case
}
}
Just create a custom navigation controller. Rest will be handled by the OS itself
class MyNavigationViewController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
navigationBar.delegate = self
}
}
extension MyNavigationViewController: UINavigationBarDelegate {
func navigationBar(_ navigationBar: UINavigationBar, shouldPush item: UINavigationItem) -> Bool {
item.setValuesForKeys([
"__largeTitleTwoLineMode": true
])
return true
}
}
viewController.navigationItem
.setValuesForKeys(["__largeTitleTwoLineMode": true])
WARNING: This method does not work on older OS versions

NavigationItem titleView is coming at left side for some time then move to Center

I am setting a custom view as titleView of the navigation. When viewcontroller appear its title view comes at left side for a moment and then move to center,what could be wrong? I am using the following code
let itemImgs: [UIImage] = [UIImage(named: "MORE_Location")!, UIImage(named: "MORE_Department")!, UIImage(named: "By_Teams")!, UIImage(named: "MORE_Status")!]
self.navigationController?.navigationBar.isTranslucent = false
self.navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
menuView = BTNavigationDropdownMenu(navigationController: self.navigationController, containerView: self.navigationController!.view, title: AppMessage.EDEmployeePeople, items: items as [AnyObject], itemImgs: itemImgs)
menuView.cellHeight = 60
menuView.cellBackgroundColor = UIColor.red
menuView.cellSelectionColor = UIColor.clear
menuView.cellSeparatorColor = UIColor.clear
menuView.shouldKeepSelectedCellColor = false
menuView.cellTextLabelColor = UIColor.white
menuView.shouldChangeTitleText = false
menuView.cellTextLabelFont = UIFont(name: "Helvetica", size: 17)
if appNeedsAutoResize
{
menuView.cellTextLabelFont = UIUtils.getFontForApproprieteField(.subHeadline).font
}
menuView.cellTextLabelAlignment = .left // .Center // .Right // .Left
menuView.arrowPadding = 15
menuView.animationDuration = 0.5
menuView.maskBackgroundColor = UIColor.clear
menuView.maskBackgroundOpacity = 0.3
menuView.didSelectItemAtIndexHandler = {(indexPath: Int) -> () in
print("Did select item at index: \(indexPath)")
if indexPath == 3
{
let byStatusViewController = ByStatusViewController(nibName: "ByStatusViewController", bundle: nil)
//UIUtils.pushViewWhenHideBottom(self, anotherVC: byStatusViewController)
self.navigationController?.pushViewController(byStatusViewController, animated: true)
}
else
{
let dropVC = DepartmentViewController(nibName: "DepartmentViewController", bundle: nil)
switch indexPath
{
case 0:
dropVC.employeeGroupInfo = EmployeeGroupInfo.locationInfo
break
case 1:
dropVC.employeeGroupInfo = EmployeeGroupInfo.departmentInfo
break
default:
dropVC.employeeGroupInfo = EmployeeGroupInfo.teamInfo
break
}
// UIUtils.pushViewWhenHideBottom(self, anotherVC: dropVC)
self.navigationController?.pushViewController(dropVC, animated: true)
}
}
self.navigationItem.titleView = menuView
}
Try to add constraints of autoresizing masks to your menu view to keep the view centered.
E.g.
menuView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin, .flexibleBottomMargin]
I had this problem and changing my function that customize my navigationItem from viewWillAppear() to viewDidLoad() resolved

pushViewController not working properly

I've got a SidebarMenueController which is my entry point after starting the app.
Within there i've got a NavigationController embedded with a sidebar (slide out) menue. With sideBarDidSelectButtonAtIndex i catch which view, which should be loaded inside the NavigationController. When selecting an entry in the sidebar menue i want to push to another view.
Via printing (into the console) the selected menue entry, i can confirm that the selection is definitely working. But the pushViewController function is not loading the desired view.
I'm using no storyboard!
Any suggestions on how to fix this?
import UIKit
class SideBarMenueController: UIViewController, SideBarDelegate {
var sideBar:SideBar = SideBar()
lazy var menueButton: UIButton = {
let button = UIButton()
button.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
button.backgroundColor = UIColor.clear
button.setImage(#imageLiteral(resourceName: "MenueIcon"), for: UIControlState.normal)
button.addTarget(self, action: #selector(didSelectMenueButton), for: UIControlEvents.touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
let layout = UICollectionViewFlowLayout()
let sideBarMenue = UINavigationController(rootViewController:FirstViewController(collectionViewLayout: layout))
self.addChildViewController(sideBarMenue)
sideBarMenue.view.frame = self.view.bounds
self.view.addSubview(sideBarMenue.view)
UINavigationBar.appearance().barTintColor = UIColor(red: 70/255, green: 174/255, blue: 253/255, alpha: 1)
let titleAttributes = [
NSFontAttributeName: UIFont.systemFont(ofSize: 20, weight: UIFontWeightBold),
NSForegroundColorAttributeName: UIColor.white
]
UINavigationBar.appearance().titleTextAttributes = titleAttributes
UINavigationBar.appearance().tintColor = UIColor.white
let NavigationMenueButton = UIBarButtonItem(customView: menueButton)
navigationItem.leftBarButtonItem = NavigationMenueButton
sideBar = SideBar(sourceView: self.view, menuItems: ["feed now", "feed times", "cats", "device status", "device setup", "about app"])
sideBar.delegate = self
}
func didSelectMenueButton() {
let layout = UICollectionViewFlowLayout()
let controller = FeedTimesController(collectionViewLayout: layout)
navigationController?.pushViewController(controller, animated: true)
}
func sideBarDidSelectButtonAtIndex(_ index: Int) {
if index == 0{
let controller = FirstViewController()
navigationController?.pushViewController(controller, animated: true)
print("touched index 0")
} else if index == 1{
let layout = UICollectionViewFlowLayout()
let controller = SecondViewController(collectionViewLayout: layout)
navigationController?.pushViewController(controller, animated: true)
print("touched index 1")
} else if index == 2{
let layout = UICollectionViewFlowLayout()
let controller = ThirdViewController(collectionViewLayout: layout)
navigationController?.pushViewController(controller, animated: true)
print("touched index 2")
} else if index == 3{
print("touched index 3")
} else if index == 4{
print("touched index 4")
} else if index == 5{
print("touched index 5")
}
sideBar.showSideBar(false)
}
}
It looks like you are creating a NavigationController with:
let sideBarMenue = UINavigationController(rootViewController:FirstViewController(collectionViewLayout: layout))
Then you are adding that controller's view as a subview to self.view:
self.view.addSubview(sideBarMenue.view)
But then later, in didSelect, you are trying to access (implied) self.navigationController:
navigationController?.pushViewController(controller, animated: true)
Not sure, but it looks like you want to access the nav controller via:
sideBar.navigationController
Without seeing the code / library you're using for SideBar, this may or may not resolve your issue.

Resources