How to draw a single dot in iOS? - ios

Is it possible to draw a single dot in iOS? I've gone this way which I think is not right:
let circlePath = UIBezierPath(arcCenter: CGPoint(x: 30 ,y: 50 ), radius: CGFloat(1), startAngle: CGFloat(0), endAngle:CGFloat(Double.pi * 2), clockwise: true)
Is it ok to draw shapes with loops?

why can't you take a one view and give a background color black and give Width and height same sizes and give cornerradius height/2

Related

Strange problem with UIBezierPath animation

I want to draw the Moon and then animate Moon's shadow. But after launching this code I can see some glitches on animation line:
GIF:
Why is this happening?
Playground code here
Update 1:
Both paths created by this function but with different angles (0 and π/2*0.6):
func calculateMoonPath(for angle: CGFloat) -> UIBezierPath {
let center = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
let radius = view.bounds.height/2
let path = UIBezierPath(arcCenter: center,
radius: radius,
startAngle: -.pi/2,
endAngle: .pi/2,
clockwise: true)
path.addArc(withCenter: .init(x: center.x - radius * tan(angle), y: center.y),
radius: radius / CGFloat(cosf(Float(angle))),
startAngle: .pi/2 - angle,
endAngle: angle - .pi/2,
clockwise: false
)
path.close()
return path
}
In my experience, the code that generates arcs creates different numbers of cubic bezier curves under the covers as the arc angle changes.
That changes the number of control points in the two curves, and messes up the animation. (as David Rönnqvist says, animations are undefined if the starting and ending path have a different number of control points.)
From what I've read, a full circle requires 4 cubic bezier curves to complete.
It wouldn't be that hard to create a variant of the addArc method that always built the arc using 4 cubic bezier curves, regardless of the arc angle. That's what I would suggest.
You could probably break your arc into 4 pieces (Using 4 sequential calls to addArc(withCenter:...) with different start and end angles such that they combine to make your desired full arc. Each of those should be short enough arc lengths to be a single Bezier curve, so you should get the same number of control points for the beginning and ending combined curve.
If you rewrite your calculateMoonPath function like this:
func calculateMoonPath(for angle: CGFloat) -> UIBezierPath {
let center = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
let radius = view.bounds.height/2
let path = UIBezierPath(arcCenter: center,
radius: radius,
startAngle: -.pi/2,
endAngle: .pi/2,
clockwise: true)
let startAngle = .pi/2 - angle
let endAngle = angle - .pi/2
let delta = (endAngle - startAngle) / 4
for index in 0...3 {
let thisStart = startAngle + delta * CGFloat(index)
let thisEnd = startAngle + delta * CGFloat(index + 1)
path.addArc(withCenter: .init(x: center.x - radius * tan(angle), y: center.y),
radius: radius / CGFloat(cosf(Float(angle))),
startAngle: thisStart,
endAngle: thisEnd,
clockwise: false
)
}
path.close()
return path
}
That yields the following:
The way you do each of the two lines,
is simply, two control points!
One at each end.
That's all there is to it. Don't try using an arc.
Here ...
https://www.desmos.com/calculator/cahqdxeshd

trouble flipping CoreGraphics upside down arc in a UIView

I am having trouble flipping an arc using core graphics api in a UIView.
I understand that the coordinate system is different. UIkit helps me half way by transforming the coordinate system to accommodate the center I provide into a core graphic coordination system. I am assuming that when I draw an arc using core graphics api that its drawing the arc by using the core graphics coordinate system?
I can just change clockwise to false and it will give me what I need. Is this good practice?
I was playing around with CGAffineTransform’s rotation and scale, it didn’t give me what I need. Any way to flip the arc around without hawkishly changing clocking to false?
I also used UIBezierPath to draw another arc the way I want it. As I understand, UIBezierPath is a wrapper around CoreGraphics. I would like to know how I can do this using pure CoreGraphics API.
Extra question: My last function
Call path.stroke() paints the UIBezierPath path and the CGPath arcPath1. How come? 0.o
override func draw(_ rect: CGRect) {
guard let CGContext = UIGraphicsGetCurrentContext() else { return }
let boxPath = CGMutablePath()
boxPath.addLines(between:
[CGPoint(x: rect.width / 2, y: rect.height),
CGPoint(x: rect.width / 2, y: rect.height / 2),
CGPoint(x: rect.width, y: rect.height / 2)])
CGContext.beginPath() // begin new path
CGContext.addPath(boxPath.copy(strokingWithWidth: 5.0, lineCap: .square, lineJoin: .round, miterLimit: 0)) // add new path
CGContext.setFillColor(UIColor.blue.cgColor)
CGContext.fillPath()
let arcPath1 = CGMutablePath()
arcPath1.addArc(center: CGPoint(x: rect.width / 4, y: rect.height / 4 * 3),
radius: 20.0,
startAngle: CGFloat(180).degreesToRadians,
endAngle: CGFloat(360).degreesToRadians,
clockwise: true,
transform: CGAffineTransform(rotationAngle: CGFloat(0).degreesToRadians))
CGContext.beginPath()
CGContext.addPath(arcPath1)
CGContext.setStrokeColor(UIColor.blue.cgColor)
let path = UIBezierPath(arcCenter: CGPoint(x: rect.width / 4 * 3, y: rect.height / 4 * 3),
radius: 20.0,
startAngle: CGFloat(180).degreesToRadians,
endAngle: CGFloat(360).degreesToRadians,
clockwise: true)
path.lineWidth = 5.0
path.stroke()
}
Arcs, left: CG, right: Bezier

How to centre a subview in UIView?

I am trying to create a timer. I have a circle using UIBezierPath which I will animate to show the time remaining. This is the code that draws the shape and adds it to the view:
func drawBgShape() {
bgShapeLayer.path = UIBezierPath(arcCenter: CGPoint(x: center.x , y: center.y), radius:
bounds.width/2, startAngle: -.pi/2, endAngle: 1.5*.pi, clockwise: true).cgPath
bgShapeLayer.strokeColor = UIColor.red.cgColor
bgShapeLayer.fillColor = UIColor.clear.cgColor
bgShapeLayer.lineWidth = 10
layer.addSublayer(bgShapeLayer)
}
However, when the code is run, it looks like this;
I have tried a number of ways to centre the progress bar but none of them seem to work. For example, using frame.height/2 doesn't have any effect.
How can I centre the progress bar?
Thanks.
EDIT:
bgShapeLayer is defined like this:
let bgShapeLayer = CAShapeLayer()
The issue is probably this phrase:
arcCenter: CGPoint(x: center.x , y: center.y)
A view center is where the view is located in its superview. That’s not what you want. You want the center of the view. Try this:
arcCenter: CGPoint(x: bounds.midX , y: bounds.midY)
However, it would be better if you gave your shape layer a frame, size, and position and did everything in terms of the shape layer (which is, after all, where the shape is being drawn). For example:
bgShapeLayer.frame = self.layer.bounds
self.layer.addSublayer(bgShapeLayer)
bgShapeLayer.path = UIBezierPath(
arcCenter: CGPoint(x: bgShapeLayer.bounds.midX, y: bgShapeLayer.bounds.midY),
radius: bgShapeLayer.bounds.width/2,
startAngle: -.pi/2, endAngle: 1.5*.pi, clockwise: true).cgPath
That way we do not confuse the view coordinates and the layer coordinates, as your code does. To some extent you can get away with this because it happens that in this situation there is a general equivalence of the view internal coordinates, its layer internal coordinates, and the bgShapeLayer internal coordinates, but such confusion is not a good habit to get into. You should say what you mean rather than relying on a contingency.

Allowing a subview's radius to be larger than its superview's?

I've got a rounded button that I'm adding a circular progress ring to using code from here:
http://notebookheavy.com/2014/07/30/ios-7-style-progress-meter-in-swift/
var progressCircle = CAShapeLayer();
let centerPoint = CGPoint (x: circle.bounds.width / 2, y: circle.bounds.width / 2);
let circleRadius : CGFloat = circle.bounds.width / 2 // this is what I need to fix * 1.25
var circlePath = UIBezierPath(arcCenter: centerPoint, radius: circleRadius, startAngle: CGFloat(-0.5 * M_PI), endAngle: CGFloat(1.5 * M_PI), clockwise: true );
circle.layer.addSublayer(progressCircle);
self.view.addSubview(circle)
The above code draws a circle around my button, but I'm wanting instead of the circle to hug the inside of the circumference, I want it to hug the outside. I figured I could just make the radius larger, but once I go beyond width / 2, the subview is no longer visible in the view at all.
How would I allow the radius of the progressCircle to be larger than the button's and appear to be wrapping around the button instead of creating a circle inside of the circumference?

Adding curves to the ends of a UIBezierPath(arcCentre)

I have the following code added in the drawRect function of a UIView creating the following attached green arc.
Is there a way I can make the edges at the ends of the curve to look like the red arc rounded edges in the smaller image attached.
[![//Draw the Interior
let center = CGPoint(x: bounds.width/2,
y: bounds.height/2)
//Calculate the radius based on the max dimension of the view.
let radius = max(bounds.width, bounds.height)
//Thickness of the Arc
let arcWidth: CGFloat = 76
//Start and End of Circle angle
let startAngle: CGFloat = 3 * π/4
let endAngle: CGFloat = π/4
let path = UIBezierPath(arcCenter: center, radius: radius/2 - arcWidth/2,
startAngle: startAngle,
endAngle: endAngle,
clockwise: true)
path.lineWidth = arcWidth
counterColor.setStroke()
path.stroke()]
Set the lineCapStyle property of the bezier path to
.Round:
A line with a rounded end. Quartz draws the line to extend beyond the
endpoint of the path. The line ends with a semicircular arc with a
radius of 1/2 the line’s width, centered on the endpoint.
path.lineCapStyle = .Round

Resources