swift3自定义导航条(三)

//
//  TGPageView.swift
//  TGPageView
//
//  Created by targetcloud on 2017/3/22.
//  Copyright © 2017年 targetcloud. All rights reserved.
//

import UIKit

class TGPageView: UIView {

    fileprivate var titles : [String]
    fileprivate var childVCs : [UIViewController]
    fileprivate var parentVC : UIViewController
    fileprivate var titleStyle : TGPageStyle
    
    init(frame: CGRect,titles : [String],titleStyle : TGPageStyle,childVCs : [UIViewController],parentVC : UIViewController) {
        self.titles = titles
        self.childVCs = childVCs
        self.parentVC = parentVC
        self.titleStyle = titleStyle
        
        super.init(frame: frame)
        
        setupUI()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

extension TGPageView{
    fileprivate func setupUI(){
        let titleViewFrame = CGRect(x: 0, y: 0, width: bounds.width, height: titleStyle.titleViewHeight)
        let titleView = TGTitleView(frame:titleViewFrame,titles:titles,style:titleStyle)
        titleView.backgroundColor = UIColor.orange
        addSubview(titleView)
        
        let contentFrame = CGRect(x: 0, y: titleViewFrame.maxY, width: bounds.width, height: bounds.height - titleStyle.titleViewHeight)
        let contentView = TGContentView(frame: contentFrame, childVCs: childVCs, parentVC: parentVC)
        contentView.backgroundColor = .red
        addSubview(contentView)
        
        //titleView与contentView进行协作
        //MARK:- 代理 1
        titleView.delegate = contentView
        
        //MARK:- 代理的使用 1
        contentView.delegate = titleView
    }
}


//
//  TGTitleView.swift
//  TGPageView
//
//  Created by targetcloud on 2017/3/22.
//  Copyright © 2017年 targetcloud. All rights reserved.
//

import UIKit

//MARK:delegate 1
protocol TGTitleViewDelegate:class {//1 NSObjectProtocol 2 class 只能被类遵守
    func titleView(_ titleView :TGTitleView,targetIndex:Int)
}

class TGTitleView: UIView {

    //MARK:delegate 2
    weak var delegate : TGTitleViewDelegate?
    
    fileprivate var titles : [String]
    fileprivate var titleStyle : TGPageStyle
    
    fileprivate lazy var scrollView : UIScrollView = {
        let sv  = UIScrollView(frame:self.bounds)
        sv.showsHorizontalScrollIndicator = false
        sv.scrollsToTop = false//点击状态栏不要回到顶部
        return sv
    }()
    
    fileprivate var currentIndex = 0
    
    fileprivate lazy var titleLabels : [UILabel] = [UILabel]()
    
    fileprivate lazy var normalRGB :(CGFloat,CGFloat,CGFloat) = self.titleStyle.normalColor.getRGB()
    fileprivate lazy var selectRGB :(CGFloat,CGFloat,CGFloat) = self.titleStyle.selectColor.getRGB()
    fileprivate lazy var  deltaRGB :(CGFloat,CGFloat,CGFloat) = {
        let deltaR =  self.selectRGB.0 - self.normalRGB.0 
        let deltaG =  self.selectRGB.1 - self.normalRGB.1
        let deltaB =  self.selectRGB.2 - self.normalRGB.2
        return (deltaR,deltaG,deltaB)
    }()
    
    fileprivate lazy var bottomLine : UIView = {
        let bottomLine = UIView()
        bottomLine.backgroundColor = self.titleStyle.bottomLineColor
        return bottomLine
    }()
    
    fileprivate lazy var coverView : UIView = {
        let coverV = UIView()
        coverV.backgroundColor = self.titleStyle.coverBgColor
        coverV.alpha = self.titleStyle.coverAlpha
        return coverV
    }()
    
    init(frame: CGRect,titles : [String],style : TGPageStyle) {
        self.titles = titles
        self.titleStyle = style
        super.init(frame: frame)
        
        setupUI()
        
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

extension TGTitleView {
    fileprivate func setupUI(){
        addSubview(scrollView)
        setupLabels()
        setupBottomLine()
        setupCoverView()
    }
    
    private func setupCoverView(){
        if titleStyle.isShowCoverView{
            scrollView.insertSubview(coverView, at: 0)//scrollView.addSubview(coverView)
            guard let firstLabel = titleLabels.first else {
                return
            }

            //MARK:- 可滚动不可滚动 有下划线没有下划线 共4种组合情况的处理 1
            var coverX : CGFloat = titleStyle.isShowBottomLine ? bottomLine.frame.origin.x : firstLabel.frame.origin.x
            let coverY : CGFloat = (firstLabel.frame.height - titleStyle.coverHeight) * 0.5//let coverY : CGFloat = (titleStyle.titleViewHeight - titleStyle.coverHeight) * 0.5
            var coverW : CGFloat = titleStyle.isShowBottomLine ? bottomLine.frame.width : firstLabel.frame.width
            let coverH : CGFloat = titleStyle.coverHeight
            
            //没有滚动条且可滚动
            if !titleStyle.isShowBottomLine && titleStyle.isScrollEnable{
                coverX -= titleStyle.coverMargin
                coverW += titleStyle.coverMargin * 2
            }
            //没有滚动条且不可滚动
            if !titleStyle.isShowBottomLine && !titleStyle.isScrollEnable{//等分
                coverX += titleStyle.coverMargin
                coverW -= titleStyle.coverMargin * 2
            }
            
            coverView.frame = CGRect(x: coverX, y: coverY, width: coverW, height: coverH)
            coverView.layer.cornerRadius = titleStyle.coverRadius
            coverView.layer.masksToBounds = true
        }
    }
    
    private func setupBottomLine(){
        if titleStyle.isShowBottomLine {
            scrollView.addSubview(bottomLine)
            bottomLine.frame = titleLabels.first!.frame
            //MARK:- 等分情况下的滚动条 1
            if !titleStyle.isScrollEnable{
                bottomLine.frame.origin.x +=  titleStyle.bottomLineExtendWidth
                bottomLine.frame.size.width -=  2 * titleStyle.bottomLineExtendWidth
            }else{
                bottomLine.frame.origin.x -=  titleStyle.bottomLineExtendWidth
                bottomLine.frame.size.width +=  2 * titleStyle.bottomLineExtendWidth
            }
            bottomLine.frame.size.height = titleStyle.bottomLineHeight
            bottomLine.frame.origin.y = titleStyle.titleViewHeight - titleStyle.bottomLineHeight - titleStyle.bottomLineMargin
        }
    }
    
    private  func setupLabels(){
        for (i,title) in titles.enumerated(){
            let titleLabel = UILabel()
            titleLabel.text = title
            titleLabel.tag = i
            titleLabel.textAlignment = .center
            titleLabel.textColor = (i==0 ? titleStyle.selectColor : titleStyle.normalColor)
            titleLabel.font = titleStyle.titleFont
            scrollView.addSubview(titleLabel)
            
            let tapGes = UITapGestureRecognizer(target: self, action: #selector(titleLabelClick))
            titleLabel.addGestureRecognizer(tapGes)
            titleLabel.isUserInteractionEnabled = true
            
            titleLabels.append(titleLabel)
        }
        
        var labelW : CGFloat = bounds.width / CGFloat(titleLabels.count)
        let labelH : CGFloat = titleStyle.titleViewHeight
        var labelX : CGFloat = 0
        let labelY : CGFloat = 0
        
        for (i,titleLabel) in titleLabels.enumerated(){
            if titleStyle.isScrollEnable{
                //可以滚动
                let size = CGSize(width: CGFloat(MAXFLOAT), height: 0)
                let attributes = [NSFontAttributeName : titleStyle.titleFont]
                labelW =  (titleLabel.text! as NSString).boundingRect(with: size, options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: attributes, context: nil).width
                labelX = ( i==0 ? titleStyle.titleMargin * 0.5 : (titleLabels[i-1].frame.maxX + titleStyle.titleMargin ))
            }else{
                //不可以滚动 平分
                labelX = labelW * CGFloat(i)
            }
            titleLabel.frame = CGRect(x: labelX, y: labelY, width: labelW, height: labelH)
        }
        
        if titleStyle.isScrollEnable{
            scrollView.contentSize = CGSize(width: (titleLabels.last?.frame.maxX)! + titleStyle.titleMargin * 0.5, height: 0)
        }
        
        if titleStyle.isNeedTitleScale{
            titleLabels.first?.transform = CGAffineTransform(scaleX: titleStyle.scaleRange, y: titleStyle.scaleRange)
        }
    }
}

extension TGTitleView{
    @objc fileprivate func titleLabelClick(_ tapGes:UITapGestureRecognizer){
        guard let targetLabel = tapGes.view as? UILabel else{
            return
        }
        
        guard targetLabel.tag != currentIndex else {
            return
        }
        
        let sourceLabel = titleLabels[currentIndex]
        sourceLabel.textColor = titleStyle.normalColor
        targetLabel.textColor = titleStyle.selectColor
        currentIndex = targetLabel.tag
        
        //调整中间位置offset
        adjustLabelPos()
        
        //MARK:delegate 3
        delegate?.titleView(self, targetIndex: currentIndex)
        
        if titleStyle.isNeedTitleScale{
            UIView.animate(withDuration: 0.5, animations: {
                sourceLabel.transform = CGAffineTransform.identity
                targetLabel.transform = CGAffineTransform(scaleX: self.titleStyle.scaleRange, y: self.titleStyle.scaleRange)
            })
        }
        
        if titleStyle.isShowBottomLine {
            //x & width    bounds是等于frame与Transform比例计算得来的,bounds不等于frame的宽高
            UIView.animate(withDuration: 0.5, animations: {
                //MARK:- 等分情况下的滚动条 2
                if !self.titleStyle.isScrollEnable{
                    self.bottomLine.frame.origin.x = targetLabel.frame.origin.x +  self.titleStyle.bottomLineExtendWidth
                    self.bottomLine.frame.size.width = targetLabel.frame.width -  2 * self.titleStyle.bottomLineExtendWidth
                }else{
                    self.bottomLine.frame.origin.x = targetLabel.frame.origin.x -  self.titleStyle.bottomLineExtendWidth
                    self.bottomLine.frame.size.width = targetLabel.frame.width +  2 * self.titleStyle.bottomLineExtendWidth
                }
            })
        }
        
        //MARK:- 可滚动不可滚动 有下划线没有下划线 共4种组合情况的处理 2
        if titleStyle.isShowCoverView{
            UIView.animate(withDuration: 0.5, animations: {
                self.coverView.frame.origin.x = self.titleStyle.isShowBottomLine ? self.bottomLine.frame.origin.x : targetLabel.frame.origin.x
                self.coverView.frame.size.width = self.titleStyle.isShowBottomLine ? self.bottomLine.frame.width : targetLabel.frame.width
                //没有滚动条且可滚动
                if !self.titleStyle.isShowBottomLine && self.titleStyle.isScrollEnable{
                    self.coverView.frame.origin.x -= self.titleStyle.coverMargin
                    self.coverView.frame.size.width += self.titleStyle.coverMargin * 2
                }
                //没有滚动条且不可滚动
                if !self.titleStyle.isShowBottomLine && !self.titleStyle.isScrollEnable{//等分
                    self.coverView.frame.origin.x += self.titleStyle.coverMargin
                    self.coverView.frame.size.width -= self.titleStyle.coverMargin * 2
                }
            })
        }
    }
    
    fileprivate func adjustLabelPos(){
        guard titleStyle.isScrollEnable else {
            return
        }
        
        let targetLabel = titleLabels[currentIndex]
        var offset = targetLabel.center.x - scrollView.frame.width * 0.5
//        print(offset)
        if offset < 0{
            offset = 0
        }
        let maxOffset = scrollView.contentSize.width - scrollView.bounds.width
        if offset > maxOffset{
            offset = maxOffset
        }
        scrollView.setContentOffset(CGPoint(x:offset,y:0), animated: true)
    }
}

//MARK:- 代理的使用 2
extension TGTitleView:TGContentViewDelegate{
    func contentView(_ contentView: TGContentView, didEndScroll inIndex: Int) {
        currentIndex = inIndex
        //调整中间位置offset
        adjustLabelPos()
    }
    
    func contentView(_ contentView: TGContentView, sourceIndex: Int, targetIndex: Int, progress: CGFloat) {
        let sourceLabel = titleLabels[sourceIndex]
        let targetLabel = titleLabels[targetIndex]
        sourceLabel.textColor = UIColor(r: selectRGB.0 - deltaRGB.0 * progress, g: selectRGB.1 - deltaRGB.1 * progress, b: selectRGB.2 - deltaRGB.2 * progress)
        targetLabel.textColor = UIColor(r: normalRGB.0 + deltaRGB.0 * progress, g: normalRGB.1 + deltaRGB.1 * progress, b: normalRGB.2 + deltaRGB.2 * progress)
        
        if titleStyle.isNeedTitleScale{
            let deltaScale = titleStyle.scaleRange - 1.0
            sourceLabel.transform = CGAffineTransform(scaleX: titleStyle.scaleRange - deltaScale * progress, y: titleStyle.scaleRange - deltaScale * progress)
            targetLabel.transform = CGAffineTransform(scaleX: 1 + deltaScale * progress, y: 1 + deltaScale * progress)
        }
        
        let deltaWidth = targetLabel.frame.width - sourceLabel.frame.width
        let deltaX = targetLabel.frame.origin.x - sourceLabel.frame.origin.x
        
        //x & width
        if titleStyle.isShowBottomLine{
            //MARK:- 等分情况下的滚动条 3
            if !titleStyle.isScrollEnable{
                bottomLine.frame.origin.x = sourceLabel.frame.origin.x + deltaX * progress +  titleStyle.bottomLineExtendWidth
                bottomLine.frame.size.width = sourceLabel.frame.width + deltaWidth * progress -  2 * titleStyle.bottomLineExtendWidth
            }else{
                bottomLine.frame.origin.x = sourceLabel.frame.origin.x + deltaX * progress -  titleStyle.bottomLineExtendWidth
                bottomLine.frame.size.width = sourceLabel.frame.width + deltaWidth * progress +  2 * titleStyle.bottomLineExtendWidth
            }
        }
        
        //MARK:- 可滚动不可滚动 有下划线没有下划线 共4种组合情况的处理 3
        if titleStyle.isShowCoverView{
            coverView.frame.origin.x = titleStyle.isShowBottomLine ? bottomLine.frame.origin.x : (sourceLabel.frame.origin.x + deltaX * progress)
            coverView.frame.size.width = titleStyle.isShowBottomLine ? bottomLine.frame.width : (sourceLabel.frame.width + deltaWidth * progress)
            //没有滚动条且可滚动
            if !titleStyle.isShowBottomLine && titleStyle.isScrollEnable{
                coverView.frame.origin.x -= titleStyle.coverMargin
                coverView.frame.size.width += titleStyle.coverMargin * 2
            }
            //没有滚动条且不可滚动
            if !titleStyle.isShowBottomLine && !titleStyle.isScrollEnable{//等分
                coverView.frame.origin.x += titleStyle.coverMargin
                coverView.frame.size.width -= titleStyle.coverMargin * 2
            }
        }
        
    }
}

//
//  TGContentView.swift
//  TGPageView
//
//  Created by targetcloud on 2017/3/22.
//  Copyright © 2017年 targetcloud. All rights reserved.
//

import UIKit

private let kContentCellID = "kContentCellID"

//MARK:- delegate 11
protocol TGContentViewDelegate :class {
    func contentView(_ contentView:TGContentView,didEndScroll inIndex : Int)
    func contentView(_ contentView:TGContentView,sourceIndex : Int,targetIndex : Int,progress : CGFloat)
}

class TGContentView: UIView {

    //MARK:- delegate 22
    weak var delegate : TGContentViewDelegate?
    
    fileprivate var childVCs : [UIViewController]
    fileprivate var parentVC : UIViewController
    fileprivate var fromOffsetX : CGFloat = 0
    fileprivate var isTitleClickForbidSVDelegate : Bool = false
    
    fileprivate lazy var collectionView : UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.itemSize = self.bounds.size;
        layout.minimumLineSpacing = 0
        layout.minimumInteritemSpacing = 0
        layout.scrollDirection = .horizontal
        
        let cv = UICollectionView(frame: self.bounds, collectionViewLayout: layout)//闭包中用到当前对象中的所有属性不能省略self.
        cv.dataSource = self
        cv.delegate = self
        cv.register(UICollectionViewCell.self, forCellWithReuseIdentifier: kContentCellID)
        
        cv.isPagingEnabled = true
        cv.showsHorizontalScrollIndicator = false
//        cv.bounces = false
        cv.scrollsToTop = false//点击状态栏不要回到顶部
        return cv
    }()
    
    init(frame: CGRect,childVCs : [UIViewController],parentVC : UIViewController) {
        self.childVCs = childVCs
        self.parentVC = parentVC
        super.init(frame: frame)
        
        setupUI()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
        
    }

}

extension TGContentView{
    fileprivate func setupUI(){
        
        for childVC in childVCs{
            parentVC.addChildViewController(childVC)
        }
        addSubview(collectionView)
    }
}

extension TGContentView : UICollectionViewDataSource{
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return childVCs.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: kContentCellID, for: indexPath)
//        cell.backgroundColor = UIColor.randomColor()
        
        for subview in cell.contentView.subviews{
            subview.removeFromSuperview()
        }
        
        let childVC = childVCs[indexPath.item]
        cell.contentView.addSubview(childVC.view)
        
        return cell
    }
}

extension TGContentView : UICollectionViewDelegate{
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        scrollDidEndScroll()
    }
    
    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        scrollDidEndScroll()
    }
    
    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        //记录开始拖动的offset
        fromOffsetX = scrollView.contentOffset.x
        isTitleClickForbidSVDelegate = false
    }
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        //MARK:- delegate 33
        //点击标题会调用在TGContentView.swift中的TGTitleViewDelegate实现的代理方法func titleView(_ titleView: TGTitleView, targetIndex: Int),此时这个 func中的代码collectionView.scrollToItem又会调用scrollViewDidScroll
        //这时需要禁止其调用scrollViewDidScroll
