How to Procedurally Draw Rectangle/Lines in Swift Using Cgcontext

How to procedurally draw rectangle / lines in swift using CGContext

Here's an example that creates a custom UIImage containing a transparent background and a red rectangle with lines crossing diagonally through it.

class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad();

let imageSize = CGSize(width: 200, height: 200)
let imageView = UIImageView(frame: CGRect(origin: CGPoint(x: 100, y: 100), size: imageSize))
self.view.addSubview(imageView)
let image = drawCustomImage(size: imageSize)
imageView.image = image
}
}

func drawCustomImage(size: CGSize) -> UIImage {
// Setup our context
let bounds = CGRect(origin: .zero, size: size)
let opaque = false
let scale: CGFloat = 0
UIGraphicsBeginImageContextWithOptions(size, opaque, scale)
let context = UIGraphicsGetCurrentContext()!

// Setup complete, do drawing here
context.setStrokeColor(UIColor.red.cgColor)
context.setLineWidth(2)

context.stroke(bounds)

context.beginPath()
context.move(to: CGPoint(x: bounds.minX, y: bounds.minY))
context.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
context.move(to: CGPoint(x: bounds.maxX, y: bounds.minY))
context.addLine(to: CGPoint(x: bounds.minX, y: bounds.maxY))
context.strokePath()

// Drawing complete, retrieve the finished image and cleanup
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}

Fill the remainder of a CGContext, keeping one rectangle transparent

If you want to fill the context (whose frame is bigRect), except for a rectangle inside it (whose frame is smallRect):

CGContextBeginPath(context);
CGContextAddRect(context, bigRect);
CGContextAddRect(context, smallRect);
CGContextSetFillColorWithColor(context, color);
CGContextEOFillPath(context, bigRect);

Redrawing CGContext after it's already been drawn in Swift?

You need to do all of your drawing inside override func draw(_ rect: CGRect) (or inside routines called from there). So your draw needs a flag set outside (by your actions) to tell it which of your styles to draw. You trigger the redraw with setNeedsDisplay() - but that should be called from your action, not from the drawing code. Your current func drawStyleOne() tries to draw (but probably to an invalid currentContext) and then calls setNeedsDisplay(), which just schedules your actual draw routine to be run, which always draws the same "style".

The UIView class works behind the scenes to do quite a lot - if makes sure that draw isn't called unnecessarily, and schedules it when it is necessary, it figures out which parts of the view need re-drawn if they have been obscured (that's why it passes you a rect), and it sets up the current graphics context for draw, amongst other things.

See this Ray Wenderlich tutorial, or the Apple docs. As the Apple docs say:

For custom views, you must override the drawRect: method and perform
all your drawing inside it.

(my emphasis)

So your code should look something like this:

class VectorView: UIView {
public var whichStyle: SomeEnumOfYours = // your default value

override func draw(_ rect: CGRect) {
switch self.whichStyle {
case .style1:
self.drawStyleOne()
case .style2:
self.drawStyleTwo()
//...
}
}
}

//...
func styleButtonPressed() {
NSLog("STYLE BUTTON PRESSED")
_vectorView?.whichStyle = // whichever style required
_vectorView?.setNeedsDisplay()
}

Debugging code:: How to colors lines in CGContext?

Was

  CGContextSaveGState(context)
CGContextTranslateCTM(context,plotCenter.x ,plotCenter.y )
CGContextRotateCTM(context, theta)
CGContextMoveToPoint(context,0.0,0.0)
CGContextAddLineToPoint(context, radiusX + scaleFactorDB*CGFloat(dataDB[i]) + offsetDB, 0.0 )
myAttributedString.drawAtPoint(CGPoint(x: radius , y: 0 ))
CGContextRestoreGState(context)

corrected

  CGContextSaveGState(context)
CGContextTranslateCTM(context,plotCenter.x ,plotCenter.y )
CGContextRotateCTM(context, theta)
CGContextSetStrokeColorWithColor(context,lineColor.CGColor)
CGContextMoveToPoint(context,0.0,0.0)
CGContextAddLineToPoint(context, radiusX + scaleFactorDB*CGFloat(dataDB[i]) + offsetDB, 0.0 )
CGContextStrokePath(context)
myAttributedString.drawAtPoint(CGPoint(x: radius , y: 0 ))
CGContextRestoreGState(context)


Related Topics



Leave a reply



Submit