在scrollView滑动的过程中,改变对应title的颜色以及下划线的颜色;
1.效果图
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)
}
}
}