Active Filters: Core Graphics

Today we'll be taking a look at another often overlooked, yet quite powerful tool in Core Graphics: CAEmitterLayer. It can help us create all sorts of interesting particle effects. We're going to use it to make some chocolate rain. Let's do it!

We'll start by creating a new emitter layer. (We'll need to do all of this inside some view we want to rain chocolate down upon).

var emitter = CAEmitterLayer()

Next, we'll configure its size, position, and shape so it looks essentially like a "rain bar" at the top of our view:

emitter.emitterPosition = CGPoint(x: frame.size.width / 2.0, y: 0)
emitter.emitterShape = kCAEmitterLayerLine
emitter.emitterSize = CGSize(width: frame.size.width, height: 1)

For the shape, we could have chosen a point, circle, rectangle, sphere, etc. We chose a line because we want the generated particles to "rain down" vertically.

emitter.emitterCells = (0..<5).map({ _ in
  let intensity = Float(0.5)

  let cell = CAEmitterCell()

  cell.birthRate = 6.0 * intensity
  cell.lifetime = 14.0 * intensity
  cell.lifetimeRange = 0
  cell.velocity = CGFloat(350.0 * intensity)
  cell.velocityRange = CGFloat(80.0 * intensity)
  cell.emissionLongitude = CGFloat(M_PI)
  cell.emissionRange = CGFloat(M_PI_4)
  cell.spin = CGFloat(3.5 * intensity)
  cell.spinRange = CGFloat(4.0 * intensity)
  cell.scaleRange = CGFloat(intensity)
  cell.scaleSpeed = CGFloat(-0.1 * intensity)
  cell.contents = UIImage(named: "bite")!.cgImage

  return cell
})

Finally, we'll do the bulk of the work by adding 5 CAEmitterCells to our emitter layer. (Hint: Want more particles? Add more cells.)

These cells are where we configure how particles get genreated and how they behave once they are on screen.

There's ton of examples of different techniques online and the docs have more indepth info on each property

Don't be scared by all the magic numbers. Entire books have been written about how to generate certain particles effects. Things can get quite complicated.

We'll finish up by adding our emitter layer as a sublayer of the layer of the view we're working in:

layer.addSublayer(emitter)

Success!

Looking for a library that encapsulates this technique into a handy reusable view? Check out SAConfettiView by Sudeep Agarwal

Core Graphics and Core Animation are incredibly powerful tools with lots of useful components. Today we'll try out one of them, CATextLayer, and learn how it can help us create some fun and interesting visuals. Let's get started.

First, we'll need a view to mask. We'll create a UIImageView and use this great looking Apple TV marketing image to test things out.

let imageView = UIImageView(
  UIImage(named: apple-tv-color-bars.png)
)

imageView.sizeToFit()

Then, we'll need a CATextLayer. We'll configure it to so it has the same size as our imageView, and do some Core Graphics housekeeping:

let textLayer = CATextLayer()

textLayer.frame = imageView.bounds
textLayer.rasterizationScale = UIScreen.mainScreen().scale
textLayer.contentsScale = UIScreen.mainScreen().scale

Next, we'll configure the text layer to look the way we want. We'll set a font, alignment, and wrapping/truncation settings:

textLayer.alignmentMode = kCAAlignmentCenter
textLayer.fontSize = 42.0
textLayer.font = UIFont(name: "TrebuchetMS-Bold", size: 42.0)
textLayer.wrapped = true
textLayer.truncationMode = kCATruncationEnd

We'll give it some content to display:

textLayer.string = "Little Bites of Cocoa"

Finally, we'll set the text layer as the mask of our imageView's layer.

imageView.layer.mask = textLayer

Success!

Looking for a library that does all this for you (plus a bit more)? Check out Translucid by Lucas Ortis.

Let's draw some strings! There's plenty of text-drawing capabilities in iOS and OS X, with full frameworks like Text Kit (or the lower-level Core Text) dedicated to the task. Today, we'll start looking at these capabilities by using Core Graphics and UIKit to draw a multiline string.

Sometimes we need to draw text ourselves, “manually”. This can be helpful when optimizing for scrolling performance, or when complete customization is necessary.

First we'll need a view to draw into, we'll make a new UIView, and override drawRect.

Next, we'll start implementing our drawRect function with a few variables. We'll choose a font, then setup a paragraph style to make our text multiline, with some tall lines.

We'll also setup some drawing options to tell the system we want multiline text:

let textFont = UIFont(name: "Avenir Next", size: 17.0)!
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineHeightMultiple = 1.2
paragraphStyle.lineBreakMode = .ByWordWrapping

let drawingOptions: NSStringDrawingOptions = [
  .UsesLineFragmentOrigin, .UsesFontLeading]

We'll save these off into a textAttributes dictionary. Next, we'll need to figure out what rectangle we'll be drawing our text into.

We'll use the boundingRectWithSize function to find out how tall it will be when constrained to the width of our view:

let textRect = (text as NSString)
  .boundingRectWithSize(
    CGSizeMake(bounds.size.width, CGFloat.max),
    options: drawingOptions,
    attributes: textAttributes,
    context: nil)

Then we'll just pass that calculated textRect into a call to drawWithRect:

(text as NSString).drawWithRect(textRect,
  options: drawingOptions,
  attributes: textAttributes,
  context: nil)

Topics

#39: CGPath Basics 📈

Topics

CGPaths are how CoreGraphics describes the bezier paths it uses for drawing to the screen. They can go a long way to unlocking some of the power of CoreGraphics, so let’s take a look:

We're going to create a custom disclosure indicator to use on our UITableView rows. We’ll create a CGPath, and add two lines to it that make up the arrow shape.

This kind of CoreGraphics code is largely imperative. You tell the system what to do in a bunch of steps.

@IBDesignable
class DisclosureIndicatorView : UIView {
  override func drawRect(rect: CGRect) {
    let path = CGPathCreateMutable()

    // move to top-left
    CGPathMoveToPoint(path, nil, bounds.origin.x, bounds.origin.y)

    // add line to middle-right
    CGPathAddLineToPoint(path, nil, bounds.size.width, CGRectGetMidY(bounds))

    // move to bottom-left
    CGPathMoveToPoint(path, nil, bounds.origin.x, bounds.size.height)

    // add another line to middle-right
    CGPathAddLineToPoint(path, nil, bounds.size.width, CGRectGetMidY(bounds))

    let context = UIGraphicsGetCurrentContext()
    CGContextAddPath(context, path)

    UIColor.lightGrayColor().setStroke()
    CGContextSetLineWidth(context, 2.0);

    CGContextDrawPath(context, kCGPathStroke)
  }
}

We tell CoreGraphics to move to the top-left point in our view, then we ask the system to add line, then move again, and so on.

Finally we add the path to our context, and draw the path to the screen using a light gray color, and a 2-pixel wide line.

Pro Tip: Add @IBDesignable to your view so you can preview it in Interface Builder! (shown below)