iOS- 用Swift写的一个标题选项卡控件

学习Swift之后这个控件写了很久了. 但是把这个框架做成pod集成到项目中的时候发现因为控件的核心类没有声明"public"而导致无法访问. 在把class 前声明为Public后又有一堆协议方法也需要修改访问权限..(这个比OC有点麻烦了)

顺便整理记录一下这个框架吧
git: [https://github.com/Zafirzzf/ZFPageTitleView]

 /// 构造函数
    ///
    /// - Parameters:
    ///   - frame: 包含自控制器的整个frame
    ///   - titles: 标题数组
    ///   - childControllers: 内容控制器数组
    ///   - 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()
        
    }

在接收到最需要的标题数组和子控制器数组之后,创建标题视图,并用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)
        
        //内容
        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)
        
        //设置标题view和内容视图的关系
        titleView.delegage = contentView
        contentView.delegate = titleView
    }

ZFTitleViewZFContentView是抽离出来的两个类.分别控制标题视图与内容.titleView于contentView之间的事件交互利用代理完成很方便.

TitleView上根据标题的数量创建N个Button添加到scrollView上,根据标题的总长度调整位置即可.

在滑动下方contentView的时候上方标题视图中的当前选中文字与将要选中的文字有一个渐变的颜色变化.处理方式如下:

   public func contentView(_ contentView: ZFContentView, targetIndex: Int, progress: CGFloat) {

        if progress > 0.9 {
            selectIndex = targetIndex
            //调整位置
            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]
        
        // 滑动过程中缩放字体
        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("文字颜色请按照RGB设置")
        }
        return (components[0] * 255, components[1] * 255, components[2] * 255)
    }

核心步骤是:

  1. 获取normalcolor和selectcolor的两个RGB值.
  2. 两个值的R,G,B相减获取到过度的一个颜色值.
  3. 根据当前滑动的进度progress,使normalcolor和selectcolor向过度值进行过度
    注: 这种方法要确保Color.cgColor.components有值,所以两种颜色需要通过RGB进行设置.其它方法设置的会获取不到RGB值.

如果想要加上滑动过程中sel的标题进行缩放,同理根据progress操作.

contentView里有一个左右滑动的collectionView,它的cell就是childControllers里的每个控制器的view.这样在控制器比较多的情况下可以让view之间复用.比ScrollView会节省内存的占用.

需要注意的是为了防止复用.每次加载新view时要将之前的删除.

   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
    }

滑动contentView更新titleView的变化通过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)
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容