Swift-根据滑动距离计算标题颜色和下划线距离

在scrollView滑动的过程中,改变对应title的颜色以及下划线的颜色;

1.效果图

Dec-24-2018 17-27-53.gif

2.实现过程

  • 第一步,自定义titleView;
  • 第二步,自定义contentView;
  • 第三步,交互部分;

首先第一部分代码如下:

class HeaderTitleView: UIView {
  
  private let tagNumber = 100
  private var titleW: CGFloat = 0
  private var titles: [String]?
  private var titleLabels: [UILabel] = []
  private var lineView: UIView = UIView()
  
  typealias HeaderTitleColor = (r: CGFloat, g: CGFloat, b: CGFloat)
  let normalColor = HeaderTitleColor(109, 109, 109)
  let selectedColor = HeaderTitleColor(238, 154, 72)
  
  var didClickTitle: SimpleCallBackWitInt?

  // 当前选中的索引
  private var selectedIndex: Int = 0
  
  init(frame: CGRect, titles: [String]) {
      self.titles = titles
      
      super.init(frame: frame)
      
      addTitleLabels()
  }
  
  func addTitleLabels() {
      backgroundColor = UIColor.white
      guard let titles = titles, titles.count > 0 else { return }
      
      titleW = SCREEN_WIDTH / CGFloat(titles.count)
      let titleH: CGFloat = self.frame.height
      var titleX: CGFloat = 0
      
      for i in 0..<titles.count {
          titleX = CGFloat(i) * titleW
          let label = UILabel(frame: CGRect(x: titleX, y: 0, width: titleW, height: titleH))
          label.tag = tagNumber + i
          label.text = titles[i]
          label.font = UIFont.systemFont(ofSize: 15)
          label.textAlignment = .center
          label.isUserInteractionEnabled = true
          label.textColor = UIColor(red: normalColor.r / 255.0, green: normalColor.g / 255.0, blue: normalColor.b / 255.0, alpha: 1.0)
          
          let tap = UITapGestureRecognizer(target: self, action: #selector(titleLabelDidClick(gesture:)))
          label.addGestureRecognizer(tap)
          
          addSubview(label)
          titleLabels.append(label)
          
          if i == 0 {
              label.textColor = UIColor(red: selectedColor.r / 255.0, green: selectedColor.g / 255.0, blue: selectedColor.b / 255.0, alpha: 1.0)
          }
      }
      
      lineView.frame = CGRect(x: 0, y: self.frame.height - 2, width: titleW, height: 2)
      lineView.backgroundColor = UIColor.orange
      addSubview(lineView)
  }
  
  @objc func titleLabelDidClick(gesture: UITapGestureRecognizer) {
      guard let label = gesture.view as? UILabel else { return }
      guard label.tag - tagNumber != selectedIndex else { return }
      
      let lastLabel = self.titleLabels[selectedIndex]
      lastLabel.textColor = UIColor(red: normalColor.r / 255.0, green: normalColor.g / 255.0, blue: normalColor.b / 255.0, alpha: 1.0)
      label.textColor = UIColor(red: selectedColor.r / 255.0, green: selectedColor.g / 255.0, blue: selectedColor.b / 255.0, alpha: 1.0)

      lineView.frame.origin.x = label.frame.origin.x
      
      selectedIndex = label.tag - tagNumber

      didClickTitle?(selectedIndex)
  }
  
  func changeTitleLineView(lastIndex: Int, toIndex: Int, percent: CGFloat) {
      let lastLabel = self.titleLabels[lastIndex]
      let toLabel = self.titleLabels[toIndex]
      
      let offsetX = toLabel.frame.origin.x - lastLabel.frame.origin.x
      
      let deltaX = offsetX * percent

      lineView.frame = CGRect(x: lastLabel.frame.origin.x + deltaX, y: lineView.frame.origin.y, width: lineView.frame.size.width, height: lineView.frame.size.height)
      
      let colorDelta: HeaderTitleColor = (selectedColor.r - normalColor.r, selectedColor.g - normalColor.g, selectedColor.b - normalColor.b)
      lastLabel.textColor = UIColor(red: (selectedColor.r - colorDelta.r * percent) / 255.0, green: (selectedColor.g - colorDelta.g * percent) / 255.0, blue: (selectedColor.b - colorDelta.b * percent) / 255.0, alpha: 1.0)
      toLabel.textColor = UIColor(red: (normalColor.r + colorDelta.r * percent) / 255.0, green: (normalColor.g + colorDelta.g * percent) / 255.0, blue: (normalColor.b + colorDelta.b * percent) / 255.0, alpha: 1.0)
      
      selectedIndex = toIndex
  }
  
  required init?(coder aDecoder: NSCoder) {
      fatalError("init(coder:) has not been implemented")
  }
  
}

第二部分代码:

class MainContentView: UIView {

