Created
May 25, 2015 00:00
-
-
Save mac389/0e2fbe441b1eef8538ae to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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