Skip to content

Instantly share code, notes, and snippets.

@mac389
Created May 25, 2015 00:00
Show Gist options
  • Save mac389/0e2fbe441b1eef8538ae to your computer and use it in GitHub Desktop.
Save mac389/0e2fbe441b1eef8538ae to your computer and use it in GitHub Desktop.
//
// SecondViewController.swift
// swiftTest
//
// Created by Michael Chary on 5/24/15.
//
//
import UIKit
import CoreGraphics
class SecondViewController: UIViewController {
private var chart: Chart?
private let colorBarHeight: CGFloat = 100
private let useViewsLayer = true
override func viewDidLoad() {
super.viewDidLoad()
let frame = ExamplesDefaults.chartFrame(self.view.bounds)
let chartFrame = CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height - colorBarHeight)
let colorBar = ColorBar(frame: CGRectMake(0, chartFrame.origin.y + chartFrame.size.height, self.view.frame.size.width, self.colorBarHeight), c1: UIColor.redColor(), c2: UIColor.greenColor())
self.view.addSubview(colorBar)
let labelSettings = ChartLabelSettings(font: ExamplesDefaults.labelFont)
func toColor(percentage: CGFloat) -> UIColor {
return colorBar.colorForPercentage(percentage).colorWithAlphaComponent(0.6)
}
let chartPoints: [ChartPointBubble] = [
(2, 2, 100),
(2.1, 5, 250),
(4, 4, 200),
(2.3, 5, 150),
(6, 7, 120),
(8, 3, 50),
(2, 4.5, 80),
(2, 5.2, 50),
(2, 4, 100),
(2.7, 5.5, 200),
(1.7, 2.8, 150),
(4.4, 8, 120),
(5, 6.3, 250),
(6, 8, 100),
(4, 8.5, 200),
(8, 5, 200),
(8.5, 10, 150),
(9, 11, 120),
(10, 6, 100),
(11, 7, 100),
(11, 4, 200),
(11.5, 10, 150),
(12, 7, 120),
(12, 9, 250)
].map{ChartPointBubble(x: ChartAxisValueFloat(CGFloat($0), labelSettings: labelSettings), y: ChartAxisValueFloat(CGFloat($1)), diameterScalar: $2)}
let xValues = Array(stride(from: -2, through: 14, by: 2)).map {ChartAxisValueInt($0, labelSettings: labelSettings)}
let yValues = Array(stride(from: -2, through: 12, by: 2)).map {ChartAxisValueInt($0, labelSettings: labelSettings)}
let xModel = ChartAxisModel(axisValues: xValues, axisTitleLabel: ChartAxisLabel(text: "Semantic Dimension 1", settings: labelSettings))
let yModel = ChartAxisModel(axisValues: yValues, axisTitleLabel: ChartAxisLabel(text: "Semantic Dimension 2", settings: labelSettings))
let coordsSpace = ChartCoordsSpaceLeftBottomSingleAxis(chartSettings: ExamplesDefaults.chartSettings, chartFrame: chartFrame, xModel: xModel, yModel: yModel)
let (xAxis, yAxis, innerFrame) = (coordsSpace.xAxis, coordsSpace.yAxis, coordsSpace.chartInnerFrame)
let lineModel = ChartLineModel(chartPoints: chartPoints, lineColor: UIColor.redColor(), animDuration: 0.5, animDelay: 0)
let bubbleLayer = self.bubblesLayer(xAxis: xAxis, yAxis: yAxis, chartInnerFrame: innerFrame, chartPoints: chartPoints)
let guidelinesLayerSettings = ChartGuideLinesDottedLayerSettings(linesColor: UIColor.blackColor(), linesWidth: ExamplesDefaults.guidelinesWidth)
let guidelinesLayer = ChartGuideLinesDottedLayer(xAxis: xAxis, yAxis: yAxis, innerFrame: innerFrame, settings: guidelinesLayerSettings)
let guidelinesHighlightLayerSettings = ChartGuideLinesDottedLayerSettings(linesColor: UIColor.blackColor(), linesWidth: 1, dotWidth: 4, dotSpacing: 4)
let guidelinesHighlightLayer = ChartGuideLinesForValuesDottedLayer(xAxis: xAxis, yAxis: yAxis, innerFrame: innerFrame, settings: guidelinesHighlightLayerSettings, axisValuesX: [ChartAxisValueFloat(0)], axisValuesY: [ChartAxisValueFloat(0)])
var popups: [UIView] = []
var selectedView: MyBubbleView?
let bubbleViewGenerator = {(chartPointModel: ChartPointLayerModel, layer: ChartPointsLayer, chart: Chart) -> UIView? in
let (chartPoint, screenLoc) = (chartPointModel.chartPoint, chartPointModel.screenLoc)
let v = MyBubbleView(chartPoint: chartPoint, center: screenLoc, diameter: Env.iPad ? 50 : 30, cornerRadius: Env.iPad ? 24: 15, borderWidth: Env.iPad ? 2 : 1, font: ExamplesDefaults.fontWithSize(Env.iPad ? 14 : 8))
v.viewTapped = {view in
for p in popups {p.removeFromSuperview()}
selectedView?.selected = false
let w: CGFloat = Env.iPad ? 250 : 150
let h: CGFloat = Env.iPad ? 100 : 80
let x: CGFloat = {
let attempt = screenLoc.x - (w/2)
let leftBound: CGFloat = chart.bounds.origin.x
let rightBound = chart.bounds.size.width - 5
if attempt < leftBound {
return view.frame.origin.x
} else if attempt + w > rightBound {
return rightBound - w
}
return attempt
}()
let frame = CGRectMake(x, screenLoc.y - (h + (Env.iPad ? 30 : 12)), w, h)
let bubbleView = BubbleView(frame: frame, arrowWidth: Env.iPad ? 40 : 28, arrowHeight: Env.iPad ? 20 : 14, bgColor: UIColor.blackColor(), arrowX: screenLoc.x - x)
chart.addSubview(bubbleView)
bubbleView.transform = CGAffineTransformConcat(CGAffineTransformMakeScale(0, 0), CGAffineTransformMakeTranslation(0, 100))
let infoView = UILabel(frame: CGRectMake(0, 10, w, h - 30))
infoView.textColor = UIColor.whiteColor()
infoView.backgroundColor = UIColor.blackColor()
infoView.text = "Some text about \(chartPoint.text)"
infoView.font = ExamplesDefaults.fontWithSize(Env.iPad ? 14 : 12)
infoView.textAlignment = NSTextAlignment.Center
bubbleView.addSubview(infoView)
popups.append(bubbleView)
UIView.animateWithDuration(0.2, delay: 0, options: UIViewAnimationOptions.allZeros, animations: {
view.selected = true
selectedView = view
bubbleView.transform = CGAffineTransformIdentity
}, completion: {finished in})
}
return v
}
let chart = Chart(
frame: chartFrame,
layers: [
xAxis,
yAxis,
guidelinesLayer,
guidelinesHighlightLayer,
bubbleLayer
]
)
self.view.addSubview(chart.view)
self.chart = chart
}
// We can use a view based layer for easy animation (or interactivity), in which case we use the default chart points layer with a generator to create bubble views.
// On the other side, if we don't need animation or want a better performance, we use ChartPointsBubbleLayer, which instead of creating views, renders directly to the chart's context.
private func bubblesLayer(#xAxis: ChartAxisLayer, yAxis: ChartAxisLayer, chartInnerFrame: CGRect, chartPoints: [ChartPointBubble]) -> ChartLayer {
let maxBubbleDiameter: CGFloat = 30, minBubbleDiameter: CGFloat = 2
if self.useViewsLayer == true {
return ChartPointsViewsLayer(xAxis: xAxis, yAxis: yAxis, innerFrame: chartInnerFrame, chartPoints: chartPoints, viewGenerator: {(chartPointModel, layer, chart) -> UIView? in
let (minDiameterScalar: CGFloat, maxDiameterScalar: CGFloat) = chartPoints.reduce((min: CGFloat(0), max: CGFloat(0))) {tuple, chartPoint in
(min: min(tuple.min, chartPoint.diameterScalar), max: max(tuple.max, chartPoint.diameterScalar))
}
let diameterFactor = (maxBubbleDiameter - minBubbleDiameter) / (maxDiameterScalar - minDiameterScalar)
let diameter = chartPointModel.chartPoint.diameterScalar * diameterFactor
let rect = CGRectMake(chartPointModel.screenLoc.x - diameter / 2, chartPointModel.screenLoc.y - diameter / 2, diameter, diameter)
return MyBubbleView(frame: rect, fillColor: chartPointModel.chartPoint.bgColor, borderColor: UIColor.blackColor().colorWithAlphaComponent(0.6), animDelay: 0.2, animDuration: 0.3)
})
} else {
return ChartPointsBubbleLayer(xAxis: xAxis, yAxis: yAxis, innerFrame: chartInnerFrame, chartPoints: chartPoints)
}
}
class MyBubbleView: UIView {
let fillColor: UIColor
let borderColor: UIColor
let borderWidth: CGFloat
let animDelay: Float
let animDuration: Float
let viewTapped: ((MyBubbleView) -> ())?
init(frame: CGRect, fillColor: UIColor, borderColor: UIColor, borderWidth: CGFloat = 1, animDelay: Float, animDuration: Float, corne) {
self.fillColor = fillColor
self.borderColor = borderColor
self.borderWidth = borderWidth
self.animDelay = animDelay
self.animDuration = animDuration
self.viewTapped = nil
self.layer.cornerRadius = cornerRadius
self.layer.borderWidth = borderWidth
self.textAlignment = NSTextAlignment.Center
self.layer.borderColor = UIColor.grayColor().CGColor
super.init(frame: CGRectInset(frame, -borderWidth, -borderWidth))
self.backgroundColor = UIColor.clearColor()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func drawRect(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
CGContextSetLineWidth(context, self.borderWidth)
CGContextSetStrokeColorWithColor(context, self.borderColor.CGColor)
CGContextSetFillColorWithColor(context, self.fillColor.CGColor)
let circleRect = (CGRectMake(self.borderWidth, self.borderWidth, self.frame.size.width - (self.borderWidth * 2), self.frame.size.height - (self.borderWidth * 2)))
CGContextFillEllipseInRect(context, circleRect)
CGContextStrokeEllipseInRect(context, circleRect)
}
override func didMoveToSuperview() {
self.transform = CGAffineTransformMakeScale(0.1, 0.1)
self.alpha = 0
UIView.animateWithDuration(NSTimeInterval(self.animDuration), delay: NSTimeInterval(self.animDelay), usingSpringWithDamping: 0.4, initialSpringVelocity: 0.5, options: UIViewAnimationOptions.allZeros, animations: { () -> Void in
self.transform = CGAffineTransformMakeScale(1, 1)
self.alpha = 1
}, completion: nil)
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
viewTapped?(self)
}
}
class ColorBar: UIView {
let dividers: [CGFloat]
let gradientImg: UIImage
lazy var imgData: UnsafePointer<UInt8> = {
let provider = CGImageGetDataProvider(self.gradientImg.CGImage)
let pixelData = CGDataProviderCopyData(provider)
return CFDataGetBytePtr(pixelData)
}()
init(frame: CGRect, c1: UIColor, c2: UIColor) {
var gradient: CAGradientLayer = CAGradientLayer()
gradient.frame = CGRectMake(0, 0, frame.width, 30)
gradient.colors = [UIColor.blueColor().CGColor, UIColor.cyanColor().CGColor, UIColor.yellowColor().CGColor, UIColor.redColor().CGColor]
gradient.startPoint = CGPointMake(0, 0.5)
gradient.endPoint = CGPointMake(1.0, 0.5)
let imgHeight = 1
let imgWidth = Int(gradient.bounds.size.width)
let bitmapBytesPerRow = imgWidth * 4
let bitmapByteCount = bitmapBytesPerRow * imgHeight
let colorSpace:CGColorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(CGImageAlphaInfo.PremultipliedLast.rawValue)
let context = CGBitmapContextCreate (nil,
imgWidth,
imgHeight,
8,
bitmapBytesPerRow,
colorSpace,
bitmapInfo)
UIGraphicsBeginImageContext(gradient.bounds.size)
gradient.renderInContext(context)
let gradientImg = UIImage(CGImage: CGBitmapContextCreateImage(context))!
UIGraphicsEndImageContext()
self.gradientImg = gradientImg
let segmentSize = gradient.frame.size.width / 6
self.dividers = Array(stride(from: segmentSize, through: gradient.frame.size.width, by: segmentSize))
super.init(frame: frame)
self.layer.insertSublayer(gradient, atIndex: 0)
let numberFormatter = NSNumberFormatter()
numberFormatter.maximumFractionDigits = 2
for x in stride(from: segmentSize, through: gradient.frame.size.width - 1, by: segmentSize) {
let dividerW: CGFloat = 1
let divider = UIView(frame: CGRectMake(x - dividerW / 2, 25, dividerW, 5))
divider.backgroundColor = UIColor.blackColor()
self.addSubview(divider)
let text = "\(numberFormatter.stringFromNumber(x / gradient.frame.size.width)!)"
let labelWidth = ChartUtils.textSize(text, font: ExamplesDefaults.labelFont).width
let label = UILabel()
label.center = CGPointMake(x - labelWidth / 2, 30)
label.font = ExamplesDefaults.labelFont
label.text = text
label.sizeToFit()
self.addSubview(label)
}
}
func colorForPercentage(percentage: CGFloat) -> UIColor {
let data = self.imgData
let xNotRounded = self.gradientImg.size.width * percentage
let x = 4 * (floor(abs(xNotRounded / 4)))
let pixelIndex = Int(x * 4)
let color = UIColor(
red: CGFloat(data[pixelIndex + 0]) / 255.0,
green: CGFloat(data[pixelIndex + 1]) / 255.0,
blue: CGFloat(data[pixelIndex + 2]) / 255.0,
alpha: CGFloat(data[pixelIndex + 3]) / 255.0
)
return color
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment