Understanding Misaligned Images in the iOS Simulator

Today, we'll discuss this setting in more detail and we'll see how we can improve our app's rendering performance by addressing issues related to misaligned images.

I've been making iPhone apps for years, but I've never really investigated this feature. Now that I know what it does, I wonder how many of my previous apps have been dealing with this issue.

Let's make sure you don't make that same mistake.

Simply put, this feature helps you identify instances where a view's position and dimensions don't fully align with the pixels on a physical device.

The documentation for a CGPoint shows that we can initialize it with fractional values:

init(x: Double, y: Double)

CoreGraphics does not complain if you try to render fractional pixels and will not raise any warnings. Instead, CoreGraphics will perform anti-aliasing in order to render a fractional pixel as effectively as possible.

In other words, misaligned views' pixels need to be interpolated with surrounding pixels to determine accurate color values. This is not only computationally expensive but may also create blurry images.

Aliasing is the visual stair-stepping of edges that occurs in an image when the resolution is too low. Anti-aliasing is the smoothing of jagged edges in digital images by averaging the colors of the pixels at a boundary.
Adobe
The letter on the left is aliased. The letter on the right has had anti-aliasing applied to make the edges appear smoother.

If you calculate the dimensions of a view's frame rather than using AutoLayout constraints, you will often encounter misaligned images. Although frame-based layouts are becoming less common, you may still use this approach if you are building custom UI components, complex animations, etc.

So, let's intentionally create this problem and then figure out how to fix it:

  • Magenta overlays are caused by subpixel misalignment
  • Yellow overlays are caused by stretching

let rect = CGRect(x: 30.4, y: 50.4323, width: 100.12, height: 100.23)
let misalignedLabel = UILabel(frame: rect)
misalignedLabel.text = "Hello, world!"        
view.addSubview(misalignedLabel)

To fix this issue, we can simply do the following:

let rect = CGRect(x: 30, y: 175, width: ceil(100.12), height: ceil(100.23))
let alignedView = UILabel(frame: rect)
alignedView.text = "Hello, world!"
view.addSubview(alignedView)
The only change we've made here is to ensure that all our inputs to rect are Int

Alternatively, we can leverage CGGeometry's integral property:

/// A rectangle with the smallest integer values for its origin and size that contains the source rectangle.
/// That is, given a rectangle with fractional origin or size values, integral rounds the rectangle’s origin
/// downward and its size upward to the nearest whole integers, such that the result contains the
/// original rectangle.
///
/// Returns a null rectangle if rect is a null rectangle.
let rect = CGRect(x: 30, y: 175, width: 100.12, height: 100.23)
let alignedViewIntegral = UILabel(frame: rect.integral)
alignedViewIntegral.text = "Hello, world!"
view.addSubview(alignedViewIntegral)

Both of these approaches resolve the issue:

I hope you now have a clearer understanding of how this feature works and how to resolve any misalignment issues you may encounter.

See you next time!


If you're interested in more articles about iOS Development & Swift, check out my YouTube channel or follow me on Twitter.

If you're an indie iOS developer, make sure to check out my newsletter:

Indie Watch
Indie Watch is an exclusive weekly hand-curated newsletter showcasing the best iOS, macOS, watchOS, and tvOS apps from developers worldwide.

I feature a new developer every issue, so feel free to submit your indie iOS apps!


Do you have an iOS Interview coming up?

Check out my book Ace The iOS Interview!


Subscribe to Digital Bunker

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
[email protected]
Subscribe