    var isHeaderClick: Bool = false
    private var startOffsetX: CGFloat = 0
    private var childVCs: [UIViewController] = []
    var didScrollToIndex: SimpleCallBackWithPercent?
    
    private lazy var collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        layout.minimumLineSpacing = 0
        layout.minimumInteritemSpacing = 0
        layout.itemSize = CGSize(width: SCREEN_WIDTH, height: KContentHeight)
        let collection = UICollectionView(frame: self.bounds, collectionViewLayout: layout)
        collection.isPagingEnabled = true
        return collection
    }()
    
    init(frame: CGRect, childVCs: [UIViewController]) {
        super.init(frame: frame)
        
        self.childVCs = childVCs
        addSubviews()
    }
    
    func addSubviews() {
        collectionView.backgroundColor = UIColor.white
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "UICollectionViewCellID")
        addSubview(collectionView)
        
    }
    
    func scrollToPage(index: Int) {
        
        collectionView.setContentOffset(CGPoint(x: CGFloat(index) * SCREEN_WIDTH, y: 0), animated: false)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}

extension MainContentView: UICollectionViewDataSource {
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return childVCs.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "UICollectionViewCellID", for: indexPath)
        let view = childVCs[indexPath.row].view!
        view.backgroundColor = indexPath.row % 2 == 0 ? .lightGray : .cyan
        cell.addSubview(view)
        return cell
    }
}

extension MainContentView: UICollectionViewDelegate {
    
    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        
        isHeaderClick = false
        startOffsetX = scrollView.contentOffset.x
    }
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        // 如果点击titleView,就不用计算percent
        guard isHeaderClick == false else { return }
        
        var lastIndex = 0
        var toIndex = 0
        var percent: CGFloat = 0
        
        let offsetX = scrollView.contentOffset.x
        if offsetX <= 0 || offsetX >= CGFloat(childVCs.count - 1) * SCREEN_WIDTH {
            return
        }

        if offsetX > startOffsetX { // 向左滑动
            percent = (offsetX - startOffsetX) / SCREEN_WIDTH
            lastIndex = Int(offsetX / SCREEN_WIDTH)
            toIndex = lastIndex + 1
            
            if toIndex >= childVCs.count {
                toIndex = childVCs.count - 1
            }
            
            if offsetX - startOffsetX == SCREEN_WIDTH {
                percent = 1.0
                toIndex = lastIndex
            }

            didScrollToIndex?(lastIndex, toIndex, percent)

        } else {
            percent = 1.0 - (offsetX / SCREEN_WIDTH - floor(offsetX / SCREEN_WIDTH))
            toIndex = Int(offsetX / SCREEN_WIDTH)
            lastIndex = toIndex + 1
            
            if toIndex >= childVCs.count {
                toIndex = childVCs.count - 1
            }
            
            if startOffsetX - offsetX == SCREEN_WIDTH {
                lastIndex = toIndex
            }
  
            didScrollToIndex?(lastIndex, toIndex, percent)
        }
    }

}

第三部分代码:

import UIKit

private let KHeaderHeight: CGFloat = 50
private let KContentHeight: CGFloat = SCREEN_HEIGHT - NavBarHeight - KHeaderHeight

typealias SimpleCallBackWitInt = (_ selIndex: Int) -> ()
typealias SimpleCallBackWithPercent = (_ lastIndex: Int, _ toIndex: Int, _ percent: CGFloat) -> ()

class Test08ViewController: BaseViewController {
  
    private lazy var titleView: HeaderTitleView = {
        let view = HeaderTitleView(frame: CGRect(x: 0, y: NavBarHeight, width: SCREEN_WIDTH, height: KHeaderHeight), titles: ["标题一", "标题二", "标题三", "标题四"])
        return view
    }()
    
    private lazy var contentView: MainContentView = {
        let vcs = [UIViewController(), UIViewController(), UIViewController(), UIViewController()]
        let view = MainContentView(frame: CGRect(x: 0, y: NavBarHeight + KHeaderHeight, width: SCREEN_WIDTH, height: KContentHeight), childVCs: vcs)
        return view
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        addSubviews()
    }
    
    func addSubviews() {
        view.addSubview(titleView)
        view.addSubview(contentView)
        
        titleView.didClickTitle = { [weak self] selIndex in
            self?.contentView.isHeaderClick = true
            self?.contentView.scrollToPage(index: selIndex)
        }
     
        contentView.didScrollToIndex = { [weak self] (lastIndex, toIndex, percent) in
            self?.titleView.changeTitleLineView(lastIndex: lastIndex, toIndex: toIndex, percent: percent)
        }
    }
 
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345