學(xué)習(xí)Swift之后這個(gè)控件寫(xiě)了很久了. 但是把這個(gè)框架做成pod集成到項(xiàng)目中的時(shí)候發(fā)現(xiàn)因?yàn)榭丶暮诵念悰](méi)有聲明"public"而導(dǎo)致無(wú)法訪問(wèn). 在把class 前聲明為Public后又有一堆協(xié)議方法也需要修改訪問(wèn)權(quán)限..(這個(gè)比OC有點(diǎn)麻煩了)
順便整理記錄一下這個(gè)框架吧
git: [https://github.com/Zafirzzf/ZFPageTitleView]
/// 構(gòu)造函數(shù)
///
/// - Parameters:
/// - frame: 包含自控制器的整個(gè)frame
/// - titles: 標(biāo)題數(shù)組
/// - childControllers: 內(nèi)容控制器數(shù)組
/// - parentVC: 裝此控件的控制器
/// - style: 界面主題色
public init(frame: CGRect, titles: [String], childControllers: [UIViewController], parentVC: UIViewController,style: ZFPageStyle = ZFPageStyle()) {
self.titleStyle = style
self.titles = titles
self.childControllers = childControllers
self.parentVC = parentVC
parentVC.automaticallyAdjustsScrollViewInsets = false
super.init(frame: frame)
setupUI()
}
在接收到最需要的標(biāo)題數(shù)組和子控制器數(shù)組之后,創(chuàng)建標(biāo)題視圖,并用collectionView展示子控制器里的view.
fileprivate func setupUI() {
// title
let titleView = ZFTitleView(frame: CGRect(x: 0, y: 0, width: bounds.width,
height: titleStyle.titleViewHeight),
style: titleStyle,
titles: titles)
addSubview(titleView)
//內(nèi)容
let contentFrame = CGRect(x: 0, y: titleView.frame.maxY, width: bounds.width, height: bounds.height - titleView.frame.height)
let contentView = ZFContentView(frame:contentFrame,
childControllers: childControllers,
parentVC: parentVC)
contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
addSubview(contentView)
//設(shè)置標(biāo)題view和內(nèi)容視圖的關(guān)系
titleView.delegage = contentView
contentView.delegate = titleView
}
ZFTitleView
與ZFContentView
是抽離出來(lái)的兩個(gè)類.分別控制標(biāo)題視圖與內(nèi)容.titleView于contentView之間的事件交互利用代理完成很方便.
TitleView上根據(jù)標(biāo)題的數(shù)量創(chuàng)建N個(gè)Button添加到scrollView上,根據(jù)標(biāo)題的總長(zhǎng)度調(diào)整位置即可.
在滑動(dòng)下方contentView的時(shí)候上方標(biāo)題視圖中的當(dāng)前選中文字與將要選中的文字有一個(gè)漸變的顏色變化.處理方式如下:
public func contentView(_ contentView: ZFContentView, targetIndex: Int, progress: CGFloat) {
if progress > 0.9 {
selectIndex = targetIndex
//調(diào)整位置
setScrollInset(titleLabels[selectIndex])
}
let fonttransformScale = style.fontTransformScale
let oldLabelScale = fonttransformScale - (fonttransformScale - 1) * progress
let targetLabelScale = 1 + (fonttransformScale - 1) * progress
let oldLabel = titleLabels[selectIndex]
let newLabel = titleLabels[targetIndex]
// 滑動(dòng)過(guò)程中縮放字體
oldLabel.transformScale(scale: oldLabelScale)
newLabel.transformScale(scale: targetLabelScale)
// 改變字體顏色
let normalColor = getRGBValue(style.textNormalColor)
let selectColor = getRGBValue(style.textSelectColor)
let excessColor = (selectColor.0 - normalColor.0,
selectColor.1 - normalColor.1,
selectColor.2 - normalColor.2)
oldLabel.textColor = UIColor(red: (selectColor.0 - excessColor.0 * progress) / 255.0,
green: (selectColor.1 - excessColor.1 * progress) / 255.0,
blue: (selectColor.2 - excessColor.2 * progress) / 255.0,
alpha: 1)
newLabel.textColor = UIColor(red: (normalColor.0 + excessColor.0 * progress) / 255.0,
green: (normalColor.1 + excessColor.1 * progress) / 255.0,
blue: (normalColor.2 + excessColor.2 * progress) / 255.0,
alpha: 1)
}
func getRGBValue(_ color: UIColor) ->(CGFloat,CGFloat,CGFloat) {
guard let components = color.cgColor.components else {
fatalError("文字顏色請(qǐng)按照RGB設(shè)置")
}
return (components[0] * 255, components[1] * 255, components[2] * 255)
}
核心步驟是:
- 獲取normalcolor和selectcolor的兩個(gè)RGB值.
- 兩個(gè)值的R,G,B相減獲取到過(guò)度的一個(gè)顏色值.
- 根據(jù)當(dāng)前滑動(dòng)的進(jìn)度progress,使normalcolor和selectcolor向過(guò)度值進(jìn)行過(guò)度
注: 這種方法要確保Color.cgColor.components有值,所以兩種顏色需要通過(guò)RGB進(jìn)行設(shè)置.其它方法設(shè)置的會(huì)獲取不到RGB值.
如果想要加上滑動(dòng)過(guò)程中sel的標(biāo)題進(jìn)行縮放,同理根據(jù)progress操作.
contentView里有一個(gè)左右滑動(dòng)的collectionView,它的cell就是childControllers里的每個(gè)控制器的view.這樣在控制器比較多的情況下可以讓view之間復(fù)用.比ScrollView會(huì)節(jié)省內(nèi)存的占用.
需要注意的是為了防止復(fù)用.每次加載新view時(shí)要將之前的刪除.
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath)
// 注意刪除之前cell上的view
_ = cell.contentView.subviews.map{ $0.removeFromSuperview() }
let childView = childVC[indexPath.item].view!
childView.frame = cell.contentView.bounds
cell.contentView.addSubview(childView)
return cell
}
滑動(dòng)contentView更新titleView的變化通過(guò)scrollView的代理即可.
//MARK:- ScrollView 代理
extension ZFContentView: UICollectionViewDelegate {
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
delegate?.contentView(self, endScroll:Int(scrollView.contentOffset.x / bounds.width))
}
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
if isForbidDelegate { return }
let offsetX = scrollView.contentOffset.x
var targetIndex: Int
var progress: CGFloat = 0
if offsetX < scrollStartOffSet { //向左滑
progress = (scrollStartOffSet - offsetX) / bounds.width
targetIndex = Int(scrollStartOffSet / bounds.width) - 1
if targetIndex < 0{
targetIndex = 0
}
}else {
progress = (offsetX - scrollStartOffSet) / bounds.width
targetIndex = Int(scrollStartOffSet / bounds.width) + 1
if targetIndex >= childVC.count {
targetIndex = childVC.count - 1
}
}
delegate?.contentView(self, targetIndex: targetIndex, progress: progress)
}
}