danielgindi/Charts how to get xLabel position - ios

I was able to get the position of the x Label from xAxisRenderer. But I failed to import this into my project. I'm trying to get this position and create a custom x-axis. How can I store a value in an external module and use it in an internal module?
#objc open func drawLabels(context: CGContext, pos: CGFloat, anchor: CGPoint)
{
guard let transformer = self.transformer else { return }
let paraStyle = ParagraphStyle.default.mutableCopy() as! MutableParagraphStyle
paraStyle.alignment = .center
let labelAttrs: [NSAttributedString.Key : Any] = [.font: axis.labelFont,
.foregroundColor: axis.labelTextColor,
.paragraphStyle: paraStyle]
let labelRotationAngleRadians = axis.labelRotationAngle.DEG2RAD
let isCenteringEnabled = axis.isCenterAxisLabelsEnabled
let valueToPixelMatrix = transformer.valueToPixelMatrix
var position = CGPoint.zero
var labelMaxSize = CGSize.zero
if axis.isWordWrapEnabled
{
labelMaxSize.width = axis.wordWrapWidthPercent * valueToPixelMatrix.a
}
let entries = axis.entries
for i in entries.indices
{
let px = isCenteringEnabled ? CGFloat(axis.centeredEntries[i]) : CGFloat(entries[i])
position = CGPoint(x: px, y: 0)
.applying(valueToPixelMatrix)
guard viewPortHandler.isInBoundsX(position.x) else { continue }
let label = axis.valueFormatter?.stringForValue(axis.entries[i], axis: axis) ?? ""
let labelns = label as NSString
if axis.isAvoidFirstLastClippingEnabled
{
// avoid clipping of the last
if i == axis.entryCount - 1 && axis.entryCount > 1
{
let width = labelns.boundingRect(with: labelMaxSize, options: .usesLineFragmentOrigin, attributes: labelAttrs, context: nil).size.width
if width > viewPortHandler.offsetRight * 2.0,
position.x + width > viewPortHandler.chartWidth
{
position.x -= width / 2.0
}
}
else if i == 0
{ // avoid clipping of the first
let width = labelns.boundingRect(with: labelMaxSize, options: .usesLineFragmentOrigin, attributes: labelAttrs, context: nil).size.width
position.x += width / 2.0
}
}
print(position.x)
drawLabel(context: context,
formattedLabel: label,
x: position.x,
y: pos,
attributes: labelAttrs,
constrainedTo: labelMaxSize,
anchor: anchor,
angleRadians: labelRotationAngleRadians)
}
}
I tried to get the position value by creating an open class called XAxisPosition, but the value is initialized to 0 in the process of importing it into my project module. I have access to it, but how do I get the values ​​fully into my project?

public protocol XAxisRendererDelegate
{
#objc optional func renderer(_ renderer: XAxisRenderer,
labelPostion: CGPoint)
}
#objc open weak var delegate: XAxisRendererDelegate?
I added this XAxisRenderer.
and add this in drawLabels.
let px = isCenteringEnabled ? CGFloat(axis.centeredEntries[i]) :
CGFloat(entries[i])
position = CGPoint(x: px, y: 0)
.applying(valueToPixelMatrix)
delegate?.renderer?(self, labelPostion: position)
and use this
chartView.xAxisRenderer.delegate = self

Related

Wrong highlight annotation on apple PDFKit

I'm using PDFKit on iOS to highlight texts (PDF file). I do it by create a PDFAnnotation and add it to the selected text area. I want to highlight precisely the selected area but it always covers the whole line like the pics below. How can I create the annotation for the selected area only??
My code:
let highlight = PDFAnnotation(bounds: selectionText.bounds(for: page), forType: PDFAnnotationSubtype.highlight, withProperties: nil)
highlight.color = highlightColor
page.addAnnotation(highlight)
As suggested in PDFKit Highlight Annotation: quadrilateralPoints you can use quadrilateralPoints to add different lines highlights to the same annotation.
func highlight() {
guard let selection = pdfView.currentSelection, let currentPage = pdfView.currentPage else {return}
let selectionBounds = selection.bounds(for: currentPage)
let lineSelections = selection.selectionsByLine()
let highlightAnnotation = PDFAnnotation(bounds: selectionBounds, forType: PDFAnnotationSubtype.highlight, withProperties: nil)
highlightAnnotation.quadrilateralPoints = [NSValue]()
for (index, lineSelection) in lineSelections.enumerated() {
let n = index * 4
let bounds = lineSelection.bounds(for: pdfView.currentPage!)
let convertedBounds = bounds.convert(to: selectionBounds.origin)
highlightAnnotation.quadrilateralPoints?.insert(NSValue(cgPoint: convertedBounds.topLeft), at: 0 + n)
highlightAnnotation.quadrilateralPoints?.insert(NSValue(cgPoint: convertedBounds.topRight), at: 1 + n)
highlightAnnotation.quadrilateralPoints?.insert(NSValue(cgPoint: convertedBounds.bottomLeft), at: 2 + n)
highlightAnnotation.quadrilateralPoints?.insert(NSValue(cgPoint: convertedBounds.bottomRight), at: 3 + n)
}
pdfView.currentPage?.addAnnotation(highlightAnnotation)
}
extension CGRect {
var topLeft: CGPoint {
get {
return CGPoint(x: self.origin.x, y: self.origin.y + self.height)
}
}
var topRight: CGPoint {
get {
return CGPoint(x: self.origin.x + self.width, y: self.origin.y + self.height)
}
}
var bottomLeft: CGPoint {
get {
return CGPoint(x: self.origin.x, y: self.origin.y)
}
}
func convert(to origin: CGPoint) -> CGRect {
return CGRect(x: self.origin.x - origin.x, y: self.origin.y - origin.y, width: self.width, height: self.height)
}
}
PDFSelection bounds(forPage:) method returns one rectangle to satisfy whole selection area. Is not the best solution in your case.
Try with selectionsByLine(), and add individual annotation for every rect, representing every single selected line in PDF. Example:
let selections = pdfView.currentSelection?.selectionsByLine()
// Simple scenario, assuming your pdf is single-page.
guard let page = selections?.first?.pages.first else { return }
selections?.forEach({ selection in
let highlight = PDFAnnotation(bounds: selection.bounds(for: page), forType: .highlight, withProperties: nil)
highlight.endLineStyle = .square
highlight.color = UIColor.orange.withAlphaComponent(0.5)
page.addAnnotation(highlight)
})

How to calculate the optimal label width for multiline text in swift

