Swift-根據(jù)滑動距離計(jì)算標(biāo)題顏色和下劃線距離

在scrollView滑動的過程中,改變對應(yīng)title的顏色以及下劃線的顏色;

1.效果圖

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

2.實(shí)現(xiàn)過程

  • 第一步坤候,自定義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?

  // 當(dāng)前選中的索引
  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) {
        // 如果點(diǎn)擊titleView吨灭,就不用計(jì)算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: ["標(biāo)題一", "標(biāo)題二", "標(biāo)題三", "標(biāo)題四"])
        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)
        }
    }
 
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市刑巧,隨后出現(xiàn)的幾起案子喧兄,更是在濱河造成了極大的恐慌,老刑警劉巖海诲,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件繁莹,死亡現(xiàn)場離奇詭異檩互,居然都是意外死亡特幔,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門闸昨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蚯斯,“玉大人,你說我怎么就攤上這事饵较∨那叮” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵循诉,是天一觀的道長横辆。 經(jīng)常有香客問我,道長茄猫,這世上最難降的妖魔是什么狈蚤? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮划纽,結(jié)果婚禮上脆侮,老公的妹妹穿的比我還像新娘。我一直安慰自己勇劣,他們只是感情好靖避,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著比默,像睡著了一般幻捏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上命咐,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天粘咖,我揣著相機(jī)與錄音,去河邊找鬼侈百。 笑死瓮下,一個胖子當(dāng)著我的面吹牛翰铡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播讽坏,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼锭魔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了路呜?” 一聲冷哼從身側(cè)響起迷捧,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎胀葱,沒想到半個月后漠秋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抵屿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年庆锦,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片轧葛。...
    茶點(diǎn)故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡搂抒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出尿扯,到底是詐尸還是另有隱情求晶,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布衷笋,位于F島的核電站芳杏,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏辟宗。R本人自食惡果不足惜爵赵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望慢蜓。 院中可真熱鬧亚再,春花似錦、人聲如沸晨抡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽耘柱。三九已至如捅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間调煎,已是汗流浹背镜遣。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留士袄,地道東北人悲关。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓谎僻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親寓辱。 傳聞我的和親對象是個殘疾皇子艘绍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評論 2 348