Mobile Development

Ilya Loshkarev


Graphics Context

Context contains drawing parameters
and all information needed to render the paint

Graphical Contexts are organized in form of a stack

Current Context

						override func draw(_ rect: CGRect) {
							// Get view's current graphics context
							if let context = UIGraphicsGetCurrentContext() {
								/* draw something */

Image Context

							// Create bitmap and make it current context
							// Copy image into current context
							image.draw(in: CGRect(origin:,
									size: image.size))
							/* draw something  on top of the image*/
							// Return an image from current bitmap-based context
							image = UIGraphicsGetImageFromCurrentImageContext()
							// Remove current bitmap context

Drawing with UI Graphics

All drawing happens in current graphics context

							func drawLine(from fromPoint: CGPoint, to toPoint: CGPoint) {
								// Create Path
								let linePath = UIBezierPath()
								// Set up context parameters
								linePath.lineWidth = 10
								linePath.lineCapStyle = .round
								// Draw path in context

Draw order is important

Paths are drawn on top of each other

Drawing with Core Graphics

							context.addRect(rect: CGRect(x: 0, y:0,
							                             width: 250, height: 250))

Core Graphics Gradient

						let colors = [startColor.cgColor, endColor.cgColor]
						let colorLocations:[CGFloat] = [0.0, 1.0]
						let colorSpace = CGColorSpaceCreateDeviceRGB()
						let gradient = CGGradient(colorsSpace: colorSpace,
						                          colors: colors as CFArray,
						                          locations: colorLocations)
						                       end: CGPoint(x: 1, y: 1),
						                       options: .drawsAfterEndLocation)


Core Graphics Layer

An offscreen context for reusing content
drawn with Core Graphics

    • Repeated drawing
    • Offscreen rendering

Creating a Layer

							let layer = CGLayer(context, size: CGSize(width: 100, height: 100), auxiliaryInfo: nil)
							let layerContext = layer?.context
							/* draw something */
							context.draw(layer, at:

Core Animation Layer

An object that manages image-based content
and allows you to perform animations on that content

Completely unrelated to CGLayer

View's Layer

Every view has underlying CALayer

							view.layer.contents = UIImage(named: "swift")?.cgImage
							// Layers can have sublayers
							let sublayer = CALayer()
							sublayer.frame = CGRect(x: 50, y: 50, width: 100, height: 100)
							sublayer.backgroundColor =

Layers are rendered by the GPU

Blessings of GPU Rendering

  • Fast graphics – layers are optimized for better use of graphical hardware
  • Percise animations – Core Animation allows for more animation control and complexity then UIKit
  • Rasterization control – layer content can be rasterized once and stored as bitmap

So Many Layers

Honorable Mention: CAMetalLayer

Allows to use 3d rendering pipeline(shaders) in a layer


Simple Animation

							// Rotate view 180 degrees
								withDuration: 0.25, // length of animation in seconds
								delay: 0.0, // animation start delay
								options: [.curveLinear], // timing curve
								animations: { // animation closure
									view.transform = view.transform.rotated(by: CGFloat(M_PI))},
								completion: { finished in // called after animation is complete
									view.transform = CGAffineTransform.identity

Interpolates values of the view's properties

Uses shortest path between the values
(360° rotation does nothing!)

Animatable View Properties

  • frame
  • bounds
  • center
  • transform
  • alpha
  • backgroundColor
  • contentStretch

You should never animate position properties
of a view with Auto Layout cnstraints

Animation Options

Timing Curve – defines how fast values change

  • linear
  • easeIn
  • easeOut
  • easeInOut

Other Options

  • repeat – repeats animation ad infinum
  • allowUserInteractions – allows user interactions
  • beginFromCurrentState – animation started in the middle
    of another animation will use current values of a view

Keyframe Animation

Interpolates values between user defined keyframes

							// Rotate view 360 degrees
							let duration = 1.0
								withDuration: duration,
								delay: 0.0,
								options: [.calculationModeCubic], // interpolation method
								animations: {  /* define frames here */ }


								// Rotate 360 degrees animations closure
								let frames = 6
								let angle = 2 * M_PI / Double(frames)
								let frameDuration = duration / Double(frames)
								for i in 0..<frames {
										withRelativeStartTime: Double(i) * frameDuration,
										relativeDuration: frameDuration,
										animations: {
											sender.transform = sender.transform.rotated(by: CGFloat(angle))

Each keyframe defines an animation
with its own delay and duration

View Property Animator

								animator = UIViewPropertyAnimator(duration: 0, curve: .linear)
								{ = newLocation
									self.view.background-color = newColor

								animator.fractionComplete = 0.5

Allows to dynamically modify animations before they finish

Avaliable since iOS 10

UIView Animation Limits

  • Animation is limited to view's properties
  • Doesn't allow custom timing functions
  • CPU capped rendering

Layer Animations

  • Processed on GPU
  • Shape Transitions
  • 3D Transformations
  • Clipping Animations

Animatable Layer Properties

anchorPoint backgroundColor backgroundFilters borderColor borderWidth bounds compositingFilter contents contentsRect cornerRadius doubleSided filters frame hidden mask masksToBounds opacity position shadowColor shadowOffset shadowOpacity shadowPath shadowRadius sublayers sublayerTransform transform zPosition

Any of the layer's properties can be animated

Basic Animation

							let fillAnimation = CABasicAnimation(keyPath: "fillColor")
							fillAnimation.fromValue = UIColor.clear.cgColor
							fillAnimation.toValue = UIColor.white.cgColor
							fillAnimation.duration = 1
							layer.add(fillAnimation, forKey: "fill animation")

Basic animation animates one property at a time

Group Animation

							end.beginTime = 1
							let groupAnimation = CAAnimationGroup()
							groupAnimation.animations = [start,end]
							groupAnimation.duration = 3
							circle.add(groupAnimation, forKey: "group stroke")

beginTime – sets the delay in the group animation

Keyframe Layer Animation

							let keyframeAnimation = CAKeyframeAnimation(keyPath: "path")
							keyframeAnimation.values = []
							for points in 5...10 {
							    let starPath = UIBezierPath(starWithNumberOfPoints: points, centeredAt:, innerRadius: view.bounds.width / 6, outerRadius: view.bounds.width / 3)
							keyframeAnimation.duration = 5
							keyframeAnimation.autoreverses = true
							star.add(keyframeAnimation, forKey: "points")

Processing Images

What is an Image?

  • UIImage – higher-level image object. Immutable, created from existing image content
  • CGImage – bitmap representation of an image. Mutable
  • CIImage – a "recipe" for an image, executed only when you request that an image be rendered for display or output

Image ⇄ Image

							cgImage = uiImage.cgImage // nil if uiImage contains ciImage reference
							ciImage = uiImage.ciImage // nil if uiImage contains ciImage reference ?
							uiImage = UIImage(ciImage: ciImage)
							uiImage = UIImage(cgImage: cgImage)

UIImage can display CIImage but to save it as a file
it has to be rendered first

Image Filtering

cat before
cat after
Filter is a piece of software that examines an input image pixel by pixel
and algorithmically applies some effect in order to create an output image

Discovering Filters

							CIFilter.filterNames(inCategories: nil) // all availiable
							CIFilter.filterNames(inCategory: category)

Core Image provides methods that let you query the system
for the available built-in filters

Filter Category

  • The type of effect produced by the filter
  • The usage of the filter (still image, video, etc)
  • Whether the filter is provided by Core Image

A filter can be a member of more than one category

Filter Configuration

							let filter = CIFilter(name: "CIThermal")
							filter.setValue(uiImage.ciImage!, forKey: kCIInputImageKey)
							if let output = filter.outputImage {
								let cgImage = CIContext().createCGImage(output, from: output.extent)
								uiImage = UIImage(cgImage: cgImage)

There are some useful Swift wrappers for filters out there:

Filter Chaining

							let ciImage = CIImage(image: uiImage)
							let chainedResult = ciImage.applyingFilter("CIPhotoEffectProcess",
								withInputParameters: nil).applyingFilter("CIBloom",
									withInputParameters: [
										kCIInputRadiusKey: 10.0,
										kCIInputIntensityKey: 1.0

Core Image combines chained filters into a single operation

Related Resources