I solely created an app called Meshing to help developers copy mesh gradient code to their SwiftUI project after visualizing it in the app.

Meshing: Explore Mesh Gradients for SwiftUI!
Meshing is the tool you need for working with stunning MeshGradients in SwiftUI with ease!Key Features:- Intuitive visual editor for 2x2, 3x3, and 4x4 MeshGradients- Real-time preview of your gradient creations- Precise control over color positions with draggable points- Customizable background color- Toggle for smooth color transitions- One-click code generation for integration into your SwiftUI projectsPerfect for:- Apple Platforms Developers- UI/UX Designers- SwiftUI Enthusiasts- Anyone looking to add vibrant, complex gradients to their appsBoost Your Workflow!- Experiment with complex gradients in seconds- Save hours of manual coding and tweaking- Achieve professional-looking results quicklyI was tired of working with Previews in Xcode for mesh gradients, so I created an app for it. MeshGradients is a fun visual experience. No more guesswork or manual coding – design the perfect gradient visually and let Meshing generate the SwiftUI code for you!!System Requirements:- macOS 15.0, iOS 18.0, iPadOS 18.0, visionOS 2.0 or later- Xcode 16.0 or later (for use in SwiftUI projects)Download now and start creating beautiful, complex gradients in minutes!

One day, I got a message from a designer. They loved the app but did not use Xcode. They just wanted to visualize the gradient and save it as an image. This opened up a whole new audience for me. While the feature set was complete for developers top copy the code, my next focus was on how could I make Meshing work for everyone?

While doing my research, I found out about ImageRenderer. Apple added it to SwiftUI in iOS 16, a class that turns SwiftUI views into images. While this post was initially supposed to be about working with ImageRenderer, I spiralled into exploring Color Rendering Modes and decided to write about it instead.

Creating Your ImageRenderer

You start by making an ImageRenderer. You give it a SwiftUI view to work with. It looks like this:

let renderer = ImageRenderer(content: YourSwiftUIView())

Color Rendering Modes

When working with ImageRenderer, you can choose how you want your colors to be handled. Maybe you can think of it as choosing the color palette for your image. It decides how colors are processed and stored. Apple gives us three options:

  • nonLinear
  • linear
  • extendedLinear

Let's break these down:

1. nonLinear

This is the default mode. It uses the sRGB color space, which is what most screens use.

renderer.colorMode = .nonLinear

Pros:

  • It is good for most images you will share or display.
  • Colors look natural on most devices.

Cons:

  • Cannot safely handle colors outside the 0 to 1 range (produces undefined results).

2. linear

This mode also uses sRGB, but without gamma correction.

renderer.colorMode = .linear

Gamma correction adjusts color brightness to match how our eyes perceive light. It makes screens display colors in a way that looks natural to us. linear mode skips this adjustment.

Without gamma correction:

  • Colors appear as raw values
  • Images might look darker than expected

Pros:

  • It is great for doing color math or blending.
  • What you see is what you get - no hidden adjustments.

Cons:

  • Images might look a bit flat or washed out on some screens.
  • Like nonLinear, cannot safely handle colors outside the 0 to 1 range (produces undefined results).

3. extendedLinear

This is the powerhouse mode. It can handle a wider range of colors.

renderer.colorMode = .extendedLinear

Normally, color values range from 0 (none) to 1 (full intensity). extendedLinear allows values below 0 or above 1. This captures a wider range of light intensities, like very bright highlights or deep shadows. It is useful for HDR (High Dynamic Range) content or complex lighting scenarios.

For most everyday use, you probably do not need this extended range. But for specialized graphics work or HDR content, it can be invaluable.

Pros:

  • It can work with colors outside the normal 0 to 1 range.
  • Great for HDR content or when you need extra color precision.

Cons:

  • Might not display correctly on all devices.
  • Can use more memory.

When to Use Each Mode

  • Use nonLinear for most everyday images.
  • Use linear if you are doing a lot of color calculations or blending.
  • Use extendedLinear if you are working with HDR content or need to preserve a wide range of color values.

Testing Color Modes

Here is a code to display the same image with different color modes. It is a great way to see which one works best for your needs, by simply replacing SampleGradientView with your own view:

struct ColorModeComparisonView: View {
  @State private var images: [UIImage] = []

  var body: some View {
    VStack {
      ForEach(Array(zip(images, ColorRenderingMode.allCases)), id: \.0) { image, label in
        VStack {
          Image(uiImage: image)
            .resizable()

          Text(label.description)
            .fontWidth(.expanded)
        }
      }
    }
    .task {
      generateImages()
    }
  }

  func generateImages() {
    images = ColorRenderingMode.allCases.compactMap { mode in
      let renderer = ImageRenderer(content: SampleGradientView())
      renderer.colorMode = mode
      return renderer.uiImage
    }
  }
}

extension ColorRenderingMode: @retroactive CaseIterable, @retroactive CustomStringConvertible {
  public var description: String {
    switch self {
      case .nonLinear: "Non Linear"
      case .linear: "Linear"
      case .extendedLinear: "Extended Linear"
      @unknown default: ""
    }
  }

  public static var allCases: [ColorRenderingMode] {
    [.nonLinear, .linear, .extendedLinear]
  }
}

struct SampleGradientView: View {
  var body: some View {
    LinearGradient(colors: [.purple, .indigo], startPoint: .topLeading, endPoint: .bottomTrailing)
  }
}

#Preview("ColorModeComparisonView") {
  ColorModeComparisonView()
}

Then I compared it on the iPad:

In my Meshing app, I stuck with nonLinear which is the default color mode:

func exportGradient() -> UIImage? {
  viewModel.showDots = false
  let renderer = ImageRenderer(content: GradientPreview(viewModel: viewModel, padding: 0))
  return renderer.uiImage
}

I chose nonLinear because:

  1. My gradients use standard colors.
  2. I want the exported images to look good on most devices.
  3. It is the safest choice for sharing on social media or using in other apps.

But if you are doing something special with colors, do not be afraid to experiment with the other modes!

Wrapping Up

Understanding color modes can help with complex SwiftUI views when working with ImageRenderer. It is about exporting beautiful pictures and making sure those pictures look great everywhere they are seen.

Have you played around with different color modes in your projects? I would love to hear about your experiences! Message on @rudrankriyam and let's geek out about colors together; maybe I can learn something from you!

x.com
String Catalog

String Catalog - App Localization on Autopilot

Push to GitHub, and we'll automatically localize your app for 40+ languages, saving you hours of manual work.

Tagged in: