Skip to content

Instantly share code, notes, and snippets.

@Jesse-calkin
Last active April 20, 2016 16:45
Show Gist options
  • Save Jesse-calkin/b85646f00ab9a32e7e33 to your computer and use it in GitHub Desktop.
Save Jesse-calkin/b85646f00ab9a32e7e33 to your computer and use it in GitHub Desktop.
An iOS version of the tabs used with Android's view pager, based on a UISegmentedControl
//
// ViewPagerTabs.swift
//
// Created by jesse calkin on 3/14/16.
// Copyright © 2016 Shoshin Boogie. All rights reserved.
//
import UIKit
class ViewPager: UISegmentedControl {
//MARK: - Properties
//MARK: Internal
@IBInspectable var highlightColor: UIColor?
@IBInspectable var activeTextColor: UIColor = UIColor.whiteColor() {
didSet {
setTitleTextAttributes([NSForegroundColorAttributeName: activeTextColor], forState: UIControlState.Selected)
}
}
@IBInspectable var inactiveTextColor: UIColor = UIColor.lightGrayColor() {
didSet {
setTitleTextAttributes([NSForegroundColorAttributeName: inactiveTextColor], forState: UIControlState.Normal)
}
}
@IBInspectable var highlightHeight: CGFloat = 2.0
@IBInspectable var hightlightInset: CGFloat = 0.0
var segmentSpacing: CGFloat = 1.0
override var selectedSegmentIndex: Int {
didSet {
animateSelection()
}
}
//MARK: Private
private let lineLayer = CAShapeLayer()
private var lastPath: CGPath?
//MARK: - View Lifecycle
override init(items: [AnyObject]?) {
super.init(items: items)
setupStyle()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupStyle()
}
override init(frame: CGRect) {
super.init(frame: frame)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesEnded(touches, withEvent: event)
animateSelection()
sendActionsForControlEvents(UIControlEvents.TouchUpInside)
}
override func drawRect(rect: CGRect) {
super.drawRect(rect)
animateSelection()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setupStyle()
animateSelection()
}
//MARK: - Private
private func animateSelection() {
let width = widthForSegmentAtIndex(selectedSegmentIndex) > 0.0 ? widthForSegmentAtIndex(selectedSegmentIndex) : bounds.width / CGFloat(numberOfSegments)
let startPoint = CGPoint(x: width * CGFloat(selectedSegmentIndex) + hightlightInset, y: bounds.maxY - 2)
let endPoint = CGPoint(x: width * CGFloat(selectedSegmentIndex) + (width - hightlightInset), y: bounds.maxY - 2)
let path = UIBezierPath()
path.moveToPoint(startPoint)
path.addLineToPoint(endPoint)
lineLayer.strokeColor = highlightColor?.CGColor ?? tintColor.CGColor
lineLayer.lineWidth = highlightHeight
lineLayer.path = path.CGPath
let lineAnimation = CABasicAnimation(keyPath: "path")
lineAnimation.fromValue = lastPath
lineAnimation.toValue = path.CGPath
lineAnimation.duration = AnimationTime.Default
lineAnimation.fillMode = kCAFillModeForwards
lineAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
lineLayer.addAnimation(lineAnimation, forKey: "ViewPagerLineAnimation")
lastPath = lineLayer.path
}
private func setupStyle() {
layer.addSublayer(lineLayer)
let colorImage = UIImage.imageWithColor(UIColor.clearColor())
momentary = false
setDividerImage(colorImage, forLeftSegmentState: UIControlState.Normal, rightSegmentState: UIControlState.Normal, barMetrics: UIBarMetrics.Default)
setBackgroundImage(colorImage, forState: .Normal, barMetrics: .Default)
setBackgroundImage(colorImage, forState: .Selected, barMetrics: .Default)
setBackgroundImage(colorImage, forState: .Highlighted, barMetrics: .Default)
setTitleTextAttributes([NSForegroundColorAttributeName: inactiveTextColor], forState: UIControlState.Normal)
setTitleTextAttributes([NSForegroundColorAttributeName: activeTextColor], forState: UIControlState.Selected)
}
}
//MARK: - UIImage Extension
extension UIImage {
class func imageWithColor(color: UIColor, frame: CGRect = CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0)) -> UIImage {
UIGraphicsBeginImageContextWithOptions(frame.size, false, 0.0)
let context = UIGraphicsGetCurrentContext()
CGContextSetFillColorWithColor(context, color.CGColor)
CGContextFillRect(context, frame)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment