Commit 15d33bde by Daniel Dahan

updated NavigationController and NavigationBar to utilize the Toolbar and remove duplicate code

parent 68e44743
......@@ -108,7 +108,7 @@ open class Bar: View {
open let contentView = UIView()
/// Left side UIViews.
open var leftViews: [UIView] {
open var leftViews = [UIView]() {
didSet {
for v in oldValue {
v.removeFromSuperview()
......@@ -118,7 +118,7 @@ open class Bar: View {
}
/// Right side UIViews.
open var rightViews: [UIView] {
open var rightViews = [UIView]() {
didSet {
for v in oldValue {
v.removeFromSuperview()
......@@ -142,8 +142,6 @@ open class Bar: View {
- Parameter aDecoder: A NSCoder instance.
*/
public required init?(coder aDecoder: NSCoder) {
leftViews = []
rightViews = []
super.init(coder: aDecoder)
}
......@@ -154,8 +152,6 @@ open class Bar: View {
- Parameter frame: A CGRect instance.
*/
public override init(frame: CGRect) {
leftViews = []
rightViews = []
super.init(frame: frame)
}
......
......@@ -39,45 +39,36 @@ open class NavigationBar: UINavigationBar {
open override var intrinsicContentSize: CGSize {
return CGSize(width: bounds.width, height: bounds.height)
}
/// A preset wrapper around contentEdgeInsets.
open var contentEdgeInsetsPreset = EdgeInsetsPreset.none {
didSet {
/// A preset wrapper around contentEdgeInsets.
open var contentEdgeInsetsPreset = EdgeInsetsPreset.none {
didSet {
contentEdgeInsets = EdgeInsetsPresetToValue(preset: contentEdgeInsetsPreset)
}
}
/// A reference to EdgeInsets.
@IBInspectable
}
}
/// A reference to EdgeInsets.
@IBInspectable
open var contentEdgeInsets = EdgeInsets.zero {
didSet {
layoutSubviews()
}
}
/// A preset wrapper around interimSpace.
open var interimSpacePreset = InterimSpacePreset.none {
didSet {
didSet {
layoutSubviews()
}
}
/// A preset wrapper around interimSpace.
open var interimSpacePreset = InterimSpacePreset.interimSpace3 {
didSet {
interimSpace = InterimSpacePresetToValue(preset: interimSpacePreset)
}
}
/// A wrapper around grid.interimSpace.
@IBInspectable
}
}
/// A wrapper around grid.interimSpace.
@IBInspectable
open var interimSpace: InterimSpace = 0 {
didSet {
layoutSubviews()
}
}
/// Grid cell factor.
@IBInspectable
open var gridFactor: CGFloat = 12 {
didSet {
assert(0 < gridFactor, "[Material Error: gridFactor must be greater than 0.]")
layoutSubviews()
}
}
didSet {
layoutSubviews()
}
}
/**
The back button image writes to the backIndicatorImage property and
......@@ -151,136 +142,6 @@ open class NavigationBar: UINavigationBar {
layoutDivider()
}
open override func pushItem(_ item: UINavigationItem, animated: Bool) {
super.pushItem(item, animated: animated)
layoutNavigationItem(item: item)
}
/**
Lays out the UINavigationItem.
- Parameter item: A UINavigationItem to layout.
*/
internal func layoutNavigationItem(item: UINavigationItem) {
guard willLayout else {
return
}
prepareItem(item: item)
prepareTitleView(item: item)
item.titleView!.frame.origin = .zero
item.titleView!.frame.size = intrinsicContentSize
var lc = 0
var rc = 0
item.titleView!.grid.begin()
item.titleView!.grid.views.removeAll()
for v in item.leftViews {
if let b = v as? UIButton {
b.contentEdgeInsets = .zero
b.titleEdgeInsets = .zero
}
v.frame.size.width = v.intrinsicContentSize.width
v.sizeToFit()
v.grid.columns = Int(ceil(v.bounds.width / gridFactor)) + 2
lc += v.grid.columns
item.titleView!.grid.views.append(v)
}
item.titleView!.grid.views.append(item.contentView)
for v in item.rightViews {
if let b = v as? UIButton {
b.contentEdgeInsets = .zero
b.titleEdgeInsets = .zero
}
v.frame.size.width = v.intrinsicContentSize.width
v.sizeToFit()
v.grid.columns = Int(ceil(v.bounds.width / gridFactor)) + 2
rc += v.grid.columns
item.titleView!.grid.views.append(v)
}
item.contentView.grid.begin()
item.contentView.grid.offset.columns = 0
var l: CGFloat = 0
var r: CGFloat = 0
if .center == item.contentViewAlignment {
if item.leftViews.count < item.rightViews.count {
r = CGFloat(item.rightViews.count) * interimSpace
l = r
} else {
l = CGFloat(item.leftViews.count) * interimSpace
r = l
}
}
let p = bounds.width - l - r - contentEdgeInsets.left - contentEdgeInsets.right
let columns = Int(ceil(p / gridFactor))
if .center == item.contentViewAlignment {
if lc < rc {
item.contentView.grid.columns = columns - 2 * rc
item.contentView.grid.offset.columns = rc - lc
} else {
item.contentView.grid.columns = columns - 2 * lc
item.rightViews.first?.grid.offset.columns = lc - rc
}
} else {
item.contentView.grid.columns = columns - lc - rc
}
item.titleView!.grid.axis.columns = columns
item.titleView!.grid.interimSpace = interimSpace
item.titleView!.grid.contentEdgeInsets = contentEdgeInsets
item.titleView!.grid.commit()
item.contentView.grid.commit()
// contentView alignment.
if nil != item.title && "" != item.title {
if nil == item.titleLabel.superview {
item.contentView.addSubview(item.titleLabel)
}
item.titleLabel.frame = item.contentView.bounds
} else {
item.titleLabel.removeFromSuperview()
}
if 0 < item.detailLabel.text?.utf16.count ?? 0 {
if nil == item.detailLabel.superview {
item.contentView.addSubview(item.detailLabel)
}
if nil == item.titleLabel.superview {
item.detailLabel.frame = item.contentView.bounds
} else {
item.titleLabel.sizeToFit()
item.detailLabel.sizeToFit()
let diff = (item.contentView.bounds.height - item.titleLabel.bounds.height - item.detailLabel.bounds.height) / 2
item.titleLabel.frame.size.height += diff
item.titleLabel.frame.size.width = item.contentView.bounds.width
item.detailLabel.frame.size.height += diff
item.detailLabel.frame.size.width = item.contentView.bounds.width
item.detailLabel.frame.origin.y = item.titleLabel.bounds.height
}
} else {
item.detailLabel.removeFromSuperview()
}
}
/**
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepare method
......@@ -292,34 +153,37 @@ open class NavigationBar: UINavigationBar {
barStyle = .black
isTranslucent = false
depthPreset = .depth1
interimSpacePreset = .interimSpace3
contentEdgeInsetsPreset = .square1
contentScaleFactor = Screen.scale
backButtonImage = Icon.cm.arrowBack
contentEdgeInsetsPreset = .square1
interimSpacePreset = .interimSpace3
let image = UIImage()
shadowImage = image
setBackgroundImage(image, for: .default)
backgroundColor = .white
}
/**
Prepare the item by setting the title property to equal an empty string.
- Parameter item: A UINavigationItem to layout.
*/
private func prepareItem(item: UINavigationItem) {
item.hidesBackButton = false
item.setHidesBackButton(true, animated: false)
}
/**
Prepare the titleView.
}
internal extension NavigationBar {
/**
Lays out the UINavigationItem.
- Parameter item: A UINavigationItem to layout.
*/
private func prepareTitleView(item: UINavigationItem) {
guard nil == item.titleView else {
func layoutNavigationItem(item: UINavigationItem) {
guard willLayout else {
return
}
item.titleView = UIView(frame: .zero)
}
item.titleView = item.toolbar
guard let v = item.titleView as? Toolbar else {
return
}
removeConstraints(constraints)
v.contentEdgeInsets = contentEdgeInsets
v.interimSpace = interimSpace
v.frame = bounds
}
}
......@@ -119,18 +119,18 @@ open class NavigationController: UINavigationController {
when subclassing.
*/
open func prepare() {
isMotionEnabled = true
navigationBar.heightPreset = .normal
navigationBar.frame.size.width = view.bounds.width
view.clipsToBounds = true
view.backgroundColor = .white
view.contentScaleFactor = Screen.scale
// This ensures the panning gesture is available when going back between views.
if let v = interactivePopGestureRecognizer {
v.isEnabled = true
v.delegate = self
}
if let v = interactivePopGestureRecognizer {
v.isEnabled = true
v.delegate = self
}
}
/// Calls the layout functions for the view heirarchy.
......@@ -156,20 +156,24 @@ extension NavigationController: UINavigationBarDelegate {
item.backButton.image = v.backButtonImage
}
item.backButton.addTarget(self, action: #selector(handleBackButton), for: .touchUpInside)
item.backButton.addTarget(self, action: #selector(handle(backButton:)), for: .touchUpInside)
if !item.backButton.isHidden {
item.leftViews.insert(item.backButton, at: 0)
}
item.hidesBackButton = false
item.setHidesBackButton(true, animated: false)
v.layoutNavigationItem(item: item)
}
return true
}
/// Handler for the back button.
/// Handler for the backbutton.
@objc
internal func handleBackButton() {
internal func handle(backButton: UIButton) {
popViewController(animated: true)
}
}
......
......@@ -35,59 +35,16 @@ fileprivate var NavigationItemKey: UInt8 = 0
fileprivate var NavigationItemContext: UInt8 = 0
fileprivate class NavigationItem: NSObject {
/// Should center the contentView.
var contentViewAlignment = ContentViewAlignment.center {
didSet {
navigationBar?.layoutSubviews()
}
}
/// A reference to the toolbar.
@objc
let toolbar = Toolbar()
/// Back Button.
lazy var backButton = IconButton()
/// Content View.
var contentView = UIView()
/// Title label.
@objc
var titleLabel = UILabel()
/// Detail label.
var detailLabel = UILabel()
/// Left items.
var leftViews = [UIView]() {
didSet {
for v in oldValue {
v.removeFromSuperview()
}
navigationBar?.layoutSubviews()
}
}
/// Right items.
var rightViews = [UIView]() {
didSet {
for v in oldValue {
v.removeFromSuperview()
}
navigationBar?.layoutSubviews()
}
}
/// Center items.
var centerViews: [UIView] {
get {
return contentView.grid.views
}
set(value) {
contentView.grid.views = value
}
}
/// An optional reference to the NavigationBar.
var navigationBar: NavigationBar? {
var v = contentView.superview
var v = toolbar.contentView.superview
while nil != v {
if let navigationBar = v as? NavigationBar {
return navigationBar
......@@ -96,76 +53,43 @@ fileprivate class NavigationItem: NSObject {
}
return nil
}
open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard "titleLabel.textAlignment" == keyPath else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
fileprivate extension UINavigationItem {
/// NavigationItem reference.
var navigationItem: NavigationItem {
get {
return AssociatedObject.get(base: self, key: &NavigationItemKey) {
return NavigationItem()
}
}
set(value) {
AssociatedObject.set(base: self, key: &NavigationItemKey, value: value)
}
contentViewAlignment = .center == titleLabel.textAlignment ? .center : .full
}
deinit {
removeObserver(self, forKeyPath: #keyPath(titleLabel.textAlignment))
}
/// Initializer.
override init() {
super.init()
prepareTitleLabel()
prepareDetailLabel()
}
/// Reloads the subviews for the NavigationBar.
func reload() {
navigationBar?.layoutSubviews()
}
internal extension UINavigationItem {
/// A reference to the NavigationItem Toolbar.
var toolbar: Toolbar {
return navigationItem.toolbar
}
/// Prepares the titleLabel.
func prepareTitleLabel() {
titleLabel.textAlignment = .center
titleLabel.contentScaleFactor = Screen.scale
titleLabel.font = RobotoFont.medium(with: 17)
titleLabel.textColor = Color.darkText.primary
addObserver(self, forKeyPath: #keyPath(titleLabel.textAlignment), options: [], context: &NavigationItemContext)
}
/// Prepares the detailLabel.
func prepareDetailLabel() {
detailLabel.textAlignment = .center
titleLabel.contentScaleFactor = Screen.scale
detailLabel.font = RobotoFont.regular(with: 12)
detailLabel.textColor = Color.darkText.secondary
}
}
extension UINavigationItem {
/// NavigationItem reference.
fileprivate var navigationItem: NavigationItem {
get {
return AssociatedObject.get(base: self, key: &NavigationItemKey) {
return NavigationItem()
}
}
set(value) {
AssociatedObject.set(base: self, key: &NavigationItemKey, value: value)
}
}
/// Should center the contentView.
/// Should center the contentView.
open var contentViewAlignment: ContentViewAlignment {
get {
return navigationItem.contentViewAlignment
return toolbar.contentViewAlignment
}
set(value) {
navigationItem.contentViewAlignment = value
toolbar.contentViewAlignment = value
}
}
/// Content View.
open var contentView: UIView {
return navigationItem.contentView
return toolbar.contentView
}
/// Back Button.
......@@ -175,41 +99,41 @@ extension UINavigationItem {
/// Title Label.
open var titleLabel: UILabel {
return navigationItem.titleLabel
return toolbar.titleLabel
}
/// Detail Label.
open var detailLabel: UILabel {
return navigationItem.detailLabel
return toolbar.detailLabel
}
/// Left side UIViews.
open var leftViews: [UIView] {
get {
return navigationItem.leftViews
return toolbar.leftViews
}
set(value) {
navigationItem.leftViews = value
toolbar.leftViews = value
}
}
/// Right side UIViews.
open var rightViews: [UIView] {
get {
return navigationItem.rightViews
return toolbar.rightViews
}
set(value) {
navigationItem.rightViews = value
toolbar.rightViews = value
}
}
/// Center UIViews.
open var centerViews: [UIView] {
get {
return navigationItem.centerViews
return toolbar.centerViews
}
set(value) {
navigationItem.centerViews = value
toolbar.centerViews = value
}
}
}
......@@ -68,6 +68,7 @@ open class Toolbar: Bar {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
contentViewAlignment = .center == titleLabel.textAlignment ? .center : .full
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment