美文网首页
iOS自定义控件:分段选择器-Swift

iOS自定义控件:分段选择器-Swift

作者: 今晚月色 | 来源:发表于2018-10-18 16:53 被阅读52次
    镇楼专用

    废话不多说,直接上代码

    1、数据源方法
    // MARK: ============ SegmentSelectorManagerDataSource ============
    @objc protocol  SegmentSelectorManagerDataSource:NSObjectProtocol {
        
        /// 主要配置 -> 必须要实现
        func nameOfSliderItems(segemntControl:SegmentSelectorManager) -> Array<String>
        func childViewControllers(segemntControl:SegmentSelectorManager) -> Array<UIViewController>
        
        /// 字体颜色配置 -> 有默认
        @objc optional func colorOfSlider(segemntControl:SegmentSelectorManager) -> UIColor
        @objc optional func colorOfTopView(segemntControl:SegmentSelectorManager) -> UIColor
        
        /// 背景颜色配置 -> 有默认
        @objc optional func colorOfSliderItemsTitle(segemntControl:SegmentSelectorManager) -> UIColor
        @objc optional func colorOfHighlightedSliderItemsTitle(segemntControl:SegmentSelectorManager) -> UIColor
        
        /// 高度配置 -> 有默认
        @objc optional func heightOfTopView(segemntControl:SegmentSelectorManager) -> CGFloat
        @objc optional func heightOfSlider(segemntControl:SegmentSelectorManager) -> CGFloat
    }
    
    2、代理方法
    // MARK: ============ SegmentSelectorManagerDelegate ============
    protocol SegmentSelectorManagerDelegate {
        func slideView(sliderView:SegmentSelectorManager, didSelectItemAtIndex:Int) -> Void
    }
    
    3、页面实现
    override init(frame: CGRect) {
            super.init(frame: frame)
        }
        
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        /// 数据源
        public weak var dataSource:SegmentSelectorManagerDataSource? {
            didSet {
                // 名称数组
                namesOfSlideItems = dataSource?.nameOfSliderItems(segemntControl: self)
                // 控制器数组
                childControllersArray = dataSource?.childViewControllers(segemntControl: self)
                
                if let ds = dataSource {
    
                    if (ds.responds(to: #selector(ds.colorOfHighlightedSliderItemsTitle(segemntControl:)))) {
                        //按钮字体颜色默认
                        colorOfSlideItemsTitle = ds.colorOfSliderItemsTitle!(segemntControl: self)
                    }
                
                    if (ds.responds(to: #selector(ds.colorOfHighlightedSliderItemsTitle(segemntControl:)))) {
                        // 按钮字体颜色选中
                        colorOfHighlightedSlideItemsTitle = ds.colorOfHighlightedSliderItemsTitle!(segemntControl: self)
                    }
                    
                    if (ds.responds(to: #selector(ds.colorOfSlider(segemntControl:)))) {
                        // 指示器颜色
                        colorOfSlider = ds.colorOfSlider!(segemntControl: self)
                    }
                    
                    if ds.responds(to: #selector(ds.heightOfTopView(segemntControl:))) {
                        // 顶部视图高度
                        heightOfTopView = (ds.heightOfTopView!(segemntControl: self))
                    }
                    
                    if ds.responds(to: #selector(ds.heightOfTopView(segemntControl:))) {
                        // 指示器高度
                        heightOfSlider = (ds.heightOfSlider!(segemntControl: self))
                    }
                }
            }
        }
        
        // 代理
        public var delegate:SegmentSelectorManagerDelegate?
        
        var namesOfSlideItems: Array<String>? = []                           // 名称数组
        var colorOfSlider: UIColor? =  UIColor.orange                        // 指示器颜色
        var colorOfSlideView: UIColor? = UIColor.white                       // 顶部视图颜色
        var colorOfSlideItemsTitle: UIColor? = UIColor.gray                  // 默认字体颜色
        var colorOfHighlightedSlideItemsTitle: UIColor? = UIColor.orange     // 选中字体颜色
        var heightOfTopView:CGFloat = 45                                     // 顶部视图高度
        var heightOfSlider:CGFloat = 2                                       // 指示器高度
        let SliderThanSliderView_WidthRatio:CGFloat = 1                      // 按钮和指示器宽度比
        var buttonsArray:Array<UIButton>? = []                               // 所有按钮的数组
        var childControllersArray:Array<UIViewController>? = []              // 控制器数组
        
        let SCREEN_WIDTH = UIScreen.main.bounds.size.width
        let SCREEN_HEIGHT = UIScreen.main.bounds.height
        
        /// 顶部View
        lazy var slideBar:UIView = {
            let view = UIView.init()
            view.backgroundColor = colorOfSlideView
            addSubview(view)
            view.translatesAutoresizingMaskIntoConstraints = false
            view.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
            view.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
            view.topAnchor.constraint(equalTo: topAnchor).isActive = true
            view.heightAnchor.constraint(equalToConstant: self.heightOfTopView).isActive = true
            return view
        }()
        
        // 底部指示器
        var slider:UIView?
        
        /// 控制器ScrollView
        lazy var contentScrollView:UIScrollView = {
            let sc = UIScrollView.init()
            sc.isDirectionalLockEnabled = true
            sc.backgroundColor = UIColor.white
            sc.isPagingEnabled = true
            sc.showsHorizontalScrollIndicator = false
            sc.delegate = self
            sc.bounces = false
            addSubview(sc)
            sc.translatesAutoresizingMaskIntoConstraints = false
            sc.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
            sc.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
            sc.topAnchor.constraint(equalTo: self.slideBar.bottomAnchor).isActive = true
            sc.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
            return sc
        }()
    
    4、绘制页面UI
        // 获取当前页面的控制器
        public func viewController()->UIViewController? {
            var nextResponder: UIResponder? = self
            repeat {
                nextResponder = nextResponder?.next
                if let viewController = nextResponder as? UIViewController {
                    return viewController
                }
            } while nextResponder != nil
            
            return nil
        }
    
    override func layoutSubviews() {
            super.layoutSubviews()
            addSlider()
            addButton()
            addContentScrollView()
        }
    /// 添加按钮到顶部视图
        func addButton() {
            
            let numberOfItems:Int = (namesOfSlideItems?.count)!
            let slideItemWidth = SCREEN_WIDTH / CGFloat(numberOfItems)
            let sliderWidth = slideItemWidth * SliderThanSliderView_WidthRatio
            let position_x = (slideItemWidth - sliderWidth) / 2.0
            
            for index in 0..<numberOfItems {
                let b = UIButton.init(type: .custom)
                b.tag = index
                b.setTitle(namesOfSlideItems![index], for: .normal)
                b.setTitleColor(colorOfSlideItemsTitle, for: .normal)
                b.titleLabel?.textAlignment = .center
                b.addTarget(self, action: #selector(buttonTouched(button:)), for: .touchUpInside)
                buttonsArray?.append(b)
                b.frame = CGRect(x: position_x + slideItemWidth * CGFloat(index),
                                 y: 5,
                                 width: sliderWidth,
                                 height: heightOfTopView-5)
                slideBar.addSubview(b)
                
                if index == 0 {
                    b.setTitleColor(colorOfHighlightedSlideItemsTitle!, for: .normal)
                }
            }
        }
        
        // 添加底部指示器到顶部视图
        func addSlider() {
            let slideItemWidth =  SCREEN_WIDTH / CGFloat((namesOfSlideItems?.count)!)
            let sliderWidth = slideItemWidth * SliderThanSliderView_WidthRatio
            let position_x = (slideItemWidth - sliderWidth) / 2.0
            slideBar.addSubview(sliderView(frame: CGRect(x: position_x,
                                                         y: heightOfTopView - heightOfSlider,
                                                         width: sliderWidth,
                                                         height: heightOfSlider)))
            slideBar.backgroundColor = colorOfSlideView
            slider?.backgroundColor = colorOfSlider
        }
        
         // 添加ScrollView
        func addContentScrollView() {
            contentScrollView.contentSize = CGSize(width: SCREEN_WIDTH * CGFloat((namesOfSlideItems?.count)!), height: 0)
    
            for (index) in (childControllersArray?.enumerated())! {
                index.element.view.frame = CGRect(x: CGFloat(index.offset) * SCREEN_WIDTH,
                                                  y: 0,
                                                  width: contentScrollView.frame.width,
                                                  height: contentScrollView.frame.height)
                contentScrollView.addSubview(index.element.view)
                viewController()?.addChild(index.element)
                index.element.didMove(toParent: viewController())
            }
        }
        
        // vc滚动动画
        func animateSlider(tag:Int) -> Void {
            contentScrollView.setContentOffset(CGPoint(x: SCREEN_WIDTH * CGFloat(tag), y: 0), animated: true)
        }
        
        // 指示器滚动动画
        func animateSliderToPosition(offset:CGPoint) -> Void {
            
            let slideItemWidth =  SCREEN_WIDTH / CGFloat((namesOfSlideItems?.count)!)
            let sliderWidth =  slideItemWidth * SliderThanSliderView_WidthRatio
            let position_x =  (slideItemWidth - sliderWidth) / 2.0
            
            let newFrame = CGRect(x: (offset.x / SCREEN_WIDTH) * slideItemWidth + position_x,
                                  y: (slider?.frame.origin.y)!,
                                  width: (slider?.frame.width)!,
                                  height: (slider?.frame.height)!)
            slider?.frame = newFrame
            
            for (index) in (buttonsArray?.enumerated())! {
                index.element.setTitleColor(colorOfSlideItemsTitle, for: .normal)
            }
            
            var buttonTag = 0
            let ratio = offset.x / SCREEN_WIDTH
            let tempRation = Int(ratio)
            
            let decimal:CGFloat = ratio - CGFloat(tempRation)
    
            if decimal >= 0.5 {
                buttonTag = Int(ratio) + 1
            } else {
                buttonTag = Int(ratio)
            }
            
            buttonsArray![buttonTag].setTitleColor(colorOfHighlightedSlideItemsTitle, for: .normal)
        }
        
        /// 根据frame初始化指示器
        func sliderView(frame:CGRect) -> UIView {
            slider = UIView.init(frame: frame)
            return slider!
        }
       
        /// 点击事件
        @objc func buttonTouched(button:UIButton) {
            delegate?.slideView(sliderView: self, didSelectItemAtIndex: button.tag)
            animateSlider(tag: tag)
            contentScrollView.setContentOffset(CGPoint(x: SCREEN_WIDTH * CGFloat(button.tag), y: 0), animated: true)
        }
    
    5、使用Extension实现UIScrollViewDidScrollDelegate
    // MARK: ============ UIScrollViewDidScrollDelegate ============
    extension SegmentSelectorManager:UIScrollViewDelegate {
        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            animateSliderToPosition(offset: scrollView.contentOffset)
        }
    }
    
    6、使用方法(实例代码)
    override func setupSubViewsProperties() {
            segmentView = SegmentSelectorManager.init(frame: CGRect.zero)
            segmentView?.delegate = self
            segmentView?.dataSource = self
            view.addSubview(segmentView!)
        }
        
        override func setupSubViewsConstrains() {
            if let seg = segmentView {
                seg.translatesAutoresizingMaskIntoConstraints = false
                seg.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
                seg.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
                seg.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -kTabBarHeight).isActive = true
                seg.topAnchor.constraint(equalTo: wd_navgationBar_normal.bottomAnchor).isActive = true
            }
        }
    

    extension HotPlayController: SegmentSelectorManagerDataSource {
    
        func nameOfSliderItems(segemntControl: SegmentSelectorManager) -> Array<String> {
            return ["正在热映", "即将上映"]
        }
        
        func childViewControllers(segemntControl: SegmentSelectorManager) -> Array<UIViewController> {
            return [HotPlayingController(), HotWillPlayViewController()]
        }
        
        func heightOfTopView(segemntControl: SegmentSelectorManager) -> CGFloat {
            return 45
        }
    
        func heightOfSlider(segemntControl: SegmentSelectorManager) -> CGFloat {
            return 3
        }
    
        func colorOfSlider(segemntControl slider: SegmentSelectorManager) -> UIColor {
            return UIColor.wd_init(r: 73, g: 73, b: 73)
        }
    
        func colorOfTopView(segemntControl: SegmentSelectorManager) -> UIColor {
            return UIColor.white
        }
    
        func colorOfSliderItemsTitle(segemntControl: SegmentSelectorManager) -> UIColor {
            return UIColor.gray
        }
    
        func colorOfHighlightedSliderItemsTitle(segemntControl: SegmentSelectorManager) -> UIColor {
            return UIColor.wd_init(r: 73, g: 73, b: 73)
        }
    }
    
    extension HotPlayController: SegmentSelectorManagerDelegate {
        func slideView(sliderView: SegmentSelectorManager, didSelectItemAtIndex: Int) {
            
        }
    }
    
    7、效果图
    效果图.gif

    相关文章

      网友评论

          本文标题:iOS自定义控件:分段选择器-Swift

          本文链接:https://www.haomeiwen.com/subject/ifsszftx.html