I'd like to create a method to calculate the optimal width of a multi-line label to attach several labels in a horizontal row of a fixed height.
With one line of text there is no problem:
let textAttributes: [String : Any] = [NSFontAttributeName: UIFont.preferredFont(forTextStyle: UIFontTextStyle.title2)]
let maximalWidth: CGFloat = text!.boundingRect(
with: CGSize(width: CGFloat.greatestFiniteMagnitude, height: height),
options: [NSStringDrawingOptions.usesLineFragmentOrigin],
attributes: textAttributes,
context: nil).size.width
As far as I understood, there is no option to indicate here, that I have several lines. This method works well in other direction when we calculate the height of the text with the fixed width. But I have the opposite goal.
As a variant, I can create a label based on the longest word (to be more precise, based on the widest word, as we can have several words with the same characters count, but different rendered width):
var sizeToReturn = CGSize()
let maxWordsCharacterCount = text?.maxWord.characters.count
let allLongWords: [String] = text!.wordList.filter {$0.characters.count == maxWordsCharacterCount}
var sizes: [CGFloat] = []
allLongWords.forEach {sizes.append($0.size(attributes: attributes).width)}
let minimalWidth = (sizes.max()! + constantElementsWidth)
I used here two String extensions to create words list and find all longest:
extension String {
var wordList: [String] {
return Array(Set(components(separatedBy: .punctuationCharacters).joined(separator: "").components(separatedBy: " "))).filter {$0.characters.count > 0}
}
}
extension String {
var maxWord: String {
if let max = self.wordList.max(by: {$1.characters.count > $0.characters.count}) {
return max
} else {return ""}
}
}
Not a bad option, but it looks ugly if we have the text that can't be fitted in three lines and that has several short words and one long word at the end. This long word, determined the width, will be just truncated. And more of that it looks not too good with 3 short words like:
Sell
the
car
Well, I have the minimum width, I have the maximum width. Perhaps, I can
go from maximum to minimum and catch when the label starts being truncated.
So I feel that there can be an elegant solution, but I'm stuck.
Hooray, I've found one of the possible solutions. You can use the code below in the playground:
import UIKit
import PlaygroundSupport
//: Just a view to launch playground timeline preview
let hostView = UIView(frame: CGRect(x: 0, y: 0, width: 320, height: 480))
hostView.backgroundColor = .lightGray
PlaygroundPage.current.liveView = hostView
// MARK: - Extensions
extension String {
var wordList: [String] {
return Array(Set(components(separatedBy: .punctuationCharacters).joined(separator: "").components(separatedBy: " "))).filter {$0.characters.count > 0}
}
}
extension String {
var longestWord: String {
if let max = self.wordList.max(by: {$1.characters.count > $0.characters.count}) {
return max
} else {return ""}
}
}
// MARK: - Mathod
func createLabelWithOptimalLabelWidth (
requestedHeight: CGFloat,
constantElementsWidth: CGFloat,
acceptableWidthForTextOfOneLine: CGFloat, //When we don't want the text to be shrinked
text: String,
attributes: [String:Any]
) -> UILabel {
let label = UILabel(frame: .zero)
label.attributedText = NSAttributedString(string: text, attributes: attributes)
let maximalLabelWidth = label.intrinsicContentSize.width
if maximalLabelWidth < acceptableWidthForTextOfOneLine {
label.frame = CGRect(origin: CGPoint.zero, size: CGSize(width: maximalLabelWidth, height: requestedHeight))
return label // We can go with this width
}
// Minimal width, calculated based on the longest word
let maxWordsCharacterCount = label.text!.longestWord.characters.count
let allLongWords: [String] = label.text!.wordList.filter {$0.characters.count == maxWordsCharacterCount}
var sizes: [CGFloat] = []
allLongWords.forEach {sizes.append($0.size(attributes: attributes).width)}
let minimalWidth = (sizes.max()! + constantElementsWidth)
// Height calculation
var flexibleWidth = maximalLabelWidth
var flexibleHeight = CGFloat()
var optimalWidth = CGFloat()
var optimalHeight = CGFloat()
while (flexibleHeight <= requestedHeight && flexibleWidth >= minimalWidth) {
optimalWidth = flexibleWidth
optimalHeight = flexibleHeight
flexibleWidth -= 1
flexibleHeight = label.attributedText!.boundingRect(
with: CGSize(width: flexibleWidth, height: CGFloat.greatestFiniteMagnitude),
options: [NSStringDrawingOptions.usesLineFragmentOrigin],
context: nil).size.height
print("Width: \(flexibleWidth)")
print("Height: \(flexibleHeight)")
print("_______________________")
}
print("Final Width: \(optimalWidth)")
print("Final Height: \(optimalHeight)")
label.frame = CGRect(origin: CGPoint.zero, size: CGSize(width: optimalWidth+constantElementsWidth, height: requestedHeight))
return label
}
// MARK: - Inputs
let text: String? = "Determine the fair price"//nil//"Select the appropriate payment method"//"Finalize the order" //"Sell the car"//"Check the payment method"
let font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.callout)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = .byWordWrapping
paragraphStyle.allowsDefaultTighteningForTruncation = true
let attributes: [String:Any] = [
NSFontAttributeName: font,
NSParagraphStyleAttributeName: paragraphStyle,
NSBaselineOffsetAttributeName: 0
]
if text != nil {
let label = createLabelWithOptimalLabelWidth(requestedHeight: 70, constantElementsWidth: 0, acceptableWidthForTextOfOneLine: 120, text: text!, attributes: attributes)
label.frame.width
label.frame.height
label.backgroundColor = .white
label.lineBreakMode = .byWordWrapping
label.numberOfLines = 3
hostView.addSubview(label)
}

How to create UITableViewCell with dynamically height change according to array coming from API

Its array data for only one cell view, I need to set product_name and total_quantity on table view cell.sometime total_quantity can be 2 sometime it can be 10. Just give me some basic idea so I can move forward.
[[{"total_quantity":"50","product_id":"13","product_name":"Prawns"},{"total_quantity":"13","product_id":"14","product_name":"Fish"},{"total_quantity":"57","product_id":"15","product_name":"Chicken
Breast"},{"total_quantity":"10","product_id":"16","product_name":"Beef"}],[{"total_quantity":"50","product_id":"13","product_name":"Prawns"},{"total_quantity":"13","product_id":"14","product_name":"Fish"},{"total_quantity":"57","product_id":"15","product_name":"Chicken
Breast"},{"total_quantity":"10","product_id":"16","product_name":"Beef"}]]
Screen shot
extension String {
func sizeWithGivenSize(_ size: CGSize,font: UIFont,paragraph: NSMutableParagraphStyle? = nil) -> CGSize {
var attributes = [String:AnyObject]()
attributes[NSFontAttributeName] = font
if let p = paragraph {
attributes[NSParagraphStyleAttributeName] = p
}
attributes[NSFontAttributeName] = font
return (self as NSString).boundingRect(with: size,
options: [.usesLineFragmentOrigin,.usesFontLeading],
attributes: attributes,
context: nil).size
}
Use this Method to get UILabel's Frame with different length string.
then to calculate Height form top to bottom.
for high performance, you'd better to do this at model.
For example:
func calculateFrame() -> Void {
let width = UIComponentsConst.screenWidth
var tmpFrame = ProjectDetailTeamCellLayout()
var maxX: CGFloat = 0
var maxY: CGFloat = tmpFrame.topSpace
if let titleText = title {
let titleSize = titleText.sizeWithGivenSize(MaxSize, font: ProjectDetailTeamCellLayout.titleFont, paragraph: nil)
tmpFrame.titleLabelFrame = CGRect.init(x: 15, y: maxY, width: titleSize.width, height: titleSize.height)
maxY = tmpFrame.titleLabelFrame!.maxY
maxX = tmpFrame.titleLabelFrame!.maxX
}
if let nameText = nameTitle, !nameText.isEmpty {
tmpFrame.grayLineFrame = CGRect(x: maxX + tmpFrame.itemSpace, y: tmpFrame.topSpace, width: 1, height: tmpFrame.titleLabelFrame?.height ?? 0)
let nameSize = nameText.sizeWithGivenSize(MaxSize, font: ProjectDetailTeamCellLayout.nameLabelFont, paragraph: nil)
let nameTmpFrame = CGRect(x: tmpFrame.grayLineFrame!.maxX + tmpFrame.itemSpace, y: tmpFrame.topSpace + (tmpFrame.titleLabelFrame?.height ?? nameSize.height ) / 2 - nameSize.height / 2, width: nameSize.width, height: nameSize.height)
tmpFrame.nameLabelFrame = nameTmpFrame
maxY = maxY > tmpFrame.nameLabelFrame!.maxY ? maxY : tmpFrame.nameLabelFrame!.maxY
}
if let subTitleText = subTitle, !subTitleText.isEmpty {
let subTitleSize = subTitleText.sizeWithGivenSize(CGSize(width: width - 45, height: CGFloat.greatestFiniteMagnitude), font: ProjectDetailTeamCellLayout.subTitleFont, paragraph: nil)
tmpFrame.subTitleLabelFrame = CGRect(x: 15, y: maxY + tmpFrame.lineSpace, width: subTitleSize.width, height: subTitleSize.height)
maxY = tmpFrame.subTitleLabelFrame!.maxY + 10
}
tmpFrame.height = maxY + 5
self.frameInfo = tmpFrame
}
try ?
Use something like this, if you want to change the height:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let object = myArrayDataset[indexPath.row]
//for example
if object.numberOfItems > 10 {
return 80.0
}
else {
return 40.0
}
}

How to pass arguments from one class into a UIView class? Swift

I have a UIView class in my app which plots a line graph. In there, I assign my graphPoints variables like so :
var graphPoints:[Int] = [1,2,3,5,7,9]
var graphPoints2:[Int] = [1,2,3,5,7,9]
What I want to do is pass an array of Int from another class and assign those variables, but I am not sure how to do it. Initially i put all my code into one func with array [Int] as parameters and called it from another class but it stopped plotting the graph altogether. How do i do this?
Here is my UIVIew GraphPlotter class code :
import UIKit
#IBDesignable class GraphPlotter: UIView {
var graphPoints:[Int] = [1,2,3,5,7,9]
var graphPoints2:[Int] = [1,2,3,5,7,9]
//1 - the properties for the gradient
var startColor: UIColor = UIColor.redColor()
var endColor: UIColor = UIColor.greenColor()
override func drawRect(rect: CGRect) {
let width = rect.width
let height = rect.height
//set up background clipping area
let path = UIBezierPath(roundedRect: rect,
byRoundingCorners: UIRectCorner.AllCorners,
cornerRadii: CGSize(width: 8.0, height: 8.0))
path.addClip()
//2 - get the current context
let context = UIGraphicsGetCurrentContext()
let colors = [startColor.CGColor, endColor.CGColor]
//3 - set up the color space
let colorSpace = CGColorSpaceCreateDeviceRGB()
//4 - set up the color stops
let colorLocations:[CGFloat] = [0.0, 1.0]
//5 - create the gradient
let gradient = CGGradientCreateWithColors(colorSpace,
colors,
colorLocations)
//6 - draw the gradient
var startPoint = CGPoint.zero
var endPoint = CGPoint(x:0, y:self.bounds.height)
CGContextDrawLinearGradient(context,
gradient,
startPoint,
endPoint,
[])
//calculate the x point
let margin:CGFloat = 40.0
let columnXPoint = { (column:Int) -> CGFloat in
//Calculate gap between points
let spacer = (width - margin*2 - 4) /
CGFloat((self.graphPoints.count - 1))
var x:CGFloat = CGFloat(column) * spacer
x += margin + 2
return x
}
// calculate the y point
let topBorder:CGFloat = 60
let bottomBorder:CGFloat = 50
let graphHeight = height - topBorder - bottomBorder
let maxValue = graphPoints2.maxElement()!
let columnYPoint = { (graphPoint2:Int) -> CGFloat in
var y:CGFloat = CGFloat(graphPoint2) /
CGFloat(maxValue) * graphHeight
y = graphHeight + topBorder - y // Flip the graph
return y
}
// draw the line graph
UIColor.flatTealColor().setFill()
UIColor.flatTealColor().setStroke()
//set up the points line
let graphPath = UIBezierPath()
//go to start of line
graphPath.moveToPoint(CGPoint(x:columnXPoint(0),
y:columnYPoint(graphPoints2[0])))
//add points for each item in the graphPoints array
//at the correct (x, y) for the point
for i in 1..<graphPoints.count {
let nextPoint = CGPoint(x:columnXPoint(i),
y:columnYPoint(graphPoints2[i]))
graphPath.addLineToPoint(nextPoint)
}
//Create the clipping path for the graph gradient
//1 - save the state of the context (commented out for now)
CGContextSaveGState(context)
//2 - make a copy of the path
let clippingPath = graphPath.copy() as! UIBezierPath
//3 - add lines to the copied path to complete the clip area
clippingPath.addLineToPoint(CGPoint(
x: columnXPoint(graphPoints.count - 1),
y:height))
clippingPath.addLineToPoint(CGPoint(
x:columnXPoint(0),
y:height))
clippingPath.closePath()
//4 - add the clipping path to the context
clippingPath.addClip()
let highestYPoint = columnYPoint(maxValue)
startPoint = CGPoint(x:margin, y: highestYPoint)
endPoint = CGPoint(x:margin, y:self.bounds.height)
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, [])
CGContextRestoreGState(context)
//draw the line on top of the clipped gradient
graphPath.lineWidth = 2.0
graphPath.stroke()
//Draw the circles on top of graph stroke
for i in 0..<graphPoints.count {
var point = CGPoint(x:columnXPoint(i), y:columnYPoint(graphPoints2[i]))
point.x -= 5.0/2
point.y -= 5.0/2
let circle = UIBezierPath(ovalInRect:
CGRect(origin: point,
size: CGSize(width: 5.0, height: 5.0)))
circle.fill()
let label = UILabel(frame: CGRectMake(0, 0, 200, 21))
label.center = CGPointMake(160, 284)
label.textAlignment = NSTextAlignment.Center
// label.text = "I'am a test label"
self.addSubview(label)
}
//Draw horizontal graph lines on the top of everything
let linePath = UIBezierPath()
//top line
linePath.moveToPoint(CGPoint(x:margin, y: topBorder))
linePath.addLineToPoint(CGPoint(x: width - margin,
y:topBorder))
//center line
linePath.moveToPoint(CGPoint(x:margin,
y: graphHeight/2 + topBorder))
linePath.addLineToPoint(CGPoint(x:width - margin,
y:graphHeight/2 + topBorder))
//bottom line
linePath.moveToPoint(CGPoint(x:margin,
y:height - bottomBorder))
linePath.addLineToPoint(CGPoint(x:width - margin,
y:height - bottomBorder))
let color = UIColor.flatTealColor()
color.setStroke()
linePath.lineWidth = 1.0
linePath.stroke()
}
}
DBController, func dosmth where I pass the array :
func dosmth(metadata: DBMetadata!) {
let documentsDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
let localFilePath = (documentsDirectoryPath as NSString).stringByAppendingPathComponent(metadata.filename)
var newarray = [Int]()
do{
let data = try String(contentsOfFile: localFilePath as String,
encoding: NSASCIIStringEncoding)
print(data)
newarray = data.characters.split(){$0 == ","}.map{
Int(String.init($0).stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()))!}
print(newarray)
}
catch let error { print(error) }
//Probably wrong
GraphPlotter().graphPoints = newarray
GraphPlotter().graphPoints2 = newarray
}
So your drawRect method is based on the two variables graphPoints and graphPoints2. Create a method whose job is to update the arrays of these two variables, and then invoke setNeedsDisplay - which will go on to redraw the view.
func plotGraphPoints(gpArray1 : [Int], andMorePoints gpArray2: [Int] ) {
print("Old Values", self.graphPoints)
self.graphPoints = gpArray1
self.graphPoints2 = gpArray2
print("New values", self.graphPoints)
self.setNeedsDisplay()
}
First, I'd set these up so that any update will redraw the view:
var graphPoints:[Int]? { didSet { setNeedsDisplay() } }
var graphPoints2:[Int]? { didSet { setNeedsDisplay() } }
Note, I made those optionals, because you generally want it to handle the absence of data with nil values rather than dummy values. This does assume, though, that you'll tweak your implementation to detect and handle these nil values, e.g., before you start drawing the lines, do a
guard graphPoints != nil && graphPoints2 != nil else { return }
But, I notice that this whole class is IBDesignable, in which case, you probably want a prepareForInterfaceBuilder that provides sample data:
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
graphPoints = [1,2,3,5,7,9]
graphPoints2 = [1,2,3,5,7,9]
}
Second, your other class needs to have a reference to this custom view.
If this "other" class is the view controller and you added the custom view via IB, you would just add a #IBOutlet for the custom view to this view controller. If you added this custom view programmatically, you'd just keep a reference to it in some property after adding it to the view hierarchy. But, however you added a reference to that view, say graphView, you'd just set these properties:
graphView.graphPoints = ...
graphView.graphPoints2 = ...
If this "other" class is something other than a view controller (and in discussion, it sounds like the class in question is a controller for processing of asynchronous DropBox API), you also need to give that class some mechanism to reference the view controller (and thus the custom view). You can accomplish this by either implementing a "completion handler pattern" or a "delegate-protocol" pattern.

Custom X Axis for BarChartView

I am using BarChartView from danielgindi/ios-charts charting library. So far I am able to get following BarChartView. My question, is there any way to Customize X Axis that way, it will only show min (left most value 37.03), max (right most value 37.31 which is hiding), value for green bar (37.17) and value for orange bar (37.18 which is hiding again). In a nutshell I want to hide some value from XAxis which is not important in my case.
This is an example: add a property called showOnlyLeftRightEnabled in YourChartXAxis class, then in your custom x axis renderer, overide internal override func drawLabels(context context: CGContext, pos: CGFloat, anchor: CGPoint).
To display the middle one's you need to figure out which bar it is, and convert the x Index to pixel values just like what I did for the left most and right most value via CGPointApplyAffineTransform(position, valueToPixelMatrix)
internal override func drawLabels(context context: CGContext, pos: CGFloat, anchor: CGPoint)
{
let paraStyle = NSParagraphStyle.defaultParagraphStyle().mutableCopy() as! NSMutableParagraphStyle
paraStyle.alignment = .Center
let labelAttrs = [NSFontAttributeName: _xAxis.labelFont,
NSForegroundColorAttributeName: _xAxis.labelTextColor,
NSParagraphStyleAttributeName: paraStyle]
let labelRotationAngleRadians = _xAxis.labelRotationAngle * ChartUtils.Math.FDEG2RAD
let valueToPixelMatrix = transformer.valueToPixelMatrix
var position = CGPoint(x: 0.0, y: 0.0)
var labelMaxSize = CGSize()
if (_xAxis.isWordWrapEnabled)
{
labelMaxSize.width = _xAxis.wordWrapWidthPercent * valueToPixelMatrix.a
}
if let showOnlyLeftRight = (_xAxis as? YourChartXAxis)?.showOnlyLeftRightEnabled
{
if (showOnlyLeftRight)
{
for (var i = lowestVisibleXIndex, maxX = min(highestVisibleXIndex + 1, _xAxis.values.count); i < maxX; i += maxX - lowestVisibleXIndex - 1)
{
let label = _xAxis.values[i]
if (label == nil)
{
continue
}
position.x = CGFloat(i)
position.y = 0.0
position = CGPointApplyAffineTransform(position, valueToPixelMatrix)
let labelns = label! as NSString
//label location by string width
// fix rightLabel
if (highestVisibleXIndex == lowestVisibleXIndex)
{
return super.drawLabels(context: context, pos: pos, anchor: anchor)
}
else if (i == highestVisibleXIndex)
{
let width = labelns.boundingRectWithSize(labelMaxSize, options: .UsesLineFragmentOrigin, attributes: labelAttrs, context: nil).size.width
rightLabelX -= width / 2.0
}
else if (i == lowestVisibleXIndex)
{ // fix leftLabel
let width = labelns.boundingRectWithSize(labelMaxSize, options: .UsesLineFragmentOrigin, attributes: labelAttrs, context: nil).size.width
leftLabelX += width / 2.0
}
// fill leftLabel and rightLabel in current visible screen
if (lowestVisibleXIndex == highestVisibleXIndex) {
ChartUtils.drawMultilineText(context: context, text: label!, point: CGPoint(x: leftLabelX, y: pos), attributes: labelAttrs, constrainedToSize: labelMaxSize, anchor: anchor, angleRadians: labelRotationAngleRadians)
ChartUtils.drawMultilineText(context: context, text: label!, point: CGPoint(x: rightLabelX, y: pos), attributes: labelAttrs, constrainedToSize: labelMaxSize, anchor: anchor, angleRadians: labelRotationAngleRadians)
break
} else if (i == lowestVisibleXIndex) {
ChartUtils.drawMultilineText(context: context, text: label!, point: CGPoint(x: leftLabelX, y: pos), attributes: labelAttrs, constrainedToSize: labelMaxSize, anchor: anchor, angleRadians: labelRotationAngleRadians)
} else if (i == highestVisibleXIndex) {
ChartUtils.drawMultilineText(context: context, text: label!, point: CGPoint(x: rightLabelX, y: pos), attributes: labelAttrs, constrainedToSize: labelMaxSize, anchor: anchor, angleRadians: labelRotationAngleRadians)
}
}
} else {
super.drawLabels(context: context, pos: pos, anchor: anchor)
}
}
}

Resources