LTPageView.swift 9.18 KB
//
//  LTPageView.swift
//  LTScrollView
//
//  Created by 高刘通 on 2017/11/14.
//  Copyright © 2017年 LT. All rights reserved.
//

import UIKit

public typealias PageViewDidSelectIndexBlock = (LTPageView, Int) -> Void
public typealias AddChildViewControllerBlock = (Int, UIViewController) -> Void

@objc public protocol LTPageViewDelegate: class {
    @objc optional func glt_scrollViewDidScroll(_ scrollView: UIScrollView)
    @objc optional func glt_scrollViewWillBeginDragging(_ scrollView: UIScrollView)
    @objc optional func glt_scrollViewWillBeginDecelerating(_ scrollView: UIScrollView)
    @objc optional func glt_scrollViewDidEndDecelerating(_ scrollView: UIScrollView)
    @objc optional func glt_scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool)
    @objc optional func glt_scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView)
}

//MARK: LTPageView相关的接口见此处
@objc protocol LTPageViewHeaders {
    
    //MARK: 构造方法
    @objc init(frame: CGRect, currentViewController: UIViewController, viewControllers:[UIViewController], titles: [String], layout: LTLayout)
    
    //MARK: 选中了第几个位置
    @objc var didSelectIndexBlock: PageViewDidSelectIndexBlock? { get }
    
    //MARK: 添加完子控制器回调
    @objc var addChildVcBlock: AddChildViewControllerBlock? { get }
    
    //MARK: 点击切换滚动过程动画
    @objc var isClickScrollAnimation: Bool { get }
    
    //MARK: pageView代理
    @objc weak var delegate: LTPageViewDelegate? { get }
    
    //MARK: 自定义子view
    @objc func customLayoutItems(handle: (([LTPageTitleItemView], LTPageView) -> Void)?)
}

public class LTPageView: UIView, LTPageViewHeaders {
    
    //当前的控制器
    private weak var currentViewController: UIViewController?
    
    //控制器数组
    private var viewControllers: [UIViewController]
    
    //标题数组
    private var titles: [String]
    
    //设置默认布局,具体设置请查看LTLayout类
    private var layout: LTLayout
    
    //当前选中的位置
    private var glt_currentIndex: Int = 0;
    
    //选中了第几个位置
    @objc public var didSelectIndexBlock: PageViewDidSelectIndexBlock?
    
    //添加完子控制器回调
    @objc public var addChildVcBlock: AddChildViewControllerBlock?
    
    // 点击切换滚动过程动画
    @objc public var isClickScrollAnimation = false {
        didSet {
            titleView.isClickScrollAnimation = isClickScrollAnimation
        }
    }
    
    //pageView的scrollView左右滑动监听
    @objc public weak var delegate: LTPageViewDelegate?
    
    //自定义子view
    func customLayoutItems(handle: (([LTPageTitleItemView], LTPageView) -> Void)?) {
        handle?(titleView.allItemViews(), self)
    }
    
    private lazy var titleView: LTPageTitleView = {
        let titleView = LTPageTitleView(frame: CGRect(x: 0, y: 0, width: glt_width, height: layout.sliderHeight), titles: titles, layout: layout)
        titleView.backgroundColor = layout.titleViewBgColor
        return titleView
    }()
    
    private lazy var scrollView: UIScrollView = {
        let scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: glt_width, height: glt_height))
        scrollView.contentSize = CGSize(width: glt_width * CGFloat(self.titles.count), height: 0)
        scrollView.isPagingEnabled = true
        scrollView.delegate = self
        scrollView.bounces = layout.isShowBounces
        scrollView.isScrollEnabled = layout.isScrollEnabled
        scrollView.showsHorizontalScrollIndicator = layout.showsHorizontalScrollIndicator
        if #available(iOS 11.0, *) {
            scrollView.contentInsetAdjustmentBehavior = .never
        }
        return scrollView
    }()
    
    @objc required public init(frame: CGRect, currentViewController: UIViewController, viewControllers:[UIViewController], titles: [String], layout: LTLayout) {
        self.currentViewController = currentViewController
        self.viewControllers = viewControllers
        self.titles = titles
        self.layout = layout
        guard viewControllers.count == titles.count else {
            fatalError("控制器数量和标题数量不一致")
        }
        super.init(frame: frame)
        
        self.addSubview(self.scrollView)
        
        guard !layout.isSinglePageView else { return }
        
        addSubview(self.titleView)
        
        glt_createViewController(0)
        
        _makeupPageView(self.titleView)
    }
    
    /* 滚动到某个位置 */
    @objc public func scrollToIndex(index: Int)  {
        titleView.mainScrollView = scrollView
        titleView.scrollToIndex(index: index)
    }
    
    required public init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

extension LTPageView {
    
    final func _makeupPageView(_ titleView: LTPageTitleView) {
        
        //设置滑动view的代理为LTPageTitleView
        self.delegate = titleView
        
        //将当前的scrollView传递给titleView
        titleView.mainScrollView = scrollView
        
        //当titleView的选中位置传递给scrollView
        titleView.scrollIndexHandle = {[weak self] in
            guard let `self` = self else { return 0 }
            return self.currentIndex()
        }
        
        //通过pageView创建自子控制器
        titleView.glt_createViewControllerHandle = {[weak self] index in
            self?.glt_createViewController(index)
        }
        
        //选中某个位置的回调
        titleView.glt_didSelectTitleViewHandle = {[weak self] index in
            guard let `self` = self else { return }
            self.didSelectIndexBlock?(self, index)
        }
    }
    
    final func makeupPageView(_ pageView:LTPageView, _ titleView: LTPageTitleView) {
        //设置滑动view的代理为LTPageTitleView
        pageView.delegate = titleView
        titleView.mainScrollView = pageView.scrollView
        titleView.scrollIndexHandle = {[weak pageView] in
            return pageView?.currentIndex() ?? 0
        }
        titleView.glt_createViewControllerHandle = {[weak pageView] index in
            pageView?.glt_createViewController(index)
        }
        titleView.glt_didSelectTitleViewHandle = {[weak pageView] index in
            pageView?.didSelectIndexBlock?((pageView)!, index)
        }
    }
}

extension LTPageView {
    
    //仅内部调用创建控制器
    final func glt_createViewController(_ index: Int)  {
        
        //如果当前控制器不存在直接return
        guard let currentViewController = currentViewController else { return }
        
        let viewController = viewControllers[index]
        
        //判断是否包含当前控制器,如果包含无需要再次创建
        guard !currentViewController.children.contains(viewController) else { return }
        
        //设置viewController的frame
        let X = scrollView.glt_width * CGFloat(index)
        let Y = layout.isSinglePageView ? 0 : layout.sliderHeight
        let W = scrollView.glt_width
        let H = scrollView.glt_height
        viewController.view.frame = CGRect(x: X, y: Y, width: W, height: H)
        
        //将控制器的view添加到scrollView上
        scrollView.addSubview(viewController.view)
        
        //将控制器作为当前控制器的子控制器
        currentViewController.addChild(viewController)
                
        //抛出回调给外界
        addChildVcBlock?(index, viewController)
        
        guard let glt_scrollView = viewController.glt_scrollView else { return }
        
        if #available(iOS 11.0, *) {
            glt_scrollView.contentInsetAdjustmentBehavior = .never
        }else {
            viewController.automaticallyAdjustsScrollViewInsets = false
        }
        //修正外部scrollView的高
        glt_scrollView.glt_height = glt_scrollView.glt_height - Y
    }
    
    //获取当前位置
    private func currentIndex() -> Int {
        if scrollView.bounds.width == 0 || scrollView.bounds.height == 0 {
            return 0
        }
        let index = Int((scrollView.contentOffset.x + scrollView.bounds.width * 0.5) / scrollView.bounds.width)
        return max(0, index)
    }
}

//MARK: 提供给外界监听的方法
extension LTPageView: UIScrollViewDelegate {
    
    public func scrollViewDidScroll(_ scrollView: UIScrollView) {
        delegate?.glt_scrollViewDidScroll?(scrollView)
    }
    
    public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        delegate?.glt_scrollViewWillBeginDragging?(scrollView)
    }
    
    public func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
        delegate?.glt_scrollViewWillBeginDecelerating?(scrollView)
    }
    
    public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        delegate?.glt_scrollViewDidEndDecelerating?(scrollView)
    }
    
    public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        delegate?.glt_scrollViewDidEndDragging?(scrollView, willDecelerate: decelerate)
    }
    
    public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
        delegate?.glt_scrollViewDidEndScrollingAnimation?(scrollView)
    }
}