swift 標(biāo)題視圖聯(lián)動(dòng) 顏色漸變 滾動(dòng)條漸變

現(xiàn)在好多app(喜馬拉雅,新浪微博等)都有類似UIPageViewController的效果,實(shí)際開(kāi)發(fā)中我們要進(jìn)行特殊效果,需要自定義,我寫(xiě)了一個(gè)swift版本的.效果如下
點(diǎn)擊鏈接下載 github下載地址

  • 多標(biāo)題,可以滑動(dòng)
swift多控制器滾動(dòng).gif
  • 標(biāo)題較少,上面不滾動(dòng),距離等分
swift多控制器滾動(dòng)1.gif

這種效果有很多坑點(diǎn),比如,快速連續(xù)滑動(dòng)時(shí)候流暢性問(wèn)題,字體顏色漸變色效果,標(biāo)題盡量滑到居中位置,我要起始在非第一個(gè)控制器上等等.下面我把關(guān)鍵代碼貼出來(lái).

UI架構(gòu)

我用的是父子控制器, parentVc.addChildViewController(vc),子控制器的視圖放在父控制器的collectionView上顯示.默認(rèn)樣式我用了一個(gè)JDPageStyle類專門(mén)進(jìn)行處理.
標(biāo)題較多時(shí)用 style.isScrollEnable = true 來(lái)讓上面的標(biāo)題可以滾動(dòng),標(biāo)題較少時(shí)style.isScrollEnable = false上面距離等分.
上面用scrollView 下面用collectionView,互為對(duì)方的代理,對(duì)方代理傳過(guò)來(lái)后自己滑動(dòng),自己滑動(dòng)發(fā)送代理對(duì)方滾動(dòng),對(duì)方傳來(lái)滑動(dòng)消息自己滑動(dòng)時(shí),不發(fā)送代理.

fileprivate func setupUI() {
    // 1.將childVc添加到父控制器中
    for vc in childVcs {
        parentVc.addChildViewController(vc)
    }
    // 2.初始化用于顯示子控制器View的View(UIScrollView/UICollectionView)
    addSubview(collectionView)
}

//設(shè)置contentView&titleView關(guān)系
titleView?.delegate = contentView
contentView?.delegate = titleView

  • 設(shè)置起始index時(shí)要用多線程,主線程異步,這樣初始化完畢后才執(zhí)行這個(gè)滾動(dòng)方法.
    DispatchQueue.main.async {
            let indexPa = IndexPath(item: self.startIndex, section: 0)
            self.contentView?.collectionView.scrollToItem(at: indexPa, at: .centeredHorizontally, animated: false)
            self.contentView?.delegate?.contentView((self.contentView)!, targetIndex: self.startIndex, progress: 1)
            self.contentView?.delegate?.contentView( (self.contentView)!, endScroll: self.startIndex)
        }
  • 按鈕滾動(dòng)到盡量居中位置
    標(biāo)簽滑動(dòng)中心位置大于屏幕一半時(shí)候,如果scrollView足夠長(zhǎng),scrollview滑動(dòng),使標(biāo)簽居中
  var offsetX = newLabel.center.x - scrollView.bounds.width * 0.5
    if offsetX < 0 {
        offsetX = 0
    }

//滑動(dòng)到后面,scrollview不能再滑動(dòng)時(shí)候,不滑動(dòng)
let maxOffset = scrollView.contentSize.width - bounds.width
if offsetX > maxOffset {
offsetX = maxOffset
}
scrollView.setContentOffset(CGPoint(x: offsetX, y: 0), animated: true)

下面控制器滑動(dòng)時(shí)候

在scrollViewDidScroll里面判斷左滑右滑,并判斷進(jìn)度,把信息傳遞給上面.

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if scrollView.contentOffset.x == startOffsetX || isForbidDelegate { return }
    // 1.定義目標(biāo)的index、進(jìn)度
    var targetIndex : Int = 0
    var progress : CGFloat = 0
    // 2.判斷用戶是左滑還是右滑
    if scrollView.contentOffset.x > startOffsetX { // 左滑 右移動(dòng)
        targetIndex = Int(startOffsetX / scrollView.bounds.width) + 1
        if targetIndex >= childVcs.count {
            targetIndex = childVcs.count - 1
        }
        progress = (scrollView.contentOffset.x - startOffsetX) / scrollView.bounds.width
    } else { // 右滑
        targetIndex = Int(startOffsetX / scrollView.bounds.width) - 1
        if targetIndex < 0 {
            targetIndex = 0
        }
        progress = (startOffsetX - scrollView.contentOffset.x) / scrollView.bounds.width
    }
    // 3.將數(shù)據(jù)傳遞給titleView
    delegate?.contentView(self, targetIndex: targetIndex, progress: progress)
}}
快速滑動(dòng)細(xì)節(jié)處理 如果開(kāi)始拖拽時(shí)候下面view一頁(yè)沒(méi)滑動(dòng)完畢,正在滑動(dòng),應(yīng)計(jì)算他應(yīng)該在哪里結(jié)束,讓它滑動(dòng)到它應(yīng)該結(jié)束的地方,以此為其實(shí)值,要不會(huì)有上面標(biāo)題底部條顯示錯(cuò)亂
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
    // 記錄開(kāi)始的位置
    isForbidDelegate = false
    if Int(collectionView.contentOffset.x) % Int(collectionView.bounds.width) != 0  {
        let theNum = collectionView.contentOffset.x / collectionView.bounds.width
            let res = self.numFormat(num: Float(theNum ), format: "0")
            collectionView.scrollToItem(at: IndexPath(item: Int(res)!, section: 0), at: .left, animated: false)
            startOffsetX = collectionView.bounds.width*CGFloat(Float(res)!)
            delegate?.contentView(self, targetIndex:  Int(res)!, progress: 1)
            delegate?.contentView(self, endScroll:  Int(res)!)
    }else{
        startOffsetX = scrollView.contentOffset.x
    }
}

文字顏色漸變處理

文字顏色不能有透明度,要可以轉(zhuǎn)成純的rgb顏色才可以
在上面view里面接受到下面正在滑動(dòng)中的progress時(shí)候,根據(jù)新舊兩個(gè)index,取出兩個(gè)頭部label,設(shè)置顏色字體等

 func contentView(_ contentView: JDContentView, targetIndex: Int, progress: CGFloat) {
   if progress < 1 {
        self.isUserInteractionEnabled = false
    }else{
        self.isUserInteractionEnabled = true
    }
    // 1.取出兩個(gè)Label
    let oldLabel = titleLabels[currentIndex]
    let newLabel = titleLabels[targetIndex]
// 2.漸變文字顏色
    let selectRGB = getGRBValue(style.selectColor)
    let normalRGB = getGRBValue(style.normalColor)
    let deltaRGB = (selectRGB.0 - normalRGB.0, selectRGB.1 - normalRGB.1, selectRGB.2 - normalRGB.2)
    oldLabel.textColor = UIColor(r: selectRGB.0 - deltaRGB.0 * progress, g: selectRGB.1 - deltaRGB.1 * progress, b: selectRGB.2 - deltaRGB.2 * progress)
    newLabel.textColor = UIColor(r: normalRGB.0 + deltaRGB.0 * progress, g: normalRGB.1 + deltaRGB.1 * progress, b: normalRGB.2 + deltaRGB.2 * progress)
    if progress > 0.5 {
        oldLabel.font = style.titleFont
        newLabel.font = style.selectedTitleFont
    }else{
        oldLabel.font = style.selectedTitleFont
        newLabel.font = style.titleFont
    }}
private func getGRBValue(_ 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)
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容