Prototyping With SwiftUI: Creating Complex Interactions Using Gestures and Modifiers

7 min read

Gestures make it easy to give your app’s touch interactions a bit of personality and color. In this post, you’ll learn how to create your own gestures and modifiers and apply them to interactions throughout your app using SwiftUI.

Amos G.
Amos G.
Published January 17, 2022 Updated February 18, 2022
Creating Complex Interactions Using Gestures and Modifiers

In part two of this series, you’ll use our iOS Chat SDK sample application to prototype several gestures that you’ll use for refreshing page content, adding seamless swiping and pagination to message lists and photos, revealing in-app actions to messages in message channels, and more.

You’ll also apply modifiers to these gestures so you can control every aspect of your app’s touch interactions, resulting in a truly customized app with a seamless user experience.

If you haven’t yet, review part one of this series, Prototyping Stream’s iOS Chat SDK With SwiftUI: Part 1. After you’ve caught up, feel free to dive into part two, or check out our SwiftUI Chat Application to learn more.

Ready? Let’s get started.

Setup

You need to download and install Xcode (13+) to run the project files. If you don’t have Xcode installed, download it from the Mac App Store.

You can also download the project files from this GitHub repo and follow the sections below to begin creating the interactions in this project.

There are several files and folders in the Xcode project, but the following are the Swift files you’ll need:

  • ChannelListView.swift
  • ContextMenuView.swift
  • SwipeToDeleteView.swift
  • PhotoGalleryZoom.swift
  • PhotoGallery.swift
  • ReactionsView.swift

Want to learn more about SwiftUI? Check out our SwiftUI Chat tutorial to see how you can get started and integrate it into your project.

How to Make the Channel List Scrollable and Refreshable

In SwiftUI, you can make interface elements automatically scrollable by embedding them in a List layout container. A list view displays data in several rows and arranges the rows in a single column.

After creating your Xcode project, create a new Swift file called ChannelList.swift and enter the following code:

swift
//
//  ContentView.swift
//  Stream iOS Chat SDK Prototyping
//
//  Created by Amos from getstream.io on 14.10.2021.
//

import SwiftUI

struct ChannelListView: View {
    
    let StreamBlue = Color(#colorLiteral(red: 0, green: 0.368627451, blue: 1, alpha: 1))
    let notificationColor = Color(#colorLiteral(red: 1, green: 0.2156862745, blue: 0.2588235294, alpha: 1))
    let onlineColor = Color(#colorLiteral(red: 0.1254901961, green: 0.8784313725, blue: 0.4392156863, alpha: 1))
    let appBarColor = Color(#colorLiteral(red: 0.07058823529, green: 0.07843137255, blue: 0.0862745098, alpha: 1))

You can also find the code of the list by downloading the project from GitHub. In the project’s folder structure, look for the folder ChannelList and the Swift file, ChannelListView.swift.

The list pulls data from another Swift file called ChannelListData.swift, which can also be found in this project.

The ChannelListData file creates the composition of the list using the layout containers HStack, ZStack, VStack, and Spacer. By wrapping the container views in the List view, the content becomes scrollable by default.

You can change the appearance of the list using list styles. For example, to convert the list to a plain list, add the .listStyle modifier and set its parameter to .plain as seen in the code above.

Since this is a long scrolling list, you can improve the user experience so that when users perform a standard drag gesture on the list, it displays a visual cue that shows the content is updating.

You can do this with the refreshable modifier in SwiftUI. To allow users to refresh the contents of the list, apply the refreshable modifier to it, creating the “pull-to-refresh” effect.

Display the Context Menus and Reactions Using the Tap-and-Hold Gesture

To display the context menu for chat messages, you need to attach a long-press gesture to the message bubbles. The context menu is used to show additional information and actions such as Reply, Copy, Message, Edit Message, and Delete Message.

In this section, you will show the context menu by tapping and holding the message bubble. Here’s how:

  1. Add the .contextMenu modifier to any view to display its contextual information or actions related to it. In this example, you need to attach the modifier to the container for the user avatar, the message bubble, and the text below it.
  2. In the .contextMenu, use a label consisting of text and an SF Symbol to represent each menu item as shown in the code below.
swift
//
//  ContextMenuView.swift
//  Stream iOS Chat SDK Prototyping
//  Longpressing inbound message
//  Created by Amos from getstream.io on 14.10.2021.
//

import SwiftUI

struct ContextMenuView: View {
    
    let inboundBubbleColor = Color(#colorLiteral(red: 0.2419013083, green: 0.2265482247, blue: 0.2486716509, alpha: 1))
    
    var body: some View {
        ZStack {

Building the Swipe Actions

SwiftUI’s .swipeActions() modifier allows you to swipe a list row to reveal actions related to the row.

To make the list row swipeable and display its associated actions, add the .swipeActions() modifier to the list row consisting of the user avatar, user name, message summary delivery receipt, and timestamp.

You can specify which side of the list the actions belong to with the edge parameter. Add .swipeActions(edge: .trailing) to the .swipeActions() modifier to show the actions on the right side of the list row.

Additionally, you can display the actions on the left side of the list row by specifying .swipeActions(edge: .leading).

swift
//
//  SwipeToDeleteView.swift
//  StreamiOSChatSDKPrototyping
//
//  Created by Amos from getstream.io on 14.10.2021.
//

import SwiftUI

struct SwipeToDeleteView: View {
    var messages: [ChannelListStructure] = []
    let notificationColor = Color(#colorLiteral(red: 1, green: 0.2156862745, blue: 0.2588235294, alpha: 1))
    let onlineColor = Color(#colorLiteral(red: 0.1254901961, green: 0.8784313725, blue: 0.4392156863, alpha: 1))
    var body: some View {
       

How to Create the Photo-Zoom Effect

Building your own app? Get early access to our Livestream or Video Calling API and launch in days!

When you attach an image and send it as a message, you can zoom in and out by applying a double-tap gesture to the image. To create the image zooming interaction, add the following code to any image uploaded to the assets folder.

swift
//
//  PhotosGalleryZoom.swift

// This creates Page Scrolling Style Interaction for Photos (with index display mode)
// Swipping or flicking through photos in a photo gallery
// ALWAYS: Use the "page" tabview style and set the index tab view style to "always"
// Created by Amos from getstream.io on 17/12/2021.
//
//  INTERACTION STYLE
//  1. Double tap to transition between fit mode and fill/fullscreen mode
//  3. In the fullscreen mode, tap the xmark (x) to transition from fill/fullscreen mode to fit mode
//  4. Drag the sheet downwards to dismiss it

import SwiftUI

This example uses the image iceland2 taken from the Xcode assets library. After adding your image:

  1. Apply the .resizable() modifier to allow the image to automatically resize to fill the available space.
  2. Define the state variable @State private var fullscreen = false at the top of your code where you can define variables.
  3. Add the aspect ratio modifier .aspectRatio(contentMode: fullscreen ? .fill : .fit) to the image and use the fullscreen state variable along with the ternary operator so that the image can switch between fullscreen (.fill) and normal (.fit) modes.
  4. Finally, add the tap gesture .onTapGesture(count: 2) to the image with the count parameter set to two so that you can double tap the image to switch between the fill and fit modes. To get the spring effect when the image transitions from the fill to fit mode, add an explicit animation using withAnimation with an interpolating spring and set the spring parameters as seen above.

Visit Hacking with Swift to learn more about how to create an explicit animation in SwiftUI.

Building the Flicking/Swiping Interaction

When you send a message containing two or more images, you can cycle through the images using a flick or a swipe gesture.

To create a flicking/swiping interaction, pick two or more images from your Xcode assets library and create a new file called PhotosGalleryZoom.

Then, add the following code:

swift
//
//  PhotosGalleryZoom.swift

// This creates Page Scrolling Style Interaction for Photos (with index display mode)
// Swipping or flicking through photos in a photo gallery
// ALWAYS: Use the "page" tabview style and set the index tab view style to "always"
// Created by Amos from getstream.io on 17/12/2021.
//
//  INTERACTION STYLE
//  1. Double tap to transition between fit mode and fill/fullscreen mode
//  3. In the fullscreen mode, tap the xmark (x) to transition from fill/fullscreen mode to fit mode
//  4. Drag the sheet downwards to dismiss it

import SwiftUI

Let’s discuss what’s happening in this code block:

Notice we’re using a TabView as the parent layout container for the six images. To get the paginated scrolling effect we want from flicking/swiping an image, set the .tabViewStyleto .page using the .tabViewStyle(.page) modifier.

You can show or hide the index (the small dots used for cycling through the images) using the .indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .always)) modifier.

To learn more about how to use a tab view, visit the Apple Developer documentation.

To get our zoom effect, add another Swift file called PhotosInGallery to create a gallery of four images.

swift
//
//  PhotosGallery.swift

//  INTERACTION STYLE
//  1. Single tap to fit the photo to the center of the screen using tab view and sheet
//  2. Double tap to transition between fit mode and fill/fullscreen mode
//  3. In the fullscreen mode, tap the xmark (x) to transition from fill/fullscreen mode to fit mode
//  4. Drag the sheet downwards to dismiss it

import SwiftUI

struct PhotosGallery: View {
    @State private var showSheet = false
    let inboundBubbleColor = Color(#colorLiteral(red: 0.07058823529, green: 0.07843137255, blue: 0.0862745098, alpha: 1))
    @State private var unzoomed = true // Single tap to fit to center

To present the expanded images over the image gallery, you need to use the sheet modifier in SwiftUI. The sheet modifier also allows you to dismiss the images using a drag gesture.

To use the sheet presentation:

  • Add the modifier called .sheet(isPresented: $showSheet) { PhotosGalleryZoom() } to the first image in the gallery iceland2.
  • Next, give the sheet some content to display. This can be an image view or a text view. Use PhotosGalleryZoom() as the content to show and add the boolean called isPresented that states whether the expanded image views (detailed views) should be displayed or not.
  • Finally, add the .onTapGesture { showSheet.toggle() } modifier to one of the images so that you can tap it to show the modal sheet.

How to Trigger the “Like” Animation Using a Tap Gesture

In SwiftUI, you can make a view recognize one or more user taps using the .onTapGesture modifier.

In this example, you should add the tap gesture to the heart icon to trigger the animation and change the icon’s state from “unliked” to “liked” as shown in the code below. You can do this using conditional visibility (‘if and else’) statements in SwiftUI.

To trigger the “like” animation, create a new Swift file called ReactionsView.swift and replace its content with the code below. The code presents the heart animation seen above when the icon is tapped.

In part 3 of this tutorial, you will learn how to build the animation from scratch so don’t worry about it now. This section aims at showing you how to use a tap gesture to initiate animations in SwiftUI.

swift
//
//  ReactionsView.swift
//  Stream iOS Chat SDK Prototyping
//
//  Created by Amos from getstream.io on 09.01.2022.
//

import SwiftUI

struct ReactionsView: View {
    
    let reactionsBGColor = Color(#colorLiteral(red: 0.07058823529, green: 0.07843137255, blue: 0.0862745098, alpha: 1))
    
    // Like Animation States
    @State private var notLiked = true

As you can see from the code above, the .onTapGesture is attached to the heart icons, which are embedded in a ZStack layout container. This allows the pink heart icon to appear on the top of the gray heart icon.

The visibility of the heart icons is controlled using ‘if and else’ statements. So, if the user has not tapped the heart icon, the gray one is presented. When the user taps the gray heart icon, it becomes hidden and the pink version is then presented.

To see the “like” animations when the heart icon is tapped, you have to embed the easing equations of the animation as well as the final states of the animation inside the .onTapGesture.

Conclusion

Well done! This tutorial covered how to prototype interaction styles for the Stream iOS Chat SDK using SwiftUI.

You learned how to make SwiftUI list views refreshable and scrollable, how to use swipe actions, how to add in paginated scrolling, and how to trigger animations with human-initiated gestures.

You can download the SwiftUI source codes for this project from this GitHub repository.

In part three of this tutorial, you will learn how to create chat messaging related animations with SwiftUI.

decorative lines
Integrating Video With Your App?
We've built an audio and video solution just for you. Launch in days with our new APIs & SDKs!
Check out the BETA!