CGContext strokepath makes edges of stroke blurred - ios

I'm working on a simple freehand drawing app. Below is the sample code, I'm using the UIGesture class to track the scribble, i.e., pencil or finger movement, and then stroking those touch points on the UIImageView
let previousLocation = touch.previousLocation(in: self)
let location = touch.location(in: self)
var lineWidth: CGFloat = 3
UIColor.black.setStroke()
context?.setLineWidth(lineWidth)
context?.setLineCap(.round)
context?.move(to: previousLocation)
context?.addLine(to: location)
context?.strokePath()
the strokes are blurry on the edges and the result looks like this:
Few other things I have tried:
1)Stroking bezier pathh
2)Using view instead of image view
3)Increasing scale here -> UIGraphicsBeginImageContextWithOptions, this does increase the sharpness but the overall writing lags
How do I get sharp annotations?

Related

Unable to create circle using UIBezierPath and SCNShape

Anyone familiar with using curved UIBezierPaths to create a SCNShape in ARKit? I'm creating a closed circle path, but I get a diamond shape in my ARKit scene.
Here is the code I use to create the bezier path and SCNShape:
let radius : CGFloat = 1.0
let outerPath = UIBezierPath(ovalIn: CGRect(x: -radius, y: -radius, width: 2* radius, height: 2* radius))
let material = SCNMaterial()
material.diffuse.contents = UIColor.blue
material.isDoubleSided = true
material.ambient.contents = UIColor.black
material.lightingModel = .constant
material.emission.contents = UIColor.blue
let shape = SCNShape(path: outerPath, extrusionDepth: 0.01)
shape.materials = [material]
let shapeNode = SCNNode(geometry: shape)
positioningNode.addChildNode(shapeNode)
I've successfully tested a rectangular bezier path, but even had issues with a rounded rect bezier path (using UIBezierPath(roundedRect:). For the rounded rect bezier path, ARKit shows the curved corners with 45 degree lines.
Thanks in advance!
UPDATE:
On the left is the initial SCNShape with UIBezierPath flatness set to 0.6. On the right is the same SCNShape with flatness set to 0.001.
I was able to find a solution. Basically the UIBezierPath's flatness variable is used to control curvature. The default value of 0.6 was too large in my case. I ended up using a flatness of 0.001
https://developer.apple.com/documentation/scenekit/scnshape/1523432-init

Drawing filled polygon on ARKit Horizontal plane

How would I draw a filled polygon connecting points touched by user on a detected horizontal plane in ARKit world.
I went on to do this by keeping hit points on an array...
let position = SCNVector3.positionFrom(matrix: result.worldTransform)
let sphere = SphereNode(position: position)
nodes.append(position)
... and then trying to draw a bezier path
let bezierPath = UIBezierPath()
bezierPath.lineWidth = 0.1
for index in 0..<nodes.count {
let node = nodes[index] as SphereNode
if (index == 0) {
let point = CGPoint(x: CGFloat(node.position.x), y: CGFloat(node.position.z))
bezeierPath.move(to: point)
} else {
let point = CGPoint(x: CGFloat(node.position.x), y: CGFloat(node.position.z))
bezeierPath.addLine(to: point)
}
}
bezeierPath.close()
bezeierPath.fill()
bezeierPath.stroke()
let shape = SCNShape(path: bezeierPath, extrusionDepth: 0.03)
shape.firstMaterial?.diffuse.contents = UIColor.red
let node = SCNNode.init(geometry: shape)
sceneView.scene.rootNode.addChildNode(node)
But this doesn't draw the polygon on the correct place. it draw it behind the camera vertically angled.
2D geometries like SCNPlane, SCNText and SCNShape are vertical by nature because they work on the xy plane. In your example, you're using the z 3D coordinate as the y 2X coordinate.
Try setting node.eulerAngles.x to -.pi / 2 (if that doesn't work try without the -), and your node will be horizontal instead of vertical.

Cannot correctly apply affine transformation to draw a subimage into UIView's context

I am implementing a subclass of UIView, for which I dynamically call drawRect(_:). To be able to call drawRect(_:) only for the updated rect. of the view canvas, I apply my drawing onto a same-sized off-screen image buffer and inside drawRect(_:), I extract the updated subimage and draw into the current context.
The problem I could not fix is that the subimage is drawn as flipped. I know this is normal due to the fact that UIView's origin is at top-left corner and the cropped subimage's origin is at bottom-left corner.
When I flip the subimage before drawing using the following code, no drawing happens. If I remove the lines shown by the arrows, I see the updated subimage as flipped. Can you please show me what is wrong with my affine transformations?
override func draw(_ rect: CGRect) {
guard let _ = bufferImage else { return }
let scaleFactor = UIScreen.main.scale
let scaledRect = rect.applying(CGAffineTransform(scaleX: scaleFactor, y: scaleFactor))
let croppedImage = bufferImage!.cgImage!.cropping(to: scaledRect)!
let context = UIGraphicsGetCurrentContext()!
context.scaleBy(x: 1.0, y: -1.0) // <------
context.translateBy(x: 0.0, y: -rect.height) // <------
context.draw(croppedImage, in: rect)
}

How to draw a border around two overlapping UIViews

I have two UIViews that create a unique shape when overlapping and I want to draw a border around the combined views.
What is the proper method for doing this?
The UIViews are:
Circle image view to display a user profile image
A rectangle view that will container user profile data (i.e. name, dob, etc)
Here is an image of what the two view together will look like:
Ok figured it out by doing the following:
Setting a border on the rectangle UIView
Creating a CAShapeLayer with UIBezierPath to draw a semi-circle and add it to the circle UIView's layer in the drawRect method:
override func draw(_ rect: CGRect) {
super.draw(rect)
let shapeLayer = CAShapeLayer()
let topSemiCirclePath = UIBezierPath(arcCenter: userImageView.center, radius: userImageView.bounds.size.width / 2.0, startAngle: CGFloat(Double.pi), endAngle: CGFloat(Double.pi / 180), clockwise: true)
topSemiCirclePath.lineWidth = 2.0
UIColor.lightGray.setStroke()
topSemiCirclePath.stroke()
shapeLayer.path = topSemiCirclePath.cgPath
userImageView.layer.addSublayer(shapeLayer)
}

How to add blinking circle inside draw:inRect: method of UIView?

Inside my custom UIView and draw:inRect initializer I have following code:
override func draw(_ rect: CGRect) {
super.draw(rect)
//...
let axisPath = UIBezierPath()
axisPath.move(to: CGPoint(x: paddingHorizontal + 20, y: paddingVertical))
axisPath.addLine(to: CGPoint(x: leftOffset, y: bottomOffset))
axisPath.addLine(to: CGPoint(x: rect.width - paddingHorizontal, y: bottomOffset))
axisPath.lineWidth = 1
UIColor.black.set()
axisPath.stroke()
var currentIndex = 0
for yearData in data {
//...
let circle = UIBezierPath(roundedRect: CGRect(x: x - 4, y: y - 4, width: 8, height: 8), cornerRadius: 4)
circle.fill()
circle.stroke() //red circle, I need to make it blinking somehow
//...
}
//...
}
The result is following:
Now I need to make red circle blinking:) How can I do that?
I would take a different approach. Use draw(_:) only for the static part of the graph.
The blinking red circle should be a simple little subview added over the graph view. Use a repeating UIView animation to fade the little circle view in and out using its alpha property.
See How do I get this fade animation to work? for an example of performing the blinking animation.
Setup a NSTimer and draw and undraw your red circle based on that. You have at least two choices: Create the red circle as an UIView and just set Hidden flag appropriately, or toggle a flag on your timer and invalidate the area encompassed by the red circle.

Resources