//        guard !isTitleClickForbidSVDelegate else {
//            return
//        }
        
        let toOffsetX = scrollView.contentOffset.x
        guard !isTitleClickForbidSVDelegate && toOffsetX != fromOffsetX else {
            return
        }
        
        var sourceIndex = 0
        var targetIndex = 0
        var progress : CGFloat = 0
        let collectionViewWidth = collectionView.bounds.width
        
        if toOffsetX > fromOffsetX {
            //左划向右滚 (回弹偶尔会调用右划向左滚,所以越界都要判断)
            sourceIndex = Int(toOffsetX / collectionViewWidth)
            targetIndex = sourceIndex + 1
            if targetIndex >= childVCs.count{
                targetIndex = childVCs.count - 1
                print(" --- \(toOffsetX) 到尾了--- ");
            }
            progress = (toOffsetX - fromOffsetX) / collectionViewWidth
            if progress > 1 {
                progress = 1
            }
            if toOffsetX - fromOffsetX >= collectionViewWidth{
                targetIndex = sourceIndex
            }
            
            print("<<<<<<左划向右滚 -> sourceIndex:\(sourceIndex) -> targetIndex:\(targetIndex) progress:\(progress)")
            delegate?.contentView(self, sourceIndex: sourceIndex, targetIndex: targetIndex, progress: progress)
        }else{
            //右划向左滚(回调偶尔会调用左划向右滚)
            if toOffsetX<0 {
                print(" --- \(toOffsetX) 到头了--- ");
                return
            }
            
            targetIndex = Int(toOffsetX / collectionViewWidth)
            sourceIndex = targetIndex + 1
            if sourceIndex >= childVCs.count{//
                sourceIndex = childVCs.count - 1
            }
            progress = (fromOffsetX - toOffsetX) / collectionViewWidth
            if progress > 1 {
                progress = 1
            }
            //完全划过去
            if fromOffsetX - toOffsetX >= collectionViewWidth {
                sourceIndex = targetIndex
            }

            print(">>>>>>右划向左滚 <- targetIndex:\(targetIndex) <- sourceIndex:\(sourceIndex) progress:\(progress)")
            delegate?.contentView(self, sourceIndex: sourceIndex, targetIndex: targetIndex, progress: progress)
        }
    }
    
    private func scrollDidEndScroll(){
        //MARK:- delegate 33
        let index = Int( collectionView.contentOffset.x / collectionView.bounds.width )
        delegate?.contentView(self, didEndScroll: index)
    }
}

//MARK:- 代理 2
extension TGContentView:TGTitleViewDelegate{
    func titleView(_ titleView: TGTitleView, targetIndex: Int) {
        isTitleClickForbidSVDelegate = true
        let indexPath = IndexPath(item: targetIndex, section: 0)
        collectionView.scrollToItem(at: indexPath, at: UICollectionViewScrollPosition.centeredHorizontally, animated: false)
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值