Share your work and get the
Latest iOS dev beans

Stay up to date with articles, tips and apps submitted by the dev community delivered directly to your inbox. Swift, iOS, macOS, watchOS, SwiftUI, UIKit and more.
Curated by Tiago Henriques and published every week.

No spam, ever. Unsubscribe at any time.
By subscribing you consent to processing of your data
as described in the privacy policy.

Issue #33

February 17, 2025

πŸ”Ά How matchedGeometryEffect() came to the rescue πŸ¦Έβ€β™‚οΈ

Sponsored

Play with native iOS components using UI Playground.

Explore and customise native UI components to unlock endless possibilities for your app. Take it to the next level by exporting native SwiftUI code, streamlining development.

Welcome to issue #33 of the iOS Coffee Break Newsletter πŸ“¬.

At a previous sprint at work, I had faced a quite interesting challenge where I had to create an animated transition when selecting items from a grid. The goal was to smoothly move the selected item within the grid.

Initially, I experimented with various withAnimation options but couldn't achieve the desired effect ... until I tried out using SwiftUI's matchedGeometryEffect() modifier πŸŽ‰!

Overview

This week, I chose to highlight the matchedGeometryEffect() modifier because it is a great tool for creating smooth, custom transitions between views β€” perfect even if you are not confident with animations!

Using unique identifiers, you can blend the geometry of two views with the same identifier creating an animated transition. Transitions like this can be useful for navigation or changing the state of UI elements for example.

Steps to enable the Matched Geometry Effect:

  • Assign a unique identifier to each view involved in the transition.
  • Declare a namespace using the @Namespace property wrapper to group view identifiers.
  • Apply the .matchedGeometryEffect(id:in:properties:anchor:isSource:) modifier to the views participating in the animation.

Once set up, when the animation is triggered, views with the same identifier within the same namespace will seamlessly transition between their states.

My Coffees App

I have assembled a practical example to demonstrate the advantages of using this view modifier. Let's imagine you have an app that displays a grid of different types of coffee β˜• and you want to choose your favorites from that list - moving the selected ones from the one grid to the other with a smooth animation!

Here is a preview of what I am aiming for:

Here is the code:

struct MyCoffeesView: View {
    [...]
 
    @Namespace private var namespace
    private let coffeesNamespace = "coffeesNamespace"
 
    var body: some View {
        ScrollView {
            VStack {
                HeaderView(title: "Favorites")
 
                LazyVGrid(columns: columns) {
                    if favoriteCoffees.isEmpty {
                        CoffeePlaceholderView()
                    } else {
                        ForEach(favoriteCoffees) { coffee in
                            CoffeeView(coffee: coffee)
                                .matchedGeometryEffect(
                                    id: "\(coffee.name)_\(coffeesNamespace)", in: namespace
                                )
                                .onTapGesture {
                                    withAnimation(.easeIn) {
                                        favoriteCoffees.removeAll { $0.id == coffee.id }
                                    }
                                }
                        }
                    }
                }
 
                HeaderView(title: "Suggested")
 
                LazyVGrid(columns: columns) {
                    if filteredSuggestedCoffees.isEmpty {
                        CoffeePlaceholderView()
                    } else {
                        ForEach(filteredSuggestedCoffees) { coffee in
                            CoffeeView(coffee: coffee)
                                .matchedGeometryEffect(
                                    id: "\(coffee.name)_\(coffeesNamespace)", in: namespace
                                )
                                .onTapGesture {
                                    withAnimation(.easeIn) {
                                        favoriteCoffees.append(coffee)
                                    }
                                }
                        }
                    }
                }
            }
            .padding(.all)
        }
        .navigationBarTitle("My Coffees", displayMode: .inline)
    }
}

To explore the full implementation of the sample application, visit the repository on GitHub.

CURATED FROM THE COMMUNITY

🌳 Let's Build: The tree Program - Part I

SwiftToolkit is launching a new series called Let's Build!

In the first article of the series, you will explore file system traversal while building a directory listing tool. This guide will walk you through using PathKit and the Swift Argument Parser to efficiently navigate and display directory structures.

⛔️ Task Cancellation in Swift Concurrency

In most cases, Swift Concurrency automatically manages task cancellation, but there are situations where manual handling is necessary.

Majid's latest article explores Swift's cooperative cancellation model, explaining how developers can take control of task cancellation and decide when and how to stop execution!

πŸ‘·πŸ»β€β™‚οΈ Presenting and Managing Expandable Sections in SwiftUI

Expanding and collapsing views is essential for improving user experience. This is particularly useful when deciding which information to display in limited space.

SerialCoder's latest article covers how to manage the expanded state programmatically and how to let users expand and collapse sections on demand - all done in SwiftUI!