From c7d11bffeb9cfd3f194a81a5b0c1ea644fe7170d Mon Sep 17 00:00:00 2001 From: Barak Harel Date: Mon, 16 Apr 2018 15:23:36 -0700 Subject: [PATCH 1/2] added support for non-circle dots and opacity fade --- Example/ViewController.swift | 22 ++++++- Sources/ISPageControl.swift | 112 ++++++++++++++++++++++++++++------- 2 files changed, 111 insertions(+), 23 deletions(-) diff --git a/Example/ViewController.swift b/Example/ViewController.swift index 772ff50..5e62718 100644 --- a/Example/ViewController.swift +++ b/Example/ViewController.swift @@ -14,21 +14,36 @@ class ViewController: UIViewController { @IBOutlet weak var collectionView: UICollectionView! @IBOutlet weak var pageControl: ISPageControl! var pageControl1: ISPageControl! + var pageControl2: ISPageControl! let images: [UIImage] = [#imageLiteral(resourceName: "bg1"), #imageLiteral(resourceName: "bg2"), #imageLiteral(resourceName: "bg3"), #imageLiteral(resourceName: "bg4"), #imageLiteral(resourceName: "bg5"), #imageLiteral(resourceName: "bg6"), #imageLiteral(resourceName: "bg7"), #imageLiteral(resourceName: "bg8")] override func viewDidLoad() { super.viewDidLoad() pageControl.numberOfPages = images.count - let frame = CGRect(x: 0, y: 500, width: UIScreen.main.bounds.width, height: 100) - pageControl1 = ISPageControl(frame: frame, numberOfPages: images.count) - pageControl1.radius = 8 + let frame1 = CGRect(x: 0, y: 450, width: UIScreen.main.bounds.width, height: 100) + pageControl1 = ISPageControl(frame: frame1, numberOfPages: images.count) + pageControl1.dotRadius = 8 pageControl1.padding = 10 pageControl1.inactiveTintColor = UIColor.white.withAlphaComponent(0.4) pageControl1.currentPageTintColor = UIColor.black pageControl1.borderWidth = 1 pageControl1.borderColor = UIColor.black.withAlphaComponent(0.4) view.addSubview(pageControl1) + + let frame2 = CGRect(x: 0, y: 550, width: UIScreen.main.bounds.width, height: 100) + pageControl2 = ISPageControl(frame: frame2, numberOfPages: images.count) + pageControl2.fadeType = .opacity + pageControl2.dotRadius = 3 + pageControl2.dotHeight = 6 + pageControl2.dotWidth = 30 + pageControl2.minOpacityValue = 0.2 + pageControl2.middleOpacityValue = 0.6 + pageControl2.inactiveTintColor = UIColor.white.withAlphaComponent(0.4) + pageControl2.currentPageTintColor = UIColor.black + pageControl2.borderWidth = 1 + pageControl2.borderColor = UIColor.black.withAlphaComponent(0.4) + view.addSubview(pageControl2) } } @@ -58,6 +73,7 @@ extension ViewController: UIScrollViewDelegate { let pageNumber = round(scrollView.contentOffset.x / scrollView.frame.size.width) pageControl.currentPage = Int(pageNumber) pageControl1.currentPage = Int(pageNumber) + pageControl2.currentPage = Int(pageNumber) } } diff --git a/Sources/ISPageControl.swift b/Sources/ISPageControl.swift index 04a8dc2..bef8619 100644 --- a/Sources/ISPageControl.swift +++ b/Sources/ISPageControl.swift @@ -9,10 +9,21 @@ import UIKit open class ISPageControl: UIControl { + + public enum FadeType { + case scale + case opacity + } + + open var fadeType: FadeType = .scale { + didSet { + setNeedsLayout() + } + } + fileprivate let limit = 5 fileprivate var fullScaleIndex = [0, 1, 2] fileprivate var dotLayers: [CALayer] = [] - fileprivate var diameter: CGFloat { return radius * 2 } fileprivate var centerIndex: Int { return fullScaleIndex[1] } open var currentPage = 0 { @@ -36,8 +47,32 @@ open class ISPageControl: UIControl { } } - @IBInspectable open var radius: CGFloat = 5 { + @IBInspectable open var dotRadius: CGFloat = 5 { + didSet { + if dotHeight < 2 * dotRadius { + dotHeight = 2 * dotRadius + } + if dotWidth < 2 * dotRadius { + dotWidth = 2 * dotRadius + } + updateDotLayersLayout() + } + } + + @IBInspectable open var dotWidth: CGFloat = 10 { + didSet { + if dotRadius > dotWidth / 2 { + dotRadius = dotWidth / 2 + } + updateDotLayersLayout() + } + } + + @IBInspectable open var dotHeight: CGFloat = 10 { didSet { + if dotRadius > dotHeight / 2 { + dotRadius = dotHeight / 2 + } updateDotLayersLayout() } } @@ -60,6 +95,18 @@ open class ISPageControl: UIControl { } } + @IBInspectable open var minOpacityValue: CGFloat = 0.4 { + didSet { + setNeedsLayout() + } + } + + @IBInspectable open var middleOpacityValue: CGFloat = 0.7 { + didSet { + setNeedsLayout() + } + } + @IBInspectable open var numberOfPages: Int = 0 { didSet { setupDotLayers() @@ -107,7 +154,7 @@ open class ISPageControl: UIControl { override open func sizeThatFits(_ size: CGSize) -> CGSize { let minValue = min(7, numberOfPages) - return CGSize(width: CGFloat(minValue) * diameter + CGFloat(minValue - 1) * padding, height: diameter) + return CGSize(width: CGFloat(minValue) * dotWidth + CGFloat(minValue - 1) * padding, height: dotHeight) } open override func layoutSubviews() { @@ -143,14 +190,14 @@ private extension ISPageControl { func updateDotLayersLayout() { let floatCount = CGFloat(numberOfPages) - let x = (bounds.size.width - diameter * floatCount - padding * (floatCount - 1)) * 0.5 - let y = (bounds.size.height - diameter) * 0.5 - var frame = CGRect(x: x, y: y, width: diameter, height: diameter) + let x = (bounds.size.width - dotWidth * floatCount - padding * (floatCount - 1)) * 0.5 + let y = (bounds.size.height - dotHeight) * 0.5 + var frame = CGRect(x: x, y: y, width: dotWidth, height: dotHeight) dotLayers.forEach { - $0.cornerRadius = radius + $0.cornerRadius = dotRadius $0.frame = frame - frame.origin.x += diameter + padding + frame.origin.x += dotWidth + padding } } @@ -160,7 +207,7 @@ private extension ISPageControl { dotLayers.enumerated().filter{ $0.offset != centerIndex }.forEach { let index = abs($0.offset - centerIndex) - let interval = $0.offset > centerIndex ? diameter + padding : -(diameter + padding) + let interval = $0.offset > centerIndex ? dotWidth + padding : -(dotWidth + padding) $0.element.position = CGPoint(x: centerLayer.position.x + interval * CGFloat(index), y: $0.element.position.y) } } @@ -171,20 +218,45 @@ private extension ISPageControl { return } - var transform = CGAffineTransform.identity - if !fullScaleIndex.contains($0.offset) { - var scaleValue: CGFloat = 0 - if abs($0.offset - first) == 1 || abs($0.offset - last) == 1 { - scaleValue = min(middleScaleValue, 1) - } else if abs($0.offset - first) == 2 || abs($0.offset - last) == 2 { - scaleValue = min(minScaleValue, 1) - } else { - scaleValue = 0 + switch self.fadeType { + + case .scale: + + var transform = CGAffineTransform.identity + if !fullScaleIndex.contains($0.offset) { + var scaleValue: CGFloat = 0 + if abs($0.offset - first) == 1 || abs($0.offset - last) == 1 { + scaleValue = min(middleScaleValue, 1) + } else if abs($0.offset - first) == 2 || abs($0.offset - last) == 2 { + scaleValue = min(minScaleValue, 1) + } else { + scaleValue = 0 + } + transform = transform.scaledBy(x: scaleValue, y: scaleValue) } - transform = transform.scaledBy(x: scaleValue, y: scaleValue) + + $0.element.setAffineTransform(transform) + + case .opacity: + + var opacity: CGFloat = 1 + if !fullScaleIndex.contains($0.offset) { + var scaleValue: CGFloat = 0 + if abs($0.offset - first) == 1 || abs($0.offset - last) == 1 { + scaleValue = min(middleOpacityValue, 1) + } else if abs($0.offset - first) == 2 || abs($0.offset - last) == 2 { + scaleValue = min(minOpacityValue, 1) + } else { + scaleValue = 0 + } + opacity = scaleValue + } + + $0.element.opacity = Float(opacity) + } - $0.element.setAffineTransform(transform) + } } From 536982451fe5f3700d474b2b8a2e8178f194a2d0 Mon Sep 17 00:00:00 2001 From: Barak Harel Date: Mon, 16 Apr 2018 15:41:08 -0700 Subject: [PATCH 2/2] allow fading both opacity and scale --- Example/ViewController.swift | 5 +++-- Sources/ISPageControl.swift | 16 ++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Example/ViewController.swift b/Example/ViewController.swift index 5e62718..c2662da 100644 --- a/Example/ViewController.swift +++ b/Example/ViewController.swift @@ -32,13 +32,14 @@ class ViewController: UIViewController { view.addSubview(pageControl1) let frame2 = CGRect(x: 0, y: 550, width: UIScreen.main.bounds.width, height: 100) - pageControl2 = ISPageControl(frame: frame2, numberOfPages: images.count) - pageControl2.fadeType = .opacity + pageControl2 = ISPageControl(frame: frame2, numberOfPages: images.count) pageControl2.dotRadius = 3 pageControl2.dotHeight = 6 pageControl2.dotWidth = 30 pageControl2.minOpacityValue = 0.2 pageControl2.middleOpacityValue = 0.6 + pageControl2.fadeOpacity = true + pageControl2.fadeScale = false pageControl2.inactiveTintColor = UIColor.white.withAlphaComponent(0.4) pageControl2.currentPageTintColor = UIColor.black pageControl2.borderWidth = 1 diff --git a/Sources/ISPageControl.swift b/Sources/ISPageControl.swift index bef8619..4439eb5 100644 --- a/Sources/ISPageControl.swift +++ b/Sources/ISPageControl.swift @@ -10,12 +10,13 @@ import UIKit open class ISPageControl: UIControl { - public enum FadeType { - case scale - case opacity + open var fadeScale: Bool = true { + didSet { + setNeedsLayout() + } } - open var fadeType: FadeType = .scale { + open var fadeOpacity: Bool = false { didSet { setNeedsLayout() } @@ -218,9 +219,7 @@ private extension ISPageControl { return } - switch self.fadeType { - - case .scale: + if fadeScale { var transform = CGAffineTransform.identity if !fullScaleIndex.contains($0.offset) { @@ -236,8 +235,9 @@ private extension ISPageControl { } $0.element.setAffineTransform(transform) + } - case .opacity: + if fadeOpacity { var opacity: CGFloat = 1 if !fullScaleIndex.contains($0.offset) {