<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bez</title>
    <description>The latest articles on DEV Community by Bez (@knightbenax).</description>
    <link>https://dev.to/knightbenax</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F291556%2Fa1ffc4e6-1333-4a2f-85de-a0b31325ac88.jpg</url>
      <title>DEV Community: Bez</title>
      <link>https://dev.to/knightbenax</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/knightbenax"/>
    <language>en</language>
    <item>
      <title>Building an Elegant Stacked Notification Banner in SwiftUI</title>
      <dc:creator>Bez</dc:creator>
      <pubDate>Tue, 25 Aug 2026 16:12:00 +0000</pubDate>
      <link>https://dev.to/knightbenax/building-an-elegant-stacked-notification-banner-in-swiftui-4m63</link>
      <guid>https://dev.to/knightbenax/building-an-elegant-stacked-notification-banner-in-swiftui-4m63</guid>
      <description>&lt;p&gt;In modern app design, managing screen real estate is critical. In our apps, &lt;a href="https://blark.app" rel="noopener noreferrer"&gt;Blark: a beautifully designed PRO monochrome camera app for iPhone and iPad&lt;/a&gt;, and &lt;a href="https://plomer.app" rel="noopener noreferrer"&gt;Plomer: a website &amp;amp; domain monitoring app for macOS, iPadOS, and iOS&lt;/a&gt;, we needed a way to communicate multiple alerts, errors, or updates to the user; standard pop-ups felt intrusive, and simple lists took up too much space.&lt;/p&gt;

&lt;p&gt;Our solution? A stacked notification banner.&lt;/p&gt;

&lt;p&gt;By layering messages on top of one another and letting users dismiss them one by one, you create a tactile, visually engaging experience that keeps your UI clean. In this article, we'll walk through how to build a dynamic, animated, stacked banner view using SwiftUI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Concept&lt;/strong&gt;&lt;br&gt;
Our goal is to build a &lt;code&gt;BannerView&lt;/code&gt; that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Takes an array of notification messages.&lt;/li&gt;
&lt;li&gt;Displays the most recent (or most urgent) message prominently at the front.&lt;/li&gt;
&lt;li&gt;Shows older messages as a "stack" behind the active message.&lt;/li&gt;
&lt;li&gt;Allows the user to dismiss the top message, smoothly animating the next message into the active spot.&lt;/li&gt;
&lt;li&gt;Changes styling dynamically based on the severity of the alert (e.g., standard vs. critical issues).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Managing the&amp;nbsp;Data&lt;/strong&gt;&lt;br&gt;
While the focus is on the UI, our view relies on an underlying data model, which we'll call &lt;code&gt;BannerMessageAction&lt;/code&gt;. It holds the message string, a subtitle/body, an issue type (to dictate color coding), and a tap action. Our view will take a &lt;code&gt;@Binding&lt;/code&gt; array of these models so we can mutate the stack (remove items) directly from the UI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: The ZStack Foundation&lt;/strong&gt;&lt;br&gt;
To create a stacked effect, we naturally reach for a &lt;code&gt;ZStack&lt;/code&gt;. The logic is split into two parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Background Stack:&lt;/strong&gt; A limited number of older messages resting behind the main card.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Active Card:&lt;/strong&gt; The top-most message that the user can interact with.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rendering the Background Cards&lt;/strong&gt;&lt;br&gt;
We don't want to render 50 overlapping cards if the user has a lot of notifications. Instead, we limit the visual depth using a &lt;code&gt;maxLength&lt;/code&gt; property (defaulting to 2).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ForEach(Array(messages.dropLast().prefix(maxLength).enumerated()), id: \.element.id) { index, message in 
    // Card UI here...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;By dropping the last element (which represents our active top card) and prefixing the rest to our &lt;code&gt;maxLength&lt;/code&gt;, we ensure only a few cards are rendered in the background.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: The Magic of Offsets and&amp;nbsp;Padding&lt;/strong&gt;&lt;br&gt;
The true "stacked" 3D effect comes from manipulating padding and Y-offsets based on the card's index in the array.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.padding(.horizontal, CGFloat(maxLength - index) * 8)
.offset(y: CGFloat(maxLength - index) * -8)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Horizontal Padding&lt;/strong&gt;: The further back a card is in the stack (calculated via &lt;code&gt;maxLength - index&lt;/code&gt;), the more horizontal padding it receives, making it look narrower than the card in front of it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Vertical Offset&lt;/strong&gt;: We push the older cards upwards (&lt;code&gt;-8&lt;/code&gt; points per index step) so they peek out from behind the top card.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Adding Life with Transitions&lt;/strong&gt;&lt;br&gt;
A static stack is fine, but SwiftUI makes it incredibly easy to add delightful interactions. When a user taps the "multiply" (close) button, we remove the last item from the array wrapped in a spring animation:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func removeFromStack(){
    withAnimation(.spring(response: 0.4, dampingFraction: 0.8)){
        messages.removeLast()
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To make the entrance and exit of these cards feel organic, we apply a &lt;code&gt;.asymmetric&lt;/code&gt; transition to the cards.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.transition(.asymmetric(
    insertion: .opacity.combined(with: .move(edge: .bottom)),
    removal: .opacity.combined(with: .scale(scale: 0.9))
))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When a notification arrives, it slides up from the bottom while fading in. When you dismiss it, it scales down slightly and fades out, letting the card behind it snap into focus.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Complete Implementation&lt;/strong&gt;&lt;br&gt;
Here is the complete code for the BannerView. You'll notice conditional styling based on &lt;code&gt;message.issueType&lt;/code&gt;, allowing the banner to swap between an attention-grabbing red for serious issues/warning and a softer yellow for standard updates.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqp1km290jumjf1pzazs0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqp1km290jumjf1pzazs0.png" alt="Works well on macOS too" width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
By combining &lt;code&gt;ZStack&lt;/code&gt;, dynamic offsets, and asymmetric transitions, you can create a highly interactive and visually appealing notification system. This approach not only saves screen space but also encourages users to actively engage with your app's alerts rather than ignoring a static list.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://apps.apple.com/us/app/blark-pro-black-white-camera/id6742157071" rel="noopener noreferrer"&gt;Blark: a beautifully designed PRO monochrome camera app for iPhone and iPad&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apps.apple.com/us/app/plomer/id6741927051" rel="noopener noreferrer"&gt;Plomer: a website &amp;amp; domain monitoring app for macOS, iPadOS and iOS&lt;/a&gt;&lt;/p&gt;

</description>
      <category>swift</category>
      <category>mobile</category>
      <category>ui</category>
      <category>swiftui</category>
    </item>
  </channel>
</rss>
