暫無描述

LLSnakePageControl.swift 4.6KB

    // // LLSnakePageControl.swift // LL 使用备注 // https://github.com/popwarsweet/PageControls // // Created by Kyle Zaragoza on 8/5/16. // Copyright © 2016 Kyle Zaragoza. All rights reserved. // import UIKit open class LLSnakePageControl: UIView { // MARK: - PageControl open var pageCount: Int = 0 { didSet { updateNumberOfPages(pageCount) } } open var progress: CGFloat = 0 { didSet { layoutActivePageIndicator(progress) } } open var currentPage: Int { return Int(round(progress)) } // MARK: - Appearance open var activeTint: UIColor = UIColor.white { didSet { activeLayer.backgroundColor = activeTint.cgColor } } open var inactiveTint: UIColor = UIColor(white: 1, alpha: 0.3) { didSet { inactiveLayers.forEach { $0.backgroundColor = inactiveTint.cgColor } } } open var indicatorPadding: CGFloat = 10 { didSet { layoutInactivePageIndicators(inactiveLayers) } } open var indicatorRadius: CGFloat = 5 { didSet { layoutInactivePageIndicators(inactiveLayers) } } fileprivate var indicatorDiameter: CGFloat { return indicatorRadius * 2 } fileprivate var inactiveLayers = [CALayer]() fileprivate lazy var activeLayer: CALayer = { [unowned self] in let layer = CALayer() layer.frame = CGRect(origin: CGPoint.zero, size: CGSize(width: self.indicatorDiameter, height: self.indicatorDiameter)) layer.backgroundColor = self.activeTint.cgColor layer.cornerRadius = self.indicatorRadius layer.actions = [ "bounds": NSNull(), "frame": NSNull(), "position": NSNull()] return layer }() override public init(frame: CGRect) { super.init(frame: frame) pageCount = 0 progress = 0 indicatorPadding = 8 indicatorRadius = 4 } required public init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: - State Update fileprivate func updateNumberOfPages(_ count: Int) { // no need to update guard count != inactiveLayers.count else { return } // reset current layout inactiveLayers.forEach { $0.removeFromSuperlayer() } inactiveLayers = [CALayer]() // add layers for new page count inactiveLayers = stride(from: 0, to:count, by:1).map { _ in let layer = CALayer() layer.backgroundColor = self.inactiveTint.cgColor self.layer.addSublayer(layer) return layer } layoutInactivePageIndicators(inactiveLayers) // ensure active page indicator is on top self.layer.addSublayer(activeLayer) layoutActivePageIndicator(progress) self.invalidateIntrinsicContentSize() } // MARK: - Layout fileprivate func layoutActivePageIndicator(_ progress: CGFloat) { // ignore if progress is outside of page indicators' bounds guard progress >= 0 && progress <= CGFloat(pageCount - 1) else { return } let denormalizedProgress = progress * (indicatorDiameter + indicatorPadding) let distanceFromPage = abs(round(progress) - progress) var newFrame = activeLayer.frame let widthMultiplier = (1 + distanceFromPage*2) newFrame.origin.x = denormalizedProgress newFrame.size.width = newFrame.height * widthMultiplier activeLayer.frame = newFrame } fileprivate func layoutInactivePageIndicators(_ layers: [CALayer]) { let layerDiameter = indicatorRadius * 2 var layerFrame = CGRect(x: 0, y: 0, width: layerDiameter, height: layerDiameter) layers.forEach { layer in layer.cornerRadius = self.indicatorRadius layer.frame = layerFrame layerFrame.origin.x += layerDiameter + indicatorPadding } // 布局 let oldFrame = self.frame let width = CGFloat(inactiveLayers.count) * indicatorDiameter + CGFloat(inactiveLayers.count - 1) * indicatorPadding self.frame = CGRect.init(x: UIScreen.main.bounds.width / 2 - width / 2, y: oldFrame.origin.y, width: width, height: oldFrame.size.height) } override open var intrinsicContentSize: CGSize { return sizeThatFits(CGSize.zero) } override open func sizeThatFits(_ size: CGSize) -> CGSize { return CGSize(width: CGFloat(inactiveLayers.count) * indicatorDiameter + CGFloat(inactiveLayers.count - 1) * indicatorPadding, height: indicatorDiameter) } }