Add rich graphics to your SwiftUI app

Description: Learn how you can bring your graphics to life with SwiftUI. We’ll begin by working with safe areas, including the keyboard safe area, and learn how to design beautiful, edge-to-edge graphics that won’t underlap the on-screen keyboard. We’ll also explore the materials and vibrancy you can use in SwiftUI to create easily customizable backgrounds and controls, and go over graphics APIs like drawingGroup and the all new canvas. With these tools, it’s simpler than ever to design fully interactive and interruptible animations and graphics in SwiftUI.

Safe area

  • By default, SwiftUI positions your content within the safe area, avoiding anything that would obscure or clip your view, like the Home indicator or any bars that are being shown.
  • The safe area is represented as a region that is inset from the outermost full area where a view is shown.

Background/Foreground view modifiers

.background() view modifier has new overloads with new behaviors:

  • previous .background() view modifiers were applied with the same bounds to the view they were called on.
  • to make previous .background() effects go beyond the safe area, we had to call .ignoreSafeArea() on the background content, e.g. .background { }
  • the new background modifier gives you this behavior automatically by default
  • the new overloads can accept:
    • a specific style, like Color, Gradient, or the new materials (think UIKit's UIVibrancyEffect)
    • a shape, to clip the background to a specific shape (when we use this, the background no longer goes beyond the safe area)

.foregroundStyle(_:) is a new view modifier that applies styles on top of another view.

  • similar to the new background overloads, it accepts styles
  • when passing a ForegroundStyle between .secondary and .quaternary, the content might be shown with an effect called Vibrancy, which blends the colors behind it. This happens when you explicitly add a background with a material, or when your content is in a system component, like a sidebar, that adds the material for you.
  • Any given text can have a single foreground style applied to it, but multiple colors within its ranges (thanks to the new AttributedString API).

.safeAreaInset(edge:content:) is a new modifier that lets us reduce the content safe area of the view is applied to.

  • it kind of work like an overlay, where the overlay placement is now considered part of the unsafe area
  • for example, this allows to place views on top of ScrollView, and the edges of the scroll view content (not frame) will not be obscured by those views defined within .safeAreaInset(edge:content:)


Previously we had the drawingGroup(opaque:colorMode:) view modifier:

  • drawingGroup tells SwiftUI to combine all of the views it contains in a single layer to draw
  • drawingGroup works well for graphical elements, but shouldn't be used with UI controls, like text fields and lists

New Canvas view

  • addresses some drawingGroup(opaque:colorMode:) shortcoming such as bookkeeping and storage required for each view
  • similar to UIKit/AppKit drawRect


Canvas { context, size in 
  var image = context.resolve(Image(systemName: "sparkle"))
  image.shading = .color(.blue)
  let imageSize = image.size 
  context.blendMode = .screen

  for i in 0..<10 {
    let frame = CGRect(
      x: 0.5 * size.width + Double(i) * imageSize.width, y: 0.5 * size. height, 
      width: imageSize.width, height: imageSize.height
    var innerContext = context
    innerContext.opacity = 0.5 
    innerContext.fill(Ellipse().path (in: frame), with: .color (.cyan))
    context.draw(image, in: frame) 


  • new view letting you control exactly how something changes over time
  • configurable via schedules like timers and animations

Missing anything? Corrections? Contributions are welcome 😃


Written by

Federico Zanetello

Federico Zanetello

iOS Engineer with strong passion for Swift, minimalism, and design. When he’s not busy automating things, he can be found writing at FIVE STARS and/or playing with the latest shiny toys.