Ghostly Corners with Overlaid Shapes

Design Notes Diary

A small coda for yesterday’s exploration of corners.

I recently was able to finally track down a bug which has been frustrating me for a long time. Widgetsmith’s interface includes several places where I displays previews of widgets. In these views I could occasionally see dark, ghostly outlines along the edges of the preview. I’ve exaggerated the effect here a bit to make it easier to see:

I tried all manner of things to fix this but was thwarted at finding a lasting solution until last week.

To solve this I created a super simple testing project to see if I could isolate the issue. A simple ZStack with two identical RoundedRectangle views on top of each other:

This results in the following view:

You can see the edges of the lower black round-rect peeking through the top rounded rectangle. Intuitively this makes absolutely no sense to me. The two shapes should be completely identical so overlaying them should fully occlude the lower shape…but that isn’t what happens.

I posted about this on Mastodon and was very helpfully pointed in the right direction as to the cause. Anti-aliasing.

The shapes aren’t in fact actually solid, instead when they are pixelated their edges will acquire small bits of transparency along an irregular grid to help smooth out their visual appearance. Which is then how the lower view can bleed through into the foreground.

This is more pronounced when using the .continuous shape style because more of the edge isn’t straight but it will also occur when .circular styles. In fact, the artifact is incredibly visible if I switch to a stack of Circle shapes.

The solution to avoiding this will vary based on your app’s needs. I bring this up mostly to illuminate this as a potential design need and something to look out for whenever you are stacking similar shapes. If you see weird ghostly outlines of hidden shapes, now you know why.

For me, the lower shape is actually there to account for situations where the topmost view includes some transparency, so it doesn’t actually need to extend to the edge of the view completely. So I just add a .inset(1) modifier to the background shape and the problem goes away.

David Smith