// // MCNavBarView.swift // HCQuanfangtong // // Created by Apple on 2022/8/1. // Copyright © 2022 Jyp. All rights reserved. // import Foundation import UIKit import RxSwift import RxCocoa enum MCNavBarType { case defaults case search case segment case custom } class MCNavBarView: UIView{ init(title: String?, subTitle: String?, style: MCNavBarStyle? = nil){ self.navStyle = style ?? MCNavBarStyle.init() super.init(frame: CGRect.zero) self.backgroundColor = self.navStyle.styleForBackgroundColor self.addSubview(self.contentView) self.addSubview(self.leftStackView) self.addSubview(self.rightStackView) if title.isNotNil{ self.titleLabel.text = title self.contentView.addArrangedSubview(self.titleLabel) } if subTitle.isNotNil{ self.subTitleLabel.text = subTitle self.contentView.addArrangedSubview(self.subTitleLabel) } let backBtn = MCNavBtn.init(type: .custom) backBtn.image = self.navStyle.styleForBackImage backBtn.setImage(self.navStyle.styleForBackImage, for: .normal) _ = backBtn.rx.tap.takeUntil(self.rx.deallocated).subscribe(onNext:{[unowned self] in self.navBarBackBlock?() }) self.leftBtns.append(backBtn) self.leftStackView.addArrangedSubview(backBtn) self.resetConstraint() } //MARK: - Property MCNavBarDelegate var showNavBackBtn: Bool = true{ willSet{ if newValue == self.showNavBackBtn{ return } self.showNavBackBtn = newValue self.leftBtns[0].isHidden = !self.showNavBackBtn self.resetConstraint() } } /// 返回事件 var navBarBackBlock: (() -> Void)? /// 高度监听 var navBarHeightSubject: BehaviorSubject = BehaviorSubject.init(value: 0) /// 样式 var navStyle: MCNavBarStyle /// 透明样式 var navTransparentStyle: MCNavBarTransparentColorStyle?{ didSet{ if self.leftBtns.count > 0{ self.leftBtns[0].transparentImage = self.navTransparentStyle?.styleForBackImage } self.resetStyle() } } weak var navTransparentBinderedScrollView: UIScrollView?{ didSet{ if let scrollView = self.navTransparentBinderedScrollView { _ = scrollView.rx.contentOffset.takeUntil(scrollView.rx.deallocated).subscribe(onNext:{[unowned self] point in guard let transparentStyle = self.navTransparentStyle else { return } if point.y > transparentStyle.styleForScrollDistanceChange && self.navIsTransparent{ self.resetStyle() } else if point.y <= transparentStyle.styleForScrollDistanceChange && !self.navIsTransparent{ self.resetStyle() } }) } } } //MARK: - 类型 var type: MCNavBarType{ .defaults } //MARK: - Private private var leftBtns: [MCNavBtn] = [] lazy private var leftStackView: UIStackView = {[unowned self] in let stackView = UIStackView.init(frame: CGRect.zero) stackView.axis = .horizontal stackView.alignment = .fill stackView.distribution = .fillProportionally stackView.spacing = self.navStyle.styleForElementSpace return stackView }() private var rightBtns: [MCNavBtn] = [] lazy private var rightStackView: UIStackView = {[unowned self] in let stackView = UIStackView.init(frame: CGRect.zero) stackView.axis = .horizontal stackView.alignment = .fill stackView.distribution = .fillProportionally stackView.spacing = self.navStyle.styleForElementSpace return stackView }() /// 标题,副标题 lazy var contentView: UIStackView = {[unowned self] in let stackView = UIStackView.init(frame: CGRect.zero) stackView.axis = .vertical stackView.alignment = .fill stackView.distribution = .fill stackView.spacing = self.navStyle.styleForTitleAndSubTitleDistance return stackView }() lazy private var titleLabel: UILabel = {[unowned self] in let label = UILabel.init(frame: CGRect.zero) label.font = self.navStyle.styleForTitleFont label.textColor = self.navStyle.styleForTitleColor label.textAlignment = .center return label }() lazy private var subTitleLabel: UILabel = {[unowned self] in let label = UILabel.init(frame: CGRect.zero) label.font = self.navStyle.styleForSubTitleFont label.textColor = self.navStyle.styleForSubTitleColor label.textAlignment = .center return label }() /// 当前是否为透明 private var navIsTransparent: Bool = false //MARK: - required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } extension MCNavBarView{ func resetConstraint(){ //左边 //计算返回按钮 if self.type == .defaults{ self.contentView.axis = .vertical } else{ self.contentView.axis = .horizontal } var leftBtnWidth: CGFloat = 0 var rightBtnWidth: CGFloat = 0 for i in 0.. 0 let rightBtnContains = self.rightBtns.compactMap { btn in !btn.isHidden }.count > 0 let leftWidth = (self.showNavBackBtn ? leftBtnWidth:(leftBtnWidth + self.navStyle.styleForContentEdge.left)) + (leftBtnContains ? (self.navStyle.styleForElementSpace - self.leftStackView.spacing):0) let rightWidth = rightBtnWidth + self.navStyle.styleForContentEdge.right + (rightBtnContains ? (self.navStyle.styleForElementSpace + self.rightStackView.spacing):0) if self.navStyle.styleForContentViewWidth > -1{ //定了宽度 //计算当前剩余的宽度是否可以放下content var remainingWidth = kSCREEN_WIDTH - leftWidth - rightWidth if remainingWidth < self.navStyle.styleForContentViewWidth{ //放不下 self.contentView.mas_remakeConstraints { make in make?.left.equalTo()(leftBtnContains ? self.leftStackView.mas_right:self)?.offset()(leftBtnContains ? self.navStyle.styleForElementSpace:self.navStyle.styleForContentEdge.left) make?.right.equalTo()(rightBtnContains ? self.rightStackView.mas_left:self)?.offset()(rightBtnContains ? -self.navStyle.styleForElementSpace:-self.navStyle.styleForContentEdge.right) make?.centerY.mas_equalTo()(self.leftStackView) } } else{ //放得下 //计算居中可以放下不 let maxWidth = max(leftWidth, rightWidth) remainingWidth = kSCREEN_WIDTH - maxWidth * 2 if remainingWidth < self.navStyle.styleForContentViewWidth{ //居中放不下 //在剩下的区域居中 let distance = (remainingWidth - self.navStyle.styleForContentViewWidth) * 0.5 self.contentView.mas_remakeConstraints { make in make?.left.mas_equalTo()(self)?.offset()(leftWidth + distance) make?.right.mas_equalTo()(self)?.offset()(-(rightWidth + distance)) make?.centerY.mas_equalTo()(self.leftStackView) } } else{ //居中 self.contentView.mas_remakeConstraints { make in make?.width.offset()(self.navStyle.styleForContentViewWidth) make?.centerY.mas_equalTo()(self.leftStackView) make?.centerX.mas_equalTo()(self) } } } } else{ self.contentView.mas_remakeConstraints { make in if self.navStyle.styleForContentViewIsMiddle{ if leftWidth > rightWidth{ make?.left.equalTo()(leftBtnContains ? self.leftStackView.mas_right:self)?.offset()(leftBtnContains ? self.navStyle.styleForElementSpace:self.navStyle.styleForContentEdge.left) } else{ make?.right.equalTo()(rightBtnContains ? self.rightStackView.mas_left:self)?.offset()(rightBtnContains ? -self.navStyle.styleForElementSpace:-self.navStyle.styleForContentEdge.right) } make?.centerY.mas_equalTo()(self.leftStackView) make?.centerX.mas_equalTo()(self) } else{ make?.left.equalTo()(leftBtnContains ? self.leftStackView.mas_right:self)?.offset()(leftBtnContains ? self.navStyle.styleForElementSpace:self.navStyle.styleForContentEdge.left) make?.right.equalTo()(rightBtnContains ? self.rightStackView.mas_left:self)?.offset()(rightBtnContains ? -self.navStyle.styleForElementSpace:-self.navStyle.styleForContentEdge.right) make?.centerY.mas_equalTo()(self.leftStackView) } } } self.navBarHeightSubject.onNext(self.navBarHeight()) } } extension MCNavBarView{ //重设样式 @objc func resetStyle(){ guard let transparentStyle = self.navTransparentStyle else{ self.resetConstraint() return } guard let scrollView = self.navTransparentBinderedScrollView else{ self.resetTransparentStyle() self.resetConstraint() return } //包含透明样式 if scrollView.contentOffset.y > transparentStyle.styleForScrollDistanceChange{ //正常样式 self.resetNormalStyle() } else{ //透明样式 self.resetTransparentStyle() } self.resetConstraint() } @objc func resetNormalStyle(){ self.navIsTransparent = false self.backgroundColor = self.navStyle.styleForBackgroundColor self.titleLabel.textColor = self.navStyle.styleForTitleColor self.subTitleLabel.textColor = self.navStyle.styleForSubTitleColor _ = self.leftBtns.map { btn in btn.setImage(btn.image, for: .normal) } _ = self.rightBtns.map { btn in btn.setImage(btn.image, for: .normal) } } @objc func resetTransparentStyle(){ self.navIsTransparent = true self.backgroundColor = self.navTransparentStyle?.styleForBackgroundColor self.titleLabel.textColor = self.navTransparentStyle?.styleForTitleColor self.subTitleLabel.textColor = self.navTransparentStyle?.styleForSubTitleColor _ = self.leftBtns.map { btn in btn.setImage(btn.transparentImage, for: .normal) } _ = self.rightBtns.map { btn in btn.setImage(btn.transparentImage, for: .normal) } } } extension MCNavBarView: MCNavBarDelegate{ @objc func navBarHeight() -> CGFloat { if type == .defaults{ if self.subTitleLabel.text.isNotEmptyStr{ return kNavAndStatuHeight + 22 } } return kNavAndStatuHeight } @objc func setNavSubTitle(_ title: String?){ if type == .defaults{ self.subTitleLabel.text = title if self.subTitleLabel.superview.isNil{ self.contentView.addArrangedSubview(self.subTitleLabel) self.contentView.insertArrangedSubview(self.subTitleLabel, at: self.contentView.arrangedSubviews.count - 1) } self.subTitleLabel.isHidden = title.isEmptyStr self.resetConstraint() } } @objc func setNavTitle(_ title: String?){ if type == .defaults{ self.titleLabel.text = title if self.titleLabel.superview.isNil{ self.contentView.addArrangedSubview(self.titleLabel) self.contentView.insertArrangedSubview(self.titleLabel, at: 0) } self.titleLabel.isHidden = title.isEmptyStr self.resetConstraint() } } /// 设置右边按钮的标题 图片 /// - Parameters: /// - title: <#title description#> /// - image: <#image description#> /// - index: <#index description#> func setNavRightBtnTitle(_ title: String?, image: String?, at index: Int){ let search = self.rightBtns.canGet(at: index) if search.0{ let index = search.1 let btn = self.rightBtns[index] btn.setTitle(title, for: .normal) let image: UIImage? = image.isEmptyStr ? nil:UIImage.init(named: image!) btn.setImage(image, for: .normal) btn.image = image self.resetStyle() } } func setNavLeftBtnTitle(_ title: String?, image: String?, at index: Int){ let search = self.leftBtns.canGet(at: index) if search.0{ let index = search.1 let btn = self.leftBtns[index] btn.setTitle(title, for: .normal) let image: UIImage? = image.isEmptyStr ? nil:UIImage.init(named: image!) btn.setImage(image, for: .normal) btn.image = image self.resetStyle() } } /// 新增按钮 /// - Parameters: /// - title: <#title description#> /// - image: <#image description#> /// - tap: <#tap description#> /// - semantic /// - index: <#index description#> func insertNavRightBtn(_ title: String?, image: String?, tap: (()->Void)?, semantic: UISemanticContentAttribute?, at index: Int){ let search = self.rightBtns.canInsert(at: index) if search.0{ let index = search.1 let image: UIImage? = image.isEmptyStr ? nil:UIImage.init(named: image!) let btn = MCNavBtn.createButton(withTitle: title, titleColor: self.navStyle.styleForBtnTitleColor, font: self.navStyle.styleForBtnTitleFont, image: image, tap: tap) btn.image = image btn.semanticContentAttribute = semantic ?? .forceRightToLeft btn.setTitleAndImageDistance(self.navStyle.styleForBtnTitleAndImageDistance) self.rightBtns.insert(btn, at: index) self.rightStackView.insertArrangedSubview(btn, at: index) self.resetStyle() } } func insertNavLeftBtn(_ title: String?, image: String?, tap: (()->Void)?, semantic: UISemanticContentAttribute?, at index: Int){ let search = self.leftBtns.canInsert(at: index) if search.0{ let index = search.1 let image: UIImage? = image.isEmptyStr ? nil:UIImage.init(named: image!) let btn = MCNavBtn.createButton(withTitle: title, titleColor: self.navStyle.styleForBtnTitleColor, font: self.navStyle.styleForBtnTitleFont, image: image, tap: tap) btn.image = image btn.semanticContentAttribute = semantic ?? .forceRightToLeft btn.setTitleAndImageDistance(self.navStyle.styleForBtnTitleAndImageDistance) self.leftBtns.insert(btn, at: index) self.leftStackView.insertArrangedSubview(btn, at: index) self.resetStyle() } } /// 设置隐藏 /// - Parameters: /// - hidden: <#hidden description#> /// - index: <#index description#> func setNavRightBtnHidden(_ hidden: Bool, at index: Int){ let search = self.rightBtns.canGet(at: index) if search.0{ let index = search.1 let btn = self.rightBtns[index] btn.isHidden = hidden self.resetConstraint() } } func setNavLeftBtnHidden(_ hidden: Bool, at index: Int) { let search = self.leftBtns.canGet(at: index) if search.0{ let index = search.1 if index == 0{ self.showNavBackBtn = !hidden } else{ let btn = self.leftBtns[index] btn.isHidden = hidden self.resetConstraint() } } } /// 删除按钮 /// - Parameter index: <#index description#> func deleteNavRightBtn(at index: Int){ let search = self.rightBtns.canDelete(at: index) if search.0{ let index = search.1 self.rightStackView.removeArrangedSubview(self.rightBtns[index]) self.rightBtns.remove(at: index) self.resetConstraint() } } /// 删除左边按钮 不可以删除返回按钮,可以操作隐藏 /// - Parameter index: <#index description#> func deleteNavLeftBtn(at index: Int){ let search = self.leftBtns.canDelete(at: index) if search.0{ let index = search.1 if index == 0{ return } self.leftStackView.removeArrangedSubview(self.leftBtns[index]) self.leftBtns.remove(at: index) self.resetConstraint() } } /// 获取右侧按钮的位置 /// - Parameter index: <#index description#> func getNavRightBtnFrame(at index: Int) -> CGRect?{ MAIN {} let search = self.rightBtns.canGet(at: index) if search.0{ let index = search.1 let searchBtn = self.rightBtns[index] return self.rightStackView.convert(searchBtn.frame, to: self) } return nil } /// 获取左侧按钮的位置 /// - Parameter index: <#index description#> func getNavLeftBtnFrame(at index: Int) -> CGRect?{ MAIN {} let search = self.leftBtns.canGet(at: index) if search.0{ let index = search.1 let searchBtn = self.leftBtns[index] return self.leftStackView.convert(searchBtn.frame, to: self) } return nil } /// 设置右边按钮 透明导航 图片 /// - Parameters: /// - img: <#img description#> /// - index: <#index description#> func setNavRightBtnTransparentImage(_ img: String, at index: Int){ let search = self.rightBtns.canGet(at: index) if search.0{ let index = search.1 let image = img.isEmpty ? nil:UIImage.init(named: img) let btn = self.rightBtns[index] btn.transparentImage = image self.resetStyle() } } /// 设置左边按钮 透明导航 图片 /// - Parameters: /// - img: <#img description#> /// - index: <#index description#> func setNavLeftBtnTransparentImage(_ img: String, at index: Int){ let search = self.leftBtns.canGet(at: index) if search.0{ let index = search.1 let image = img.isEmpty ? nil:UIImage.init(named: img) let btn = self.leftBtns[index] btn.transparentImage = image self.resetStyle() } } } extension Reactive where Base : NSLayoutConstraint{ /// Reactive wrapper for `.constant` public var constBind : Binder{ return Binder(self.base) { layoutConst, constBind in layoutConst.constant = constBind } } } fileprivate class MCNavBtn: UIButton{ //透明图片 var transparentImage: UIImage? //图片 var image: UIImage? } extension UIButton{ func getAdjustWidth(minWidth: CGFloat, titleDistanceImage: CGFloat = 2) -> CGFloat{ var adjustWidth: CGFloat = 0; if let title = self.title(for: .normal){ if let font = self.titleLabel?.font{ let rect = title.boundingRect(with: CGSize.init(width: .greatestFiniteMagnitude, height: font.pointSize), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font:font], context: nil) adjustWidth += CGFloat(ceilf(Float(rect.width))) } } if let image = self.image(for: .normal){ if titleDistanceImage > 0 && adjustWidth > 0{ adjustWidth += titleDistanceImage } adjustWidth += image.size.width } if adjustWidth < minWidth{ adjustWidth = minWidth } return adjustWidth } }