Jean-Marc Boullianne

This week’s SwiftUI tutorial is focusing on a popular running and cycling app, Strava. In this tutorial, I’ll walk through how I recreated the activity history graph which is displayed inside the Strava app. As usual, I’ll break it down into bite-size chunks and explain along the way.

Image for post
Image for post
Strava Activity History View

If you found this tutorial helpful, please consider subscribing using this link, and if you aren’t reading this on TrailingClosure.com, please come check us out sometime!

We’ll break this post up into a few different parts. …


Easiest method for implementing a custom style TabBar in SwiftUI

Image for post
Image for post
Custom TabBar in SwiftUI

Here’s an easy quick tip for this week. Have you ever wanted to customize the TabView's TabBar in SwiftUI? Don't reinvent the component from scratch. The method I show below simply uses the native TabView in SwiftUI and overlays your custom TabBar component on top. Your component will handle the user taps and selections by passing them on to the native TabView via its selection parameter. See below for how it's done!

If you found this tutorial helpful, please consider subscribing using this link, and if you aren’t reading this on TrailingClosure.com, please come check us out sometime!

struct RootView: View {

// Hold the state for which tab is active/selected
@State var selection: Int = 0

var body: some View {

// Your native TabView here
TabView(selection: $selection) {
HomeTab()
.tag(0)

FavoritesTab()
.tag(1)

SettingsTab()
.tag(2)
}
.overlay( // Overlay the custom TabView component here
Color.white // Base color for Tab Bar
.edgesIgnoringSafeArea(.vertical)
.frame(height: 50) // Match Height of native bar
.overlay(HStack {
Spacer()

// First Tab Button
Button(action: {
self.selection = 0
}, label: {
Image(systemName: "house.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25, height: 25, alignment: .center)
.foregroundColor(Color(red: 32/255, green: 43/255, blue: 63/255))
.opacity(selection == 0 ? 1 : 0.4)
})
Spacer()

// Second Tab Button
Button(action: {
self.selection = 1
}, label: {
Image(systemName: "heart.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25, height: 25, alignment: .center)
.foregroundColor(Color(red: 32/255, green: 43/255, blue: 63/255))
.opacity(selection == 1 ? 1 : 0.4)
})

Spacer()

// Third Tab Button
Button(action: {
self.selection = 2
}, label: {
Image(systemName: "gear")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25, height: 25, alignment: .center)
.foregroundColor(Color(red: 32/255, green: 43/255, blue: 63/255))
.opacity(selection == 2 ? 1 : 0.4)
})
Spacer()

})
,alignment: .bottom) // Align the overlay to bottom to ensure tab bar stays pinned. …


Build a useful sidebar for your users

Pop-out sidebar menu in SwiftUI
Pop-out sidebar menu in SwiftUI
Pop-out sidebar menu in SwiftUI. Photo by the author.

Today, I decided to mess around with a SwiftUI view that allows me to easily create a sidebar menu for content inside my apps. After designing a few prototypes, I generalized it into a single custom view class called SideBarStack, as shown below:

As you can see, the custom view utilizes the @ViewBuilder property wrapper twice to allow you to pass in custom content for both the sidebar and actual view content.

The custom SideBarStack also takes in two parameters: sidebarWidth and showSidebar. The first allows the view to translate both the sidebar and main content correctly when the sidebar opens and closes. …


A SwiftUI tutorial on creating asynchronous button micro-interaction for your app

Image for post
Image for post
Photo by the author.

Welcome back! This week’s articles cover an assortment of SwiftUI micro-interactions that I’ve made for my apps. The benefits these interactions bring can really help make your app feel polished and simple to use.

Today’s micro-interaction tutorial is about creating a custom button for asynchronous tasks such as downloading, sending, or loading data.

Creating AsyncButton

Before we get programming, let’s explain the different states of AsyncButton:

  • inactive — User has not started asynchronous task.
  • inProgress — Actively processing asynchronous task.
  • isComplete — Asynchronous task complete.

During each of these states, the AsyncButton will show a different View describing the asynchronous task:


Jean-Marc Boullianne

Image for post
Image for post
Wave in Background of ScrollView

Welcome back! This week’s posts cover an assortment of SwiftUI micro-interactions that I’ve made for my apps. The benefits these interactions bring can really help make your app feel polished and simple to use. Today’s micro-interactions are all based on my custom Wave shape.

If you found this tip helpful, please consider subscribing using this link, and if you aren’t reading this on TrailingClosure.com, please come check us out sometime!

These animations all start with one thing in common and that’s my custom SwiftUI Shape struct, Wave. The way this shape works is by drawing a continuous wave from the leading to the trailing side of the frame.


Create simple micro-interactions in SwiftUI to add polish and ease of use to your next app.

Image for post
Image for post
AlignmentControl with animations

For the next few posts, I’m going to cover a few micro-interactions that I’ve made for my apps. The benefits these interactions bring can really help make your app feel polished and simple to use. As we get started these examples should help get you thinking about what’s possible in your app. As we dive in, I encourage you to mess around with the code to really get a view for what’s possible and looks good.

If you found this tip helpful, please consider subscribing using this link, and if you aren’t reading this on TrailingClosure.com, …


Lose the default Toggle style in iOS. Create your own fresh toggles to suit your app’s UI

Image for post
Image for post
Image credit: Author

My SwiftUI quick tip for this week covers custom Toggle views! You can easily apply your own style to SwiftUI toggles by using the ToggleStyle protocol. The best part is you don't need to worry about implementing any of the backing properties of the Toggle. Simply toggle the isOn property inside the Configuration instance that's passed from the makeBody(configuration:) function.

Create a Custom ToggleStyle

Start off by creating a new struct, and make sure to inherit from the ToggleStyle Protocol. Then, implement the makeBody(configuration:) function. This is where you'll construct your custom View to be shown in place of the default switch.

import SwiftUI 

struct MyToggleStyle: ToggleStyle {

func makeBody(configuration: Configuration) -> some View {
// Insert custom View code here. …


SwiftUI tutorial on converting 2D Views and Images into isometric views using custom View Modifiers.

Image for post
Image for post
Isometric SwiftUI View

The other day I started messing around with converting 2D designs into isometric views in Figma. I thought it might be neat to create a ViewModifier in SwiftUI that does the same thing. After posting a screenshot of my work on twitter, I decided to write this tutorial.

I’ve broken the tutorial into two parts (both below):

  1. Applying an Isometric Transformation to a View
  2. Extruding an Isometric View

Before getting started, please consider subscribing using this link, and if you aren’t reading this on TrailingClosure.com, please come check us out sometime!

Throughout this tutorial, you’re going to see my best 2D square drawing of a watermelon slice. Yes…I apologize in advance, but there’s one sitting in front of me as I write this tutorial, so that’s the image you’re going to get……


Implement Snap to Item scrolling in SwiftUI using this custom ViewModifier technique. This quick tip shows how it can be implemented for horizontal stacks.

Image for post
Image for post

One of the biggest things I’m missing with SwiftUI is the ability to snap to views as I scroll in a ScrollView. Below is a technique I use to implement snapping on my HStack with a custom ViewModifier.

Before getting started, please consider subscribing using this link, and if you aren’t reading this on TrailingClosure.com, please come check us out sometime!

Instead of using a ScrollView, this technique uses an HStack with a dynamically changing x-offset to simulate scrolling. To change the offset I use a DragGesture to calculate the scroll distance. You'll see below inside the DragGesture onChanged() closure that I update the dragOffset . This makes the HStack appear to scroll as the user drags across the screen. Finally, when the DragGesture function onEnded() is called, I calculate which view item is closest and then apply the final offset. …


Quick SwiftUI Tutorial on how to implement a sticky header at the top of your ScrollView. Use it with images and other custom views.

Image for post
Image for post
Sticky Header Image in SwiftUI

Quick SwiftUI tip for today. If you don’t like that annoying white space a user sees when they scroll past the top of your ScrollView, a Sticky Header works perfectly!

Before getting started, please consider subscribing using this link, and if you aren’t reading this on TrailingClosure.com, please come check us out sometime!

Place this StickyHeader component at the top of a ScrollView and any content you put inside will stretch in size to fill that gap when a user scrolls to the top. See the video below for an example!

struct StickyHeader<Content: View>: View {

var minHeight: CGFloat
var content: Content

init(minHeight: CGFloat = 200, @ViewBuilder content: () -> Content) {
self.minHeight = minHeight
self.content = content()
}

var body: some View {
GeometryReader { geo in
if(geo.frame(in: .global).minY <= 0) {
content
.frame(width: geo.size.width, height: geo.size.height, alignment: .center)
} else {
content
.offset(y: -geo.frame(in: .global).minY)
.frame(width: geo.size.width, height: geo.size.height + geo.frame(in: .global).minY)
}
}.frame(minHeight: …

About

Jean-Marc Boullianne

Creator of TrailingClosure.com, and an avid learner!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store