Among numerous advancements in iOS development brought to us in WWDC20 are a number of updates, enhancements, and demonstrations of an overall continued effort to build out the robustness of SwiftUI, furthering its viability as the preferred method of UI development in iOS.
In order to learn as much as possible about these updates to SwiftUI, we built a sample Cities app, with the objectives being to explore the new app lifecycle, new
View types, modifiers, property wrappers, and any other general improvements we found along the way.
The SwiftUI App Lifecycle
Upon creating a new project in Xcode, with the
SwiftUIApp Life Cycle selected, you’ll notice your project is pre-packaged with a new Swift file, named to match your project. This file effectively replaces
SceneDelegate, and is all that is needed to startup and run your app. The code will look something like this:
ContentView can be any
View type you want your app to begin with. We’re going to create a
TabView to allow our user access to the three different views that comprise our Cities app:
We are going to modify each tab item using the
tabItem() modifier. In addition to the updates to SwiftUI, WWDC20 brought us some advancements to SF Symbols. SF Symbols 2 offers over 750 new Symbols, multicolor Symbols, localization enhancements, and better optical alignment control with the ability to customize leading and trailing attributes. For each tab item, we’ll provide a SF Symbol Image and associated text.
The new SwiftUI App Lifecycle has dramatically simplified the process of creating and starting up your app, allowing you, the developer, to focus more on the fun stuff, and less on the setup.
SwiftUI in iOS 14 offers new
View types that load lazily.
HStack now have lazy counterparts,
LazyHStack, to load our content lazily on demand.
HStackis created the system also creates all of their subviews. On the other hand, when a
LazyHStack is created the system creates each subview only when it is needed to render on screen. These lazy stacks match the performance benefits of
UICollectionView from UIKit.
The benefit of lazy stacks is made apparent through large data sets. In fact, we recommend not using lazy stacks unless you are dealing with a large data set. Let’s walk through some examples.
The third tab in our app,
CityListView shows a long list of city names. A button in the navigation bar toggles between displaying the list using a
LazyVStack and a regular
VStack using a binding called
isLazy. Here’s how it looks:
We started off with a list of approximately 26,500 city names from simplemaps.com. Running on an iPhone 11 Pro the lazy stack loads instantly and is able to immediately scroll smoothly through thousands of names. Unfortunately, scrolling quickly down the list eventually causes the app to run out of memory and crash!
VStack do better? Tapping the toggle button switches to the regular stack and immediately pauses the UI. After a few seconds the app crashes.
VStack attempts to render the view with all 26,500 city names up front and unfortunately it doesn’t succeed.
Whichever way we go it appears that 26,500 names pushes the system too far. After dividing the list of names in half three times down to about 3,300 we are finally able to render the regular
VStack. With about 3,300 names the
LazyVStack is fully capable of loading instantly and scrolling smoothly through the entire list. On the other hand, regular
VStack takes a couple seconds to render the view after tapping the toggle button. Once loaded the
VStack scrolls smoothly through all of the names.
We also tried scrolling through the 26,500 city names using
List which was already available before WWDC20.
List we are able to successfully scroll through all of the names without crashing. However, framerate was noticeably bad. If you need to show an enormous number of rows in a table layout then
List might be preferable to using either a
SwiftUI and iOS 14 also offer us
LazyHGrid for grid layouts. The addition of these views is an exciting enhancement, as they serve as a flexible replacement for
UICollectionView, a commonly used view type which SwiftUI was previously lacking.
LazyHGrid are container views that arrange their child views in a grid, growing vertically and horizontally, respectively, and only creating items as needed.
On initialization, you can provide an array of
GridItem's, which essentially describe how the grid should lay out. In our example, we’ll use
GridItem(.adaptive(minimum: 180)), meaning we want our grid to fit as many items as possible in each row, so long as that item has a minimum size of 180 points. We’ll call our array of
columns, since we are going to use a
We could have also used
GridItem(.flexible()) if we wanted to specify how many columns to use, or
GridItem(.fixed()) to explicitly set the size of the item.
Once we have set our columns, we can create our
LazyVGrid using those columns:
And the result:
What if we wanted a horizontal grid? It’s really quite easy. We just need to set our
GridItem array to account for rows, rather than columns. I could use the same
GridItem(.adaptive(minimum: 180)) and ensure we have exactly 180 points for each item, but for variation’s sake, let’s specify that we want exactly 4 rows, with
.flexible(), and give each one a minimum height of 200:
SwiftUI in iOS 14 now offers us the ability to easily show and display MapKit views in our SwiftUI views.
We just need a
@State variable to track an
MKCoordinateRegion that will be used for the center of our map.
So, we’ll create a
CityDetailView, showing a map of the provided city, using that city’s coordinates, MapKit, and SwiftUI’s
Once we have a City, we can set our
@State variable to track that City’s coordinates in an
And in our view, we can just display that region in a map as such:
Label is a new UI component to combine an icon with text. The simplest form of a
Label can be created using a title and the name of an image. We’ll do so, using an SF Symbol:
Label("Cities", systemImage: "airplane")
The icon will size accordingly with the font of the text in the label, and will scale for dynamic type. iOS 14 also introduces the ability to change color for SF Symbols, so we can apply view modifiers to this label and consequentially change the color of both the icon and the text.
Label("Cities", systemImage: "airplane")
SwiftUI in iOS 14 also offers a new
ColorPicker. We just need to track a
color property as a
@State variable, and we can set our
Label’s color to match that of the
fullScreenCover view modifier allows us to present another view modally, covering as much of the screen as possible. Let’s present a larger version of our image on
CityDetailView when the user taps the image.
We just need to add a
@State variable to track when the modal view has presented, and to add the
.fullScreenCover modifier to our image. Our
fullScreenCover will track the binding to our
@State variable, and we will supply it with View content.
In our newly created
CityImageModalView, we just need an environment property to allow the view to dismiss itself. In the View, we’ll add a Button to dismiss.
SwiftUI continues to develop, expand, and advance its viability as the preferred method of UI development for iOS. The addition of new view types makes it easier to replicate more and more
UIKit views, while its sleekness, flexibility, and intuitiveness, whether it be through the new SwiftUI App Lifecycle or view definition, enhance the simplicity and efficiency of development. Look for SwiftUI to continue to grow in the future, and in the meantime, check out these latest advancements for yourself!