zoukankan      html  css  js  c++  java
  • iOS 页面滑动与标题切换颜色渐变的联动效果

    话不多说,直接上图,要实现类似如下效果。

    • 这个效果非常常见,这里着重讲讲核心代码

    封装顶部的PageTitleView

    封装构造函数

    • 封装构造函数,让别人在创建对象时,就传入其实需要显示的内容
      • frame:创建对象时确定了frame就可以直接设置子控件的位置和尺寸
      • isScrollEnable:是否可以滚动。某些地方该控件是可以滚动的。
      • titles:显示的所有标题
    1. // MARK:- 构造函数
    2. init(frame: CGRect, isScrollEnable : Bool, titles : [String]) {
    3. self.isScrollEnable = isScrollEnable
    4. self.titles = titles
    5. super.init(frame: frame)
    6. }

    设置UI界面

    • 设置UI界面
      • 添加UIScrollView,如果标题过多,则可以滚动
      • 初始化所有的Label,用于显示标题。并且给label添加监听手势
      • 添加顶部线和滑块的View

               实现相对来说比较简单,这里代码从略

    封装底部的PageCotentView

    封装构造函数

    • 封装构造函数,让别人在创建对象时,就传入其实需要显示的内容
      • 所有用于显示在UICollectionView的Cell的所有控制器
      • 控制器的父控制器
    1. // MARK:- 构造函数
    2. init(frame: CGRect, childVcs : [UIViewController], parentViewController : UIViewController) {
    3. self.childVcs = childVcs
    4. self.parentViewController = parentViewController
    5. super.init(frame: frame)
    6. }

    设置UI界面内容

    • 设置UI界面
      • 将所有的子控制器添加到父控制器中
      • 添加UICollectionView,用于展示内容
    1. // 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中设置当前应该滚动的位置
    1. // MARK:- 对外暴露方法
    2. extension PageContentView {
    3. func scrollToIndex(index : Int) {
    4. let offset = CGPoint(x: CGFloat(index) * collectionView.bounds.width, y: 0)
    5. collectionView.setContentOffset(offset, animated: false)
    6. }
    7. }

    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
        }
    }
  • 相关阅读:
    7.21 高博教育 数组 内存
    【基础扎实】Python操作Excel三模块
    PAT 甲级 1012 The Best Rank
    PAT 甲级 1011  World Cup Betting
    PAT 甲级 1010 Radix
    链式线性表——实验及提升训练
    循环程序设计能力自测
    链表应用能力自测
    PAT 甲级 1009 Product of Polynomials
    1008 Elevator (20分)
  • 原文地址:https://www.cnblogs.com/imsz/p/6686367.html
Copyright © 2011-2022 走看看