What’s New in SwiftUI 2.0?
iOS 14 brings Lazy Stacks, ProgressView, ColorPickers, labels, grids, and more SwiftUI controls
WWDC 2020 commences this week, and the developer community was eagerly looking forward to SwiftUI 2.0. Unsurprisingly, Apple dropped some brand new API updates for SwiftUI at the end of Keynote.
The new improvements in SwiftUI are additive in nature. This means that there are no deprecations or changes that’ll break your old 13 SwiftUI codebases.
In the next few sections, we’ll take a sneak peek at the new SwiftUI controls released with iOS 14. You’ll need an Xcode 12 beta (which requires a minimum macOS version of 10.15.4) to run them. Let’s get started.
A New SwiftUI App Starting Point
Up until now, we had to use AppDelegates
and SceneDelegates
to set our first SwiftUI view. Swift 5.3 introduced a type-based program entry point that can be set using the @main
attribute and the latest SwiftUI iteration smartly leverages that.
SwiftUI now provides the following struct that’s called upon app launch:
@main | |
struct WhatsNewiOS14SwiftUIApp: App { | |
var body: some Scene { | |
WindowGroup { | |
ContentView() | |
} | |
} | |
} |
WindowGroup
is a scene property inside so that we can define the starting view hierarchy. We can set out TabView, NavigationViews, or App Clips inside the WindowGroup
computed property.
SwiftUI LazyVStack and LazyHStack
Previously, SwiftUI views used to load immediately, which led to performance and memory issues when populating huge amounts of data.
Funnily, the NavigationLink
destination view used to load contents upfront too in SwiftUI’s first iteration. This time, Apple has introduced new lazy horizontal and vertical stacks that load content as and when it’s needed, thereby aiding in the performance optimization of SwiftUI. Lazy loading is introduced in SwiftUI Lists as well now.
Here’s an example of a SwiftUI LazyHStack
in action:
struct ContentView: View { | |
var body: some View { | |
ScrollView(.horizontal) { | |
LazyHStack(spacing: 10) { | |
ForEach(0..<1000) { index in | |
Text("\(index)") | |
.frame(width: 100, height: 200) | |
.border(Color.gray.opacity(0.5), width: 0.5) | |
.background(Color.blue) | |
.cornerRadius(6) | |
} | |
} | |
.padding(.leading, 10) | |
} | |
} | |
} |
SwiftUI Scroll View Position
The first version of SwiftUI suffered a lot at the ScrollView
front due to its limited capabilities. iOS 14’s SwiftUI brings the much-needed ScrollViewReader
and ScrollViewProxy
to capture scroll view offset positions and move onto them programmatically.
In order to do so, we embed our views inside a ScrollViewReader
and use the scrollTo
method in either of the following ways:
scrollView.scrollTo(viewId)
//or
scrollView.scrollTo(viewId, anchor: .center)
By default, the scroll view position gets set to the leading or top of the view. We can refine that by using the anchor
property. For example, in the following piece of code, when setting the anchor property to the center
, the scroll position looks much better than when it was set to leading
.
SwiftUI ProgressView
Previously, we had to leverage SwiftUI Shapes to replicate linear ProgressView
and UIViewRepresentable
for creating ActivityIndicator
in SwiftUI.
Now, in iOS 14’s SwiftUI, ProgressView
has native support.
A default ProgressView()
creates an indeterminate UIActivityIndicator
like progress loader whereas the following creates a linear ProgressView
in SwiftUI:
ProgressView("Text", value: 10, total: 100)
We can further customize the ProgressView
by using progressViewStyle
, which accepts built-in CircularProgressViewStyle
, DefaultProgressViewStyle
, and lets you create custom modifiers as well.
accentColor
is used to set the text color in the ProgressView
, while foregroundColor
acts as the tint
.
SwiftUI Labels, Links, and ColorPickers
Labels are a much-needed addition in the latest SwiftUI iteration. They let you set icons alongside text with the following line of code:
Label("SwiftUI 2.0", systemImage: "checkmark.icloud")
Inside the icons
property, you can set SF Symbols, image assets, or custom SwiftUI Shapes.
Note: At the time of writing, the icon is aligned with the top of the text. Hopefully, this will get fixed soon.
SwiftUI Link is another cool UI control that provides built-in support for navigating to a URL:
Link("Click me",destination: URL(string: "your_url")!)
The link is redirected to either the web browser or the associated application in case it’s a universal link.
Another big addition to the SwiftUI arsenal is the inclusion of a native ColorPicker
UI control. You can use a State
property wrapper to update the color picked by the user.
ColorPicker("Sample Picker", selection: $myColor)
SwiftUI TextEditor, MapKit, Sign In With Apple
Multi-line scrollable UITextViews
that were omitted last time are now included natively in SwiftUI and known as TextEditor
.
TextEditor(text: $stateProperty)
MapKit, which had to be embedded in SwiftUI by wrapping in UIViewRepresentable
, now gets added natively. We can pass a MKCoordinateRegion
, show user location, and do a lot of other MapKit
stuff directly from SwiftUI’s view interface itself.
Map(mapRect:interactionModes:showsUserLocation: userTrackingMode:
SignInWithAppleButton
now makes its way into SwiftUI’s built-in control. To set up the button, we simply instantiate the struct
and set the label
argument as either .signUp
or .signIn
to indicate the type of authorization. For more details on its syntax, refer to the official documentation.
A New onChange Modifier to Listen for State Changes
onChange
is a new view modifier that’s available across all SwiftUI views. It lets you listen to state changes and perform actions on a view accordingly.
For instance, we can toggle a Button state change and trigger the TextEditor
to clear since the onChanged
modifier is attached, as shown below:
import SwiftUI | |
struct ContentView: View { | |
@State var currentText: String = "Hi How are you?" | |
@State var clearText: Bool = false | |
var body: some View { | |
VStack{ | |
TextEditor(text: $currentText) | |
.onChange(of: clearText) { value in | |
if clearText{ | |
currentText = "" | |
} | |
} | |
Button(action: {clearText = true}, label: { | |
Text("Clear Text Editor") | |
}) | |
} | |
} | |
} |
Note: The clearText
state property triggers the onChange
modifier automatically for the first time when SwiftUI’s body is instantiated.
SwiftUI TabView Brings New Style for Page Control
UIPageViewController
did make its way into SwiftUI the last time. In the iOS 14 iteration, TabView
introduces a new style to let you embed swipeable page control in your SwiftUI views. Simply set the PageTabViewStyle()
in your .tabViewStyle()
modifier, as shown below:
import SwiftUI | |
struct ContentView: View { | |
let colors: [Color] = [.red, .green, .yellow, .blue] | |
var body: some View { | |
TabView { | |
ForEach(0..<6) { index in | |
Text("Tab \(index)") | |
.font(.title) | |
.frame(maxWidth: .infinity, maxHeight: .infinity) | |
.background(colors[index % colors.count]) | |
.cornerRadius(8) | |
} | |
}.tabViewStyle(PageTabViewStyle()) | |
} | |
} |
Here’s a screengrab of the code above in action on Xcode 12:
SwiftUI Grids
CollectionView and Compositional Layouts were once again missed in iOS 14 SwiftUI. But that didn’t stop Apple from introducing new containers for grid-based layouts that let you set child views in LazyHGrid
or LazyVGrid
.
Each element of a SwiftUI grid is a GridItem
. We can set the alignments, spacing, and size of the GridItem
. In the following code, we’ve created a vertical grid layout in SwiftUI that consists of three columns:
struct ContentView: View { | |
let colors: [Color] = [.red, .green, .yellow, .blue] | |
var columns: [GridItem] = | |
Array(repeating: .init(.flexible(), alignment: .center), count: 3) | |
var body: some View { | |
ScrollView { | |
LazyVGrid(columns: columns, spacing: 10) { | |
ForEach(0...100, id: \.self) { index in | |
Text("Tab \(index)") | |
.frame(width: 110, height: 200) | |
.background(colors[index % colors.count]) | |
.cornerRadius(8) | |
} | |
} | |
} | |
} | |
} |
With just a few lines of code, we’ve built a customizable grid layout in SwiftUI for iOS 14.
Conclusion
There’s a lot more to look forward to from SwiftUI and other iOS 14 framework updates this year. SwiftUI OutlineGroups
and VideoPlayer
support are a few promising new features. Most importantly, SwiftUI View builders now support if let
and switch
statements.
That’s it for this one. Thanks for reading.