123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- import UIKit
- public protocol FFPageMenuViewDelegate {
- func pageMenuView(pageMenuView: FFPageMenuView, didSelectItemAt index: Int)
- }
- public enum FFPageMenuContentMode {
- case scaleAspectFill
- case center(space: CGFloat)
- }
- @IBDesignable
- open class FFPageMenuView: UIView {
- private var needlayout = true
- private var space: CGFloat = 0
- private let baseTag = 55161750
- private var selectedLablel = UILabel()
- private var sliderConstriant: NSLayoutConstraint?
- private var isTap = false
- private var ffConstraints = [NSLayoutConstraint]()
-
- public var sliderView: UIView?
- public var selectedColor = UIColor.blue
- public var normalColor = UIColor.black
- public var font = UIFont.systemFont(ofSize: 17)
- public var pageDelegate: FFPageMenuViewDelegate?
- public var selectIndex: Int = 0
- public var pageMenuContentMode = FFPageMenuContentMode.scaleAspectFill
- public var isBigger = true
- public var titles = [String]() {
- didSet {
- needlayout = true
- }
- }
- public override init(frame: CGRect) {
- super.init(frame: frame)
- let tap = UITapGestureRecognizer(target: self, action: #selector(didTap(gesture:)))
- addGestureRecognizer(tap)
- }
- required public init?(coder aDecoder: NSCoder) {
- super.init(coder: aDecoder)
- let tap = UITapGestureRecognizer(target: self, action: #selector(didTap(gesture:)))
- addGestureRecognizer(tap)
- }
- @objc func didTap(gesture: UITapGestureRecognizer) {
- let point = gesture.location(in: self)
- let targetView = subviews.filter { $0.frame.contains(point) }.first
- guard let target = targetView else { return }
- scroll(at: target.tag - baseTag)
- pageDelegate?.pageMenuView(pageMenuView: self, didSelectItemAt: target.tag - baseTag)
- selectIndex = target.tag - baseTag
- }
- open override func layoutSubviews() {
- super.layoutSubviews()
- if needlayout {
- reloadSubviews()
- }
- }
- private func reloadSubviews() {
- func clear() {
- subviews.forEach { $0.removeFromSuperview() }
- NSLayoutConstraint.deactivate(ffConstraints)
- ffConstraints.removeAll()
- needlayout = false
- }
- clear()
- layoutIfNeeded()
- configurationOfMenu()
- configurationOfSlider()
- NSLayoutConstraint.activate(ffConstraints)
- }
- }
- extension FFPageMenuView {
- private func configurationOfSlider() {
- guard let label = viewWithTag(baseTag) else {
- return
- }
- if sliderView == nil {
- sliderView = UIView(frame: CGRect(x: 0, y: 0, width: 15, height: 2))
- sliderView?.backgroundColor = selectedColor
- }
- sliderView!.translatesAutoresizingMaskIntoConstraints = false
- addSubview(sliderView!)
-
- sliderConstriant = NSLayoutConstraint(item: sliderView!,
- attribute: .centerX,
- relatedBy: .equal,
- toItem: label,
- attribute: .centerX,
- multiplier: 1,
- constant: 0)
- let sliderConstriants = [sliderConstriant!,
- NSLayoutConstraint(item: sliderView!,
- attribute: .height,
- relatedBy: .equal,
- toItem: nil,
- attribute: .height,
- multiplier: 1,
- constant: sliderView!.bounds.height),
- NSLayoutConstraint(item: sliderView!,
- attribute: .top,
- relatedBy: .equal,
- toItem: label,
- attribute: .bottom,
- multiplier: 1,
- constant: 5),
- NSLayoutConstraint(item: sliderView!,
- attribute: .width,
- relatedBy: .equal,
- toItem: nil,
- attribute: .width,
- multiplier: 1,
- constant: sliderView!.bounds.width)]
- ffConstraints += sliderConstriants
- }
- private func configurationOfMenu() {
- var last: UILabel?
- let labelWidth: CGFloat
- switch pageMenuContentMode {
- case .scaleAspectFill:
- space = 0
- labelWidth = bounds.width / CGFloat(titles.count)
- case .center(let spacing):
- space = spacing
- labelWidth = 0
- }
- var contentWidth: CGFloat = 0
- for (i, title) in titles.enumerated() {
-
- let label = UILabel()
- label.tag = baseTag + i
- label.text = title
- label.font = font
- label.textAlignment = .center
- label.translatesAutoresizingMaskIntoConstraints = false
- label.sizeToFit()
- label.textColor = normalColor
- addSubview(label)
- if i == 0 {
- selectedLablel = label
- label.textColor = selectedColor
- if isBigger {
- label.font = UIFont.systemFont(ofSize: font.pointSize + 1)
- }
- }
-
- let leftConstraint: NSLayoutConstraint
- if last == nil {
- leftConstraint = NSLayoutConstraint(item: label,
- attribute: .leading,
- relatedBy: .equal,
- toItem: self,
- attribute: .leading,
- multiplier: 1,
- constant: space)
- } else {
- leftConstraint = NSLayoutConstraint(item: label,
- attribute: .left,
- relatedBy: .equal,
- toItem: last,
- attribute: .right,
- multiplier: 1,
- constant: space)
- }
- var labelConstraints = [NSLayoutConstraint(item: label,
- attribute: .centerY,
- relatedBy: .equal,
- toItem: self,
- attribute: .centerY,
- multiplier: 1,
- constant: 0),
- leftConstraint]
- if labelWidth != 0 {
- labelConstraints.append(NSLayoutConstraint(item: label,
- attribute: .width,
- relatedBy: .equal,
- toItem: nil,
- attribute: .width,
- multiplier: 1,
- constant: labelWidth))
- contentWidth += labelWidth
- } else {
- contentWidth += label.bounds.width + space
- }
- ffConstraints += labelConstraints
- last = label
- }
- ffConstraints.append(NSLayoutConstraint(item: self,
- attribute: .width,
- relatedBy: .equal,
- toItem: nil,
- attribute: .width,
- multiplier: 1,
- constant: contentWidth))
- }
- }
- extension FFPageMenuView {
- private func translationSlider(percent: CGFloat) {
- sliderConstriant?.isActive = false
- sliderConstriant = NSLayoutConstraint(item: sliderView!,
- attribute: .centerX,
- relatedBy: .equal,
- toItem: selectedLablel,
- attribute: .centerX,
- multiplier: 1,
- constant: percent)
- sliderConstriant?.isActive = true
- }
- func scroll(at index: Int, animated: Bool = true) {
- guard let targetLabel = viewWithTag(index + baseTag) as? UILabel,
- targetLabel.tag != selectedLablel.tag else { return }
- selectedLablel.textColor = normalColor
- selectedLablel.font = font
- targetLabel.textColor = selectedColor
- if isBigger {
- targetLabel.font = UIFont.systemFont(ofSize: font.pointSize + 1)
- }
- if animated {
- let animation = CAKeyframeAnimation(keyPath: "position.x")
- animation.values = [sliderView!.frame.origin.x, sliderView!.frame.origin.x + targetLabel.frame.origin.x - selectedLablel.frame.origin.x]
- animation.delegate = self
- animation.isRemovedOnCompletion = false
- animation.fillMode = kCAFillModeForwards
- sliderView?.layer.add(animation, forKey: "FFPage.FFPageMenuView.sliderView.animation")
- } else {
- sliderConstriant?.isActive = false
- sliderConstriant = NSLayoutConstraint(item: sliderView!,
- attribute: .centerX,
- relatedBy: .equal,
- toItem: targetLabel,
- attribute: .centerX,
- multiplier: 1,
- constant: 0)
- sliderConstriant?.isActive = true
- }
- selectedLablel = targetLabel
- }
- func scroll(offset: CGFloat) {
- let percent = offset - CGFloat(selectedLablel.tag - baseTag)
- if percent > 0 && percent < 1 {
- guard let nextLabel = viewWithTag(selectedLablel.tag + 1) as? UILabel else {
- return
- }
- translationSlider(percent: percent * (space + nextLabel.frame.width / 2 + selectedLablel.frame.width / 2))
- } else if percent < 0 && percent > -1 {
- guard let nextLabel = viewWithTag(selectedLablel.tag - 1) as? UILabel else {
- return
- }
- translationSlider(percent: percent * (space + nextLabel.frame.width / 2 + selectedLablel.frame.width / 2))
- } else {
- if percent != 0 {
- scroll(at: Int(offset), animated: false)
- }
- }
- }
- }
- extension FFPageMenuView: CAAnimationDelegate {
- public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
-
- translationSlider(percent: 0)
- sliderView!.layer.removeAnimation(forKey: "FFPage.FFPageMenuView.sliderView.animation")
- }
- }
|