Swift: Draw a Circle around a Label - ios

I'm trying to draw a circle around a label at runtime in a TableViewCell's cell.
I can figure out how to get it roughly around the label, but I'm having some trouble centring it exactly around the label.
The circle seems to be drawing just to the right and towards the middle of the label.
Here's my code so far, I'm sure this will be easy for someone to bang out.
func drawCircle() {
let x = countLabel.layer.position.x - (countLabel.frame.width)
let y = countLabel.layer.position.y - (countLabel.frame.height / 2)
let circlePath = UIBezierPath(roundedRect: CGRectMake(x, y, countLabel.frame.height, countLabel.frame.height), cornerRadius: countLabel.frame.height / 2).CGPath
let circleShape = CAShapeLayer()
circleShape.path = circlePath
circleShape.lineWidth = 3
circleShape.strokeColor = UIColor.whiteColor().CGColor
circleShape.fillColor = UIColor.clearColor().CGColor
self.layer.addSublayer(circleShape)
}

You can instead just use corner radius on your label's layer. You make the label square and then set its layer's corner radius to half the width/height of the label which will make it perfectly round. The you set the border width to something greater than zero and the border color to the stroke color you want to use.
let size:CGFloat = 35.0 // 35.0 chosen arbitrarily
countLabel.bounds = CGRectMake(0.0, 0.0, size, size)
countLabel.layer.cornerRadius = size / 2
countLabel.layer.borderWidth = 3.0
countLabel.layer.backgroundColor = UIColor.clearColor().CGColor
countLabel.layer.borderColor = UIColor.greenColor().CGColor
It will look something like this:
Although the full code for that goes like this in a single view controller iPad template project:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let size:CGFloat = 35.0 // 35.0 chosen arbitrarily
let countLabel = UILabel()
countLabel.text = "5"
countLabel.textColor = .greenColor()
countLabel.textAlignment = .Center
countLabel.font = UIFont.systemFontOfSize(14.0)
countLabel.bounds = CGRectMake(0.0, 0.0, size, size)
countLabel.layer.cornerRadius = size / 2
countLabel.layer.borderWidth = 3.0
countLabel.layer.backgroundColor = UIColor.clearColor().CGColor
countLabel.layer.borderColor = UIColor.greenColor().CGColor
countLabel.center = CGPointMake(200.0, 200.0)
self.view.addSubview(countLabel)
}
}

countLabel.layer.cornerRadius = 0.5 * countLabel.bounds.size.width

Ugh, it was what I thought, silly mistake.
X and Y are calculated from the middle instead of from the top left when dealing with BezierPath's.
So the code for x and y should be as follows:
let x = countLabel.layer.position.x - (countLabel.frame.height / 2)
let y = countLabel.layer.position.y - (countLabel.frame.height / 2)

Try this:
func drawCircle() {
var padding : CGFloat = 8
let x = countLabel.layer.position.x - (countLabel.frame.width / 2)
let y = countLabel.layer.position.y - (countLabel.frame.width / 2)
let circlePath = UIBezierPath(roundedRect: CGRectMake(x - padding, y - padding, countLabel.frame.width + (2 * padding), countLabel.frame.width + (2 * padding)), cornerRadius: (countLabel.frame.width + (2 * padding)) / 2).CGPath
let circleShape = CAShapeLayer()
circleShape.path = circlePath
circleShape.lineWidth = 3
circleShape.strokeColor = UIColor.greenColor().CGColor
circleShape.fillColor = UIColor.clearColor().CGColor
self.view.layer.addSublayer(circleShape)
}
Modify padding variable to adjust the padding between the label and the circle.
Cheers!

Related

How to create a solid rectangle border around the circle in UICollectionView Swift4+

I have tried the following but only creates a border around the circle but not a separate rectangle border.
as shown here
cell.layer.borderWidth = 1.0
cell.layer.borderColor = UIColor.black.cgColor
I want to create a rectangle border as shown here
Instead of
cell.layer.borderWidth = 1.0
cell.layer.cornerRadius = 10
cell.layer.borderColor = UIColor.black.cgColor
Have you tried?
cell.layer.borderWidth = 1.0
cell.layer.borderColor = UIColor.black.cgColor
I'm not sure if it'll work for you, but you can try to add an additional layer like that:
var rect = UIView()
var circle: CALayer?
override func loadView() {
// create view with border
rect.frame = CGRect(x: 50, y: 50, width: 300, height: 200)
rect.layer.borderColor = UIColor.black.cgColor
rect.layer.backgroundColor = UIColor.clear.cgColor
rect.layer.borderWidth = 2
// add circle layer
let circle = CALayer()
circle = UIColor.red.cgColor
// add circle layer on view with drawn border
rect.layer.addSublayer(circle)
view.addSubview(rect)
circle = circle
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// layout circle on your view
let radius = min(rect.frame.width, rect.frame.height) * 0.5
circle?.frame = CGRect(x: rect.bounds.width / 2 - radius / 2, y: rect.bounds.height / 2 - radius / 2, width: radius, height: radius)
circle?.cornerRadius = radius / 2
}
But probably that's not the best option, maybe someone gives you better solution.
If it doesn't work for you, please, give more details of you problem.

Set CGPath width to view width

I am using PocketSVG for converting my SVG file to a CGPath like that:
override func awakeFromNib() {
super.awakeFromNib()
let myPath = PocketSVG.pathFromSVGFileNamed("CategoriesBar").takeUnretainedValue()
let lineWidth: CGFloat = 3.0
let insetRect = CGRectInset(bounds, lineWidth / 2.0, lineWidth / 2.0)
let boundingRect = CGPathGetPathBoundingBox(myPath)
let scale = insetRect.size.width / boundingRect.size.width
var transform = CGAffineTransformScale(CGAffineTransformMakeTranslation(-boundingRect.origin.x * scale + insetRect.origin.x, -boundingRect.origin.y * scale + insetRect.origin.y), scale, scale)
let transformedPath = CGPathCreateMutableCopyByTransformingPath(myPath, &transform)
let myShapeLayer = CAShapeLayer()
myShapeLayer.path = transformedPath
myShapeLayer.strokeColor = UIColor.redColor().CGColor
myShapeLayer.lineWidth = lineWidth
myShapeLayer.fillColor = UIColor.whiteColor().CGColor
self.layer.addSublayer(myShapeLayer)
}
The problem is that the myShapeLayer is bigger the view and it being cut off the side, I want that the width of the path (I don't care about the height) to be equals to the view's width.
How can I do that?
Thank you!

UILabel won't center in UIView

I'm trying to create a custom UIView that can be used as a progress bar or similar (#IBDesignable with IBInspectables). I want a centered (primarily X-axis, but for now Y as well) UILabel in this view.
private func addLabel()
{
let label = UILabel(frame: CGRectMake(0, 0, self.frame.width / 4, self.frame.height / 4))
label.center = self.center
label.textAlignment = .Center
label.text = "5"
//Color just to see the whole label
label.backgroundColor = UIColor.redColor().colorWithAlphaComponent(0.5)
self.addSubview(label)
}
In Interfacebuilder the label centers just fine:
However, when running it on my device (iPhone 5S) the label is aligned slightly to the right (picture below). I've tried different approaches (like making the label frame self.frame) but it's still not centered correctly. What am I missing?
override func drawRect(rect: CGRect) {
// Drawing code
let center = CGPoint(x:bounds.width/2, y: bounds.height)
let radius: CGFloat = max(bounds.width, bounds.height)
let startAngle: CGFloat = π
let endAngle: CGFloat = 2 * π
path = UIBezierPath(arcCenter: center, radius: radius / 2 - arcWidth / 2, startAngle: startAngle, endAngle: endAngle, clockwise: true)
path.lineWidth = arcWidth
arcBackgroundColor.setStroke()
path.stroke()
self.addLayer()
}
private func addLayer()
{
progressLayer = CAShapeLayer()
progressLayer.path = self.path.CGPath
progressLayer.strokeColor = self.progressColor.CGColor
progressLayer.fillColor = UIColor.clearColor().CGColor
progressLayer.lineWidth = self.arcWidth
if animatesOnDraw && progress > 0 {
self.layer.addSublayer(progressLayer)
self.animateProgress(0)
} else {
progressLayer.strokeEnd = CGFloat(self.progress / self.maxValue)
progressLayer.opacity = Float(self.progressStrokeEndValue)
self.layer.addSublayer(progressLayer)
}
addLabel() //as shown above
}
The problem is label.center = self.center, because the center of the label and the center of the superview are in different coordinate systems so they might not be the same. Use instead
label.center = CGPoint(x: self.view.frame.bounds.size.width / 2.0, y:self.view.frame.bounds.size.height / 2.0)
Try this:
self.label.center = view.center
Add the UILabel programatically and Modify the UILabel frame from
let label = UILabel(frame: CGRectMake(0, 0, self.frame.width / 4, self.frame.height / 4))
To
let label = UILabel(frame: CGRectMake(self.frame.width/2 - (self.frame.height/4)/2, 0, self.frame.width / 4, self.frame.height / 4))
ELSE
Implement Autolayout
A possible solution can be:-
#IBOutlet customView : UIView!
//Let customView be the outlet of your custom view
func addLabel () {
let CenterY = customView.center.y
let CenterX = customView.center.x
let heightOfCustom = customView.frame.size.height
let widthOfCustom = customView.frame.size.width
// If you want a label with height and width 1/4th of the width of the custom view
let label = UILabel(frame : CGRectMake(CenterX - widthOfCustom/8, CenterY - heightOfCustom/8, widthOfCustom/4, heightOfCustom/4))
label.text = "5"
label.textAlignment = .Center
label.backgroundColor = UIColor.redColor().colorWithAlphaComponent(0.5)
self.addSubview(label)
}

Drawing dots and lines on to a UIView

I gave myself an exercise to learn Swift, based on an example I have found on the Apple Swift website:
As you can see there's a river and a few dots in it right in the middle, forming a path. So I have started looking for a similar river image on the internet and I have created a Xcode playground. This is what I have now:
So basically I have an UIView with a subview consisting in the river image I have found and a dot made with UIBezierPath.
My first question is: is this the right way to drawn on to a UIView? I mean using a UIBezierPath. And my second question is: how do I draw the dot at a precise coordinate inside the UIView? (UIBezierPath or everything else?)
Just to be more precise, my intent here is to make an algorithm to make the program recognize the image and based on the pixel color it would draw a line with dots from the start to the end of the river, passing between it's middle.
To draw UIBezierPath on UIView do this:
let xCoord = 10
let yCoord = 20
let radius = 8
let dotPath = UIBezierPath(ovalInRect: CGRectMake(xCoord, yCoord, radius, radius))
let layer = CAShapeLayer()
layer.path = dotPath.CGPath
layer.strokeColor = UIColor.blueColor().CGColor
drawingView.layer.addSublayer(layer)
This code will draw a dot with radius 8 with coordinates 10,20 on your view.
Update: Swift 4+
let xCoord = 10
let yCoord = 20
let radius = 8
let dotPath = UIBezierPath(ovalIn: CGRect(x: xCoord, y: yCoord, width: radius, height: radius))
let layer = CAShapeLayer()
layer.path = dotPath.cgPath
layer.strokeColor = UIColor.blue.cgColor
drawingView.layer.addSublayer(layer)
Here is an attempt at the lines part of the equation:
var offset:CGFloat = 0; var squareWidth:Int = 20
var squareRows:Int = Int(view.frame.size.width/CGFloat(squareWidth))
var squareColumns:Int = Int(view.frame.size.height/CGFloat(squareWidth))
for (index,element) in (0...squareRows).enumerate(){
for (column,element) in (0...squareColumns).enumerate(){
// Build The Square
let rectanglePath = UIBezierPath(roundedRect: CGRectMake(
view.frame.minX + CGFloat(squareWidth * index) - offset,
view.frame.minY + CGFloat(column * squareWidth), 20, 20
),
cornerRadius: 0.00)
// Style Square
let a = CAShapeLayer()
a.path = rectanglePath.CGPath
a.strokeColor = UIColor.whiteColor().CGColor
a.fillColor = nil
a.opacity = 0.3
a.lineWidth = 1.5
view.layer.insertSublayer(a, atIndex: 1)
}
}

Centering an Oval using CGRectMake Swift

I am having trouble trying to center my oval programmatically currently I have this code
func setupLayers(){
let oval = CAShapeLayer()
oval.frame = CGRectMake(137.5, 283.5, 100, 100)
oval.path = ovalPath().CGPath;
self.layer.addSublayer(oval)
layers["oval"] = oval
resetLayerPropertiesForLayerIdentifiers(nil)
}
This code is able to center the oval in an iPhone 6 and 6s, but I want it to be able to be centered in all devices.
I have tried this code too:
func drawCircle(size: CGFloat) {
let circleShape = CAShapeLayer()
circleShape.path = UIBezierPath(ovalInRect: CGRectMake(-size/2, -size/2, size, size)).CGPath
circleShape.fillColor = UIColor.blueColor().CGColor
circleShape.strokeColor = UIColor.redColor().CGColor
circleShape.lineWidth = 1
circleShape.frame.origin = view.center
circleShape.name = "circleShape"
view.layer.addSublayer(circleShape)
}
drawCircle(100)
You need to set the layer's anchorPoint to be its center.
oval.anchorPoint = CGPoint(x: 0.5, y: 0.5)
Then you can set position of layer as the center of view:
oval.position = self.center

Resources