iOS 页面滑动与标题切换颜色渐变的联动效果实例
话不多说,直接上图,要实现类似如下效果。
这个效果非常常见,这里着重讲讲核心代码
封装顶部的pagetitleview
封装构造函数
封装构造函数,让别人在创建对象时,就传入其实需要显示的内容 frame:创建对象时确定了
- frame就可以直接设置子控件的位置和尺寸
- isscrollenable:是否可以滚动。某些地方该控件是可以滚动的。
- titles:显示的所有标题
// mark:- 构造函数 init(frame: cgrect, isscrollenable : bool, titles : [string]) { selfisscrollenable = isscrollenable selftitles = titles superinit(frame: frame) }
设置ui界面
设置ui界面
- 添加uiscrollview,如果标题过多,则可以滚动
- 初始化所有的label,用于显示标题。并且给label添加监听手势
- 添加顶部线和滑块的view
实现相对来说比较简单,这里代码从略
封装底部的pagecotentview
封装构造函数
封装构造函数,让别人在创建对象时,就传入其实需要显示的内容
- 所有用于显示在uicollectionview的cell的所有控制器
- 控制器的父控制器
// mark:- 构造函数 init(frame: cgrect, childvcs : [uiviewcontroller], parentviewcontroller : uiviewcontroller) { selfchildvcs = childvcs selfparentviewcontroller = parentviewcontroller superinit(frame: frame) }
设置ui界面内容
设置ui界面
- 将所有的子控制器添加到父控制器中
- 添加uicollectionview,用于展示内容
// mark:- 懒加载属性 private lazy var collectionview : uicollectionview = { // 1.创建布局 let layout = uicollectionviewflowlayout() layout.itemsize = self.bounds.size layout.minimumlinespacing = 0 layout.minimuminteritemspacing = 0 layout.scrolldirection = .horizontal // 2.创建collectionview let collectionview = uicollectionview(frame: self.bounds, collectionviewlayout: layout) collectionview.showshorizontalscrollindicator = false collectionview.pagingenabled = true collectionview.bounces = false collectionview.scrollstotop = false collectionview.datasource = self collectionview.delegate = self collectionview.registerclass(uicollectionviewcell.self, forcellwithreuseidentifier: kcontentcellid) return collectionview }() private func setupui() { // 1.添加所有的控制器 for childvc in childvcs { parentviewcontroller?.addchildviewcontroller(childvc) } // 2.添加collectionview addsubview(collectionview) }
实现uicollectionview的数据源方法
- 在返回cell的方法中,先将cell的contentview中的子控件都移除,防止循环引用
- 取出indexpath.item对应的控制器,将控制器的view添加到cell的contentview中
// mark:- 遵守uicollectionview的数据源 extension pagecontentview : uicollectionviewdatasource { func collectionview(collectionview: uicollectionview, numberofitemsinsection section: int) -> int { return childvcs.count } func collectionview(collectionview: uicollectionview, cellforitematindexpath indexpath: nsindexpath) -> uicollectionviewcell { let cell = collectionview.dequeuereusablecellwithreuseidentifier(kcontentcellid, forindexpath: indexpath) // 移除之前的 for subview in cell.contentview.subviews { subview.removefromsuperview() } // 取出控制器 let childvc = childvcs[indexpath.item] childvc.view.frame = cell.contentview.bounds cell.contentview.addsubview(childvc.view) return cell } }
pagetitleview点击改变pagecontentview
通过代理将pagetitleview的事件传递出去
/// 定义协议 protocol pagetitleviewdelegate : class { func pagetitleview(pagetitleview : pagetitleview, didselectedindex index : int) } @objc private func titlelabelclick(tapges : uitapgesturerecognizer) { // 1.获取点击的下标志 guard let view = tapges.view else { return } let index = view.tag // 2.滚到正确的位置 scrolltoindex(index) // 3.通知代理 delegate?.pagetitleview(self, didselectedindex: index) }
内部调整
// 内容滚动 private func scrolltoindex(index : int) { // 1.获取最新的label和之前的label let newlabel = titlelabels[index] let oldlabel = titlelabels[currentindex] // 2.设置label的颜色 newlabel.textcolor = kselecttitlecolor oldlabel.textcolor = knormaltitlecolor // 3.scrollline滚到正确的位置 let scrolllineendx = scrollline.frame.width * cgfloat(index) uiview.animatewithduration(0.15) { self.scrollline.frame.origin.x = scrolllineendx } // 4.记录index currentindex = index }
在pagecontentview中设置当前应该滚动的位置
// mark:- 对外暴露方法 extension pagecontentview { func scrolltoindex(index : int) { let offset = cgpoint(x: cgfloat(index) * collectionviewboundswidth, y: 0) collectionviewsetcontentoffset(offset, animated: false) } }
pagecontentview滚动调整pagetitleview
通过观察,我们发现:
1> 原来位置的title颜色会逐渐变暗
2> 目标位置的title颜色会逐渐变亮
3> 变化程度是和滚动的多少相关
由此得出结论:
我们一共需要获取三个值
1> 起始位置下标值
2> 目标位置下标值
3> 当前滚动的进度
其实前2点可以由第3点计算而来,可以只需要将进度传递出去。
根据进度值处理标题颜色渐变及滑块逻辑
。当前进度值唯一确定了标题的状态,计算出需要发生颜色变化的两相邻标题索引
。注意:下标值需要防止越界问题,临界点的处理
实现代码
extension pagecontentview : uicollectionviewdelegate { func scrollviewwillbegindragging(scrollview: uiscrollview) { startoffsetx = scrollview.contentoffset.x } func scrollviewdidscroll(scrollview: uiscrollview) { // 0.判断是否是点击事件 if isforbidscrolldelegate { return } // 1.定义获取需要的数据 var progress : cgfloat = 0 let currentoffsetx = scrollview.contentoffset.x let scrollvieww = scrollview.bounds.width // 1.计算progress progress = currentoffsetx / scrollvieww // 3.将progress传递给titleview delegate?.pagecontentview(self, progress: progress) } }
根据滚动传入的值,调整pagetitleview
两种颜色必须使用rgb值设置(方便通过rgb实现渐变效果)
private let knormalrgb : (cgfloat, cgfloat, cgfloat) = (85, 85, 85) private let kselectrgb : (cgfloat, cgfloat, cgfloat) = (255, 128, 0) private let kdeltargb = (kselectrgb.0 - knormalrgb.0, kselectrgb.1 - knormalrgb.1, kselectrgb.2 - knormalrgb.2) private let knormaltitlecolor = uicolor(red: 85/255.0, green: 85/255.0, blue: 85/255.0, alpha: 1.0) private let kselecttitlecolor = uicolor(red: 255.0/255.0, green: 128/255.0, blue: 0/255.0, alpha: 1.0)
调整scrollline及两个label颜色渐变
// mark:- 对外暴露方法 extension pagetitleview func changelabel(progress: cgfloat) { // 开启弹簧效果时的过滤处理 var progress = progress > 0 ? progress : 0 progress = progress <= cgfloat(titlelabels.count - 1) ? progress : cgfloat(titlelabels.count - 1) var leftlabelindex = int(floor(progress)) let ratio = progress - cgfloat(leftlabelindex) //获取leftlabel和rightlabel let leftlabel = titlelabels[leftlabelindex] if leftlabelindex >= 3{ leftlabelindex = 3 } print("leftlabelindex = \(leftlabelindex)") var rightindex = leftlabelindex + 1 if rightindex >= 3{ rightindex = 3 } print("rightindex = \(rightindex)") let rightlabel = titlelabels[rightindex] //滑块的逻辑 let movetotalx = leftlabel.frame.width let movex = movetotalx * ratio scrollline.frame.origin.x = leftlabel.frame.origin.x + movex //3.label颜色的渐变 // 3.1.取出变化的范围 let colordelta = (kselectedcolor.0 - knormalcolor.0, kselectedcolor.1 - knormalcolor.1, kselectedcolor.2 - knormalcolor.2) if leftlabelindex != rightindex { // 3.2.变化leftlabel leftlabel.textcolor = uicolor(r: kselectedcolor.0 - colordelta.0 * ratio, g: kselectedcolor.1 - colordelta.1 * ratio, b: kselectedcolor.2 - colordelta.2 * ratio) // 3.2.变化rightlabel rightlabel.textcolor = uicolor(r: knormalcolor.0 + colordelta.0 * ratio, g: knormalcolor.1 + colordelta.1 * ratio, b: knormalcolor.2 + colordelta.2 * ratio) } // 4.记录最新的index currentindex = leftlabelindex } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。