Skip to content

Instantly share code, notes, and snippets.

@glennposadas
Created November 11, 2018 18:14
Show Gist options
  • Save glennposadas/43b609ecf8d6c088137da47bccd8b761 to your computer and use it in GitHub Desktop.
Save glennposadas/43b609ecf8d6c088137da47bccd8b761 to your computer and use it in GitHub Desktop.
//
// ViewController.swift
// TestNavigation
//
// Created by Glenn Von C. Posadas on 11/11/2018.
// Copyright © 2018 Glenn Von C. Posadas. All rights reserved.
//
import UIKit
class BaseViewController: UIViewController {
private var hasCustomBackButton: Bool = false
/// Edged chevron for iOS 10.
/// Curved chevron for iOS 11 and above.
private lazy var button_Back: UIButton = {
let button = UIButton(type: .custom)
let image = ProcessInfo.processInfo.operatingSystemVersion.majorVersion < 11 ?
UIImage(named: "ic_chevron_back_edged") : UIImage(named: "ic_chevron_back")
var imageInsets: UIEdgeInsets!
let iOSVersion = ProcessInfo.processInfo.operatingSystemVersion.majorVersion
switch iOSVersion {
case 11:
imageInsets = UIEdgeInsets(top: 0, left: -8, bottom: 0, right: 0)
default:
imageInsets = UIEdgeInsets(top: 0, left: -16, bottom: 0, right: 0)
}
button.setImage(image, for: .normal)
button.imageEdgeInsets = imageInsets
button.addTarget(self, action: #selector(self.popVC), for: .touchUpInside)
return button
}()
func addBackButton() {
if self.navigationController?.viewControllers.first == self {
self.hasCustomBackButton = false
return
}
self.hasCustomBackButton = true
let backBarButton = UIBarButtonItem(customView: self.button_Back)
self.button_Back.frame = CGRect(x: 0, y: 0, width: 44.0, height: 44.0)
self.navigationItem.leftBarButtonItem = backBarButton
self.navigationController?.interactivePopGestureRecognizer?.delegate = self
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if self.hasCustomBackButton {
self.navigationController?.fixNavigationItemsMargin(0)
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if self.hasCustomBackButton {
self.navigationController?.fixNavigationItemsMargin(0)
}
}
@objc private func popVC() {
if let navCon = self.navigationController {
navCon.popViewController(animated: true)
}
}
}
extension BaseViewController: UIGestureRecognizerDelegate {
}
////=-=-=-=-=-=-=-=
class ViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .white
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3)) {
self.navigationItem.title = ""
self.navigationController?.pushViewController(VC2(), animated: true)
}
}
}
class VC3: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .green
self.addBackButton()
}
}
class VC2: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .lightGray
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3)) {
self.navigationItem.title = ""
self.navigationController?.pushViewController(VC3(), animated: true)
}
}
// override func viewDidLayoutSubviews() {
// super.viewDidLayoutSubviews()
// navigationController?.fixNavigationItemsMargin(40)
// }
// override func viewDidAppear(_ animated: Bool) {
// super.viewDidAppear(animated)
// navigationController?.fixNavigationItemsMargin(40)
// }
}
// do common initilizer
class BaseNavigationController: UINavigationController {
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
fixNavigationItemsMargin()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
fixNavigationItemsMargin()
}
}
extension UINavigationController {
func fixNavigationItemsMargin(_ margin: CGFloat = 0) {
let systemMajorVersion = ProcessInfo.processInfo.operatingSystemVersion.majorVersion
if systemMajorVersion >= 11 {
// iOS >= 11
guard let contentView = navigationBar.subviews
.first(
where: { sub in
String(describing: sub).contains("ContentView")
}) else { return }
// refer to: https://www.matrixprojects.net/p/uibarbuttonitem-ios11/
// if rightBarButtonItems has not any custom views, then margin would be 8(320|375)/12(414)
// should use customView
let needAdjustRightItems: Bool
if let currentVC = viewControllers.last,
let rightItems = currentVC.navigationItem.rightBarButtonItems,
rightItems.count > 0,
rightItems.filter({ $0.customView != nil }).count > 0 {
needAdjustRightItems = true
} else {
print("Use 8(320|375)/12(414), if need precious margin ,use UIBarButtonItem(customView:)!!!")
needAdjustRightItems = false
}
let needAdjustLeftItems: Bool
if let currentVC = viewControllers.last,
let leftItems = currentVC.navigationItem.leftBarButtonItems,
leftItems.count > 0,
leftItems.filter({ $0.customView != nil }).count > 0 {
needAdjustLeftItems = true
} else {
print("Use 8(320|375)/12(414), if need precious margin ,use UIBarButtonItem(customView:)!!!")
needAdjustLeftItems = false
}
let layoutMargins: UIEdgeInsets
if #available(iOS 11.0, *) {
let directionInsets = contentView.directionalLayoutMargins
layoutMargins = UIEdgeInsets(
top: directionInsets.top,
left: directionInsets.leading,
bottom: directionInsets.bottom,
right: directionInsets.trailing)
} else {
layoutMargins = contentView.layoutMargins
}
contentView.constraints.forEach(
{ cst in
// iOS 11 the distance between rightest item and NavigationBar should be margin
// rightStackView trailing space is -margin / 2
// rightestItem trailing to rightStackView trailing is -margin / 2
let rightConstant = -margin / 2
switch (cst.firstAttribute, cst.secondAttribute) {
case (.leading, .leading), (.trailing, .trailing):
if let stackView = cst.firstItem as? UIStackView,
stackView.frame.minX < navigationBar.frame.midX {
// is leftItems
if needAdjustLeftItems {
cst.constant = margin - layoutMargins.left
}
} else if let layoutGuide = cst.firstItem as? UILayoutGuide,
layoutGuide.layoutFrame.minX < navigationBar.frame.midX {
// is leftItems
if needAdjustLeftItems {
cst.constant = margin - layoutMargins.left
}
}
if let stackView = cst.firstItem as? UIStackView,
stackView.frame.maxX > navigationBar.frame.midX {
// is rightItems
if needAdjustRightItems {
cst.constant = rightConstant
}
} else if let layoutGuide = cst.firstItem as? UILayoutGuide,
layoutGuide.layoutFrame.maxX > navigationBar.frame.midX {
// is rightItems
if needAdjustRightItems {
cst.constant = rightConstant
}
}
default: break
}
})
// ensure items space == 8, minispcae
contentView.subviews.forEach(
{ subsub in
guard subsub is UIStackView else { return }
subsub.constraints.forEach(
{ cst in
guard cst.firstAttribute == .width
|| cst.secondAttribute == .width
else { return }
cst.constant = 0
})
})
} else {
// iOS < 11
let versionItemsCount: Int
if systemMajorVersion == 10 {
// iOS 10 navigationItem.rightBarButtonItems == 0
// space = 16(320|375) / 20(414)
// should adjust margin
versionItemsCount = 0
} else {
// iOS 9 navigationItem.rightBarButtonItems == 0
// space = 8(320|375) / 12(414)
// should not adjust margin
versionItemsCount = 1
}
let spaceProducer = { () -> UIBarButtonItem in
let spaceItem = UIBarButtonItem(
barButtonSystemItem: .fixedSpace,
target: nil,
action: nil)
spaceItem.width = margin - 16
return spaceItem
}
if let currentVC = viewControllers.last,
var rightItems = currentVC.navigationItem.rightBarButtonItems,
rightItems.count > versionItemsCount,
let first = rightItems.first {
// ensure the first BarButtonItem is NOT fixedSpace
if first.title == nil && first.image == nil && first.customView == nil {
print("rightBarButtonItems SPACE SETTED!!! SPACE: ", abs(first.width))
} else {
rightItems.insert(spaceProducer(), at: 0)
// arranged right -> left
currentVC.navigationItem.rightBarButtonItems = rightItems
}
}
if let currentVC = viewControllers.last,
var leftItems = currentVC.navigationItem.leftBarButtonItems,
leftItems.count > versionItemsCount,
let first = leftItems.first {
if first.title == nil && first.image == nil && first.customView == nil {
print("leftBarButtonItems SPACE SETTED!!! SPACE: ", abs(first.width))
} else {
leftItems.insert(spaceProducer(), at: 0)
// arranged left -> right
currentVC.navigationItem.leftBarButtonItems = leftItems
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment