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
        }
    }
  • 相关阅读:
    2.2、Dstreams数据源之高级数据源
    配置git 环境变量
    AngularJS的 $resource服务 关于CRUD操作
    如何安装和使用Karma-Jasmine
    ui-router 留存
    angular的service与factory
    留存- angularjs 弹出框 $modal
    js 的eval()方法 计算某个字符串,并执行其中的的 JavaScript 代码;
    javascript函数作用域和提前声明
    npm 全局环境变量配置
  • 原文地址:https://www.cnblogs.com/imsz/p/6686367.html
Copyright © 2011-2022 走看看