How to Draw a Point Using Core Graphics

Drawing lines with core graphics

Try drawing the line using CAShapeLayer like so:

func addLine(fromPoint start: CGPoint, toPoint end:CGPoint) {
let line = CAShapeLayer()
let linePath = UIBezierPath()
linePath.move(to: start)
linePath.addLine(to: end)
line.path = linePath.cgPath
line.strokeColor = UIColor.red.cgColor
line.lineWidth = 1
line.lineJoin = kCALineJoinRound
self.view.layer.addSublayer(line)
}

Hope this helps!

How to draw graph using core graphics

Yes, of course it is possible. A graph like that is just a line. You can use a UIBezierPath or CGPath to draw this. Typically you'd have an array of the points on your graph, then just add lines to each point when building your path.

WWDC 2011 session 129 goes through how the graph in the Stocks app is drawn and made "fancy", all using core graphics.

Drawing a rectangle on UIImageView using Core Graphics

You are replacing your image with a new image composed of the previous image plus a rectangle drawn over it. Rather than drawing the image from the image view, draw the original image.

Alternatively, you could render the the rectangle as a shape layer and just update that shape layer's path:

class ViewController: UIViewController {

@IBOutlet weak var imageView: UIImageView!

private let shapeLayer: CAShapeLayer = {
let _shapeLayer = CAShapeLayer()
_shapeLayer.fillColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0).cgColor
_shapeLayer.strokeColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1).cgColor
_shapeLayer.lineWidth = 3
return _shapeLayer
}()

private var startPoint: CGPoint!

override func viewDidLoad() {
super.viewDidLoad()

imageView.layer.addSublayer(shapeLayer)
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
startPoint = touches.first?.location(in: imageView)
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let startPoint = startPoint, let touch = touches.first else { return }

let point: CGPoint

if let predictedTouch = event?.predictedTouches(for: touch)?.last {
point = predictedTouch.location(in: imageView)
} else {
point = touch.location(in: imageView)
}

updatePath(from: startPoint, to: point)
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let startPoint = startPoint, let touch = touches.first else { return }

let point = touch.location(in: imageView)

updatePath(from: startPoint, to: point)
imageView.image = imageView.snapshot(afterScreenUpdates: true)
shapeLayer.path = nil
}

override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
shapeLayer.path = nil
}

private func updatePath(from startPoint: CGPoint, to point: CGPoint) {
let size = CGSize(width: point.x - startPoint.x, height: point.y - startPoint.y)
let rect = CGRect(origin: startPoint, size: size)
shapeLayer.path = UIBezierPath(rect: rect).cgPath
}

}

Where:

extension UIView {
func snapshot(afterScreenUpdates: Bool = false) -> UIImage {
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
drawHierarchy(in: bounds, afterScreenUpdates: afterScreenUpdates)
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
}

This is not only simpler, but more efficient, too.

That yields:

enter image description here

By the way, I might suggest using predictive touches in touchesMoved. On a device (not the simulator) that can yield a slightly more responsive UI.

Drawing a circle using Core Graphics Context in iOS - Objective-C or Swift

You mean you only want the outline? Use CGContextStrokeEllipseInRect in that case.

CGContextRef contextRef = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(contextRef, 2.0);
CGContextSetStrokeColorWithColor(contextRef, [color CGColor]);
CGRect circlePoint = (CGRectMake(coordsFinal.x, coordsFinal.y, 50.0, 50.0));

CGContextStrokeEllipseInRect(contextRef, circlePoint);

Draw bullet arrow in Core Graphics

If you draw with strokes as suggested by @NSGod, you need to move to the upper or lower tip, and then line to the arrow head, then line to the lower or upper tip. If you want a 45 degree angle like the one you drew, the amount you add to or subtract from the coordinates should be equal.

You will also need to set the line width for the path. This will need to be proportional to the size.

CGPoint origin = CGPointMake(100, 20);

UIBezierPath *path = [UIBezierPath bezierPath];
// Upper tip
[path moveToPoint:CGPointMake(origin.x+20, origin.y-20)];
// Arrow head
[path addLineToPoint:CGPointMake(origin.x, origin.y)];
// Lower tip
[path addLineToPoint:CGPointMake(origin.x+20, origin.y+20)];

[[UIColor redColor] set];
// The line thickness needs to be proportional to the distance from the arrow head to the tips. Making it half seems about right.
[path setLineWidth:10];
[path stroke];

The result is a shape like this.

Arrow drawn by code above

If you want to fill, not stroke - which may be necessary if you want to outline your arrow - you will need to plot out 6 points and then close the path.

Is this code drawing at the point or pixel level? How to draw retina pixels?

[Note: The code on the github example does not calculate the gradient on a pixel basis. The code on the github example calculates the gradient on a points basis. -Fattie]

The code is working in pixels. First, it fills a simple raster bitmap buffer with the pixel color data. That obviously has no notion of an image scale or unit other than pixels. Next, it creates a CGImage from that buffer (in a bit of an odd way). CGImage also has no notion of a scale or unit other than pixels.

The issue comes in where the CGImage is drawn. Whether scaling is done at that point depends on the graphics context and how it has been configured. There's an implicit transform in the context that converts from user space (points, more or less) to device space (pixels).

The -drawInContext: method ought to convert the rect using CGContextConvertRectToDeviceSpace() to get the rect for the image. Note that the unconverted rect should still be used for the call to CGContextDrawImage().

So, for a 2x Retina display context, the original rect will be in points. Let's say 100x200. The image rect will be doubled in size to represent pixels, 200x400. The draw operation will draw that to the 100x200 rect, which might seem like it would scale the large, highly-detailed image down, losing information. However, internally, the draw operation will scale the target rect to device space before doing the actual draw, and fill a 200x400 pixel area from the 200x400 pixel image, preserving all of the detail.

Core graphics - Line using midpoint and angle

A unit vector with angle alpha to the x-axis is

(cos(alpha), sin(alpha))

Now you want to draw a line that is tangential to the circle line, so it is perpendicular to the line from the center of the circle to the point on the circle line.

To get a perpendicular vector, you add 90º = π/2 to the angle:

(cos(alpha + π/2), sin(alpha + π/2)) = (-sin(alpha), cos(alpha))

using basic trigonometric identities.

This (hopefully) explains why the endpoint of the line must be computed as

CGPointMake(newPoint.x - distance * sinf(angleInRadians), newPoint.y + distance * cosf(angleInRadians));


Related Topics



Leave a reply



Submit