Earlier this week, Apple kicked off WWDC 22. The SwiftUI framework continues to be one of many major focuses of the convention. As anticipated, Apple introduced a brand new model of SwiftUI that comes together with iOS 16 and Xcode 14.
This replace comes with tons of options to assist builders construct higher apps and write much less code. On this tutorial, let me provide you with an outline of what’s new in SwiftUI 4.0.
SwiftUI Charts
You not must construct your personal chart library or depend on third-party libraries to create charts. The SwiftUI framework now comes with the Charts APIs. With this declarative framework, you possibly can current animated charts with only a few traces of code.
Briefly, you construct SwiftUI Charts by defining what it calls Mark. Here’s a fast instance:
struct ContentView: View {
var physique: some View {
Chart {
BarMark(
x: .worth(“Day”, “Monday”),
y: .worth(“Steps”, 6019)
)
BarMark(
x: .worth(“Day”, “Tuesday”),
y: .worth(“Steps”, 7200)
)
}
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import SwiftUI import Charts
struct ContentView: View { var physique: some View { Chart { BarMark( x: .worth(“Day”, “Monday”), y: .worth(“Steps”, 6019) )
BarMark( x: .worth(“Day”, “Tuesday”), y: .worth(“Steps”, 7200) ) } } } |
Whether or not you wish to create a bar chart or line chart, we begin with the Chart
view. Contained in the chart, we outline the bar marks to offer the chart knowledge. The BarMark
view is used for making a bar chart. Every BarMark
view accepts the x
and y
worth. The x
worth is used for outlining the chart knowledge for x-axis. Within the code above, the label of the x-axis is ready to Day. The y
axis exhibits the entire variety of steps.
When you enter the code in Xcode 14, the preview routinely shows the bar chart with two vertical bars.

The code above exhibits you the best method to create a bar chart. Nonetheless, as an alternative of hardcoding the chart knowledge, you normally use the Charts API with a group of knowledge. Right here is an instance:

By default, the Charts API renders the bars in the identical colour. To show a distinct colour for every of the bars, you possibly can connect the foregroundStyle
modifier to the BarMark
view:
.foregroundStyle(by: .worth(“Day”, weekdays[index])) |
So as to add an annotation to every bar, you employ the annotation
modifier like this:
.annotation { Textual content(“(steps[index])“) } |
By making these adjustments, the bar chart turns into extra visually interesting.

To create a horizontal bar chart, you possibly can merely swap the values of x
and y
parameter of the BarMark
view.

By altering the BarMark
view to LineMark
, you possibly can flip a bar chart right into a line chart.
Chart { ForEach(weekdays.indices, id: .self) { index in LineMark( x: .worth(“Day”, weekdays[index]), y: .worth(“Steps”, steps[index]) ) .foregroundStyle(.purple) .lineStyle(StrokeStyle(lineWidth: 4.0)) } } |
Optionally, you employ foregroundStyle
to vary the colour of the road chart. To vary the road width, you may as well connect the lineStyle
modifier.
The Charts API is so versatile which you could overlay a number of charts in the identical view. Right here is an instance.

Apart from BarMark
and LineMark
, the SwiftUI Charts framework additionally gives PointMark
, AreaMark
, RectangularMark
, and RuleMark
for creating several types of charts.
Resizable Backside Sheet
Apple launched UISheetPresentationController
in iOS 15 for presenting an expandable backside sheet. Sadly, this class is simply out there in UIKit. If we wish to use it in SwiftUI, we have now to write down extra code to combine the part into SwiftUI initiatives. This yr, Swift comes with a brand new modifier referred to as presentationDetents
for presenting a resizable backside sheet .
To make use of this modifier, you simply want to position this modifier inside a sheet
view. Right here is an instance:
var physique: some View {
VStack {
Button(“Present Backside Sheet”) {
showSheet.toggle()
}
.buttonStyle(.borderedProminent)
.sheet(isPresented: $showSheet) {
Textual content(“That is the resizable backside sheet.”)
.presentationDetents([.medium])
}
Spacer()
}
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | struct BottomSheetDemo: View { @State non-public var showSheet = false
var physique: some View { VStack { Button(“Present Backside Sheet”) { showSheet.toggle() } .buttonStyle(.borderedProminent) .sheet(isPresented: $showSheet) { Textual content(“That is the resizable backside sheet.”) .presentationDetents([.medium]) }
Spacer() } } } |
The presentationDetents
modifier accepts a set of detents for the sheet. Within the code above, we set the detent to .medium
. This exhibits a backside sheet that takes up about half of the display.

To make it resizable, it’s important to present multiple detent for the presentationDetents
modifier:
.presentationDetents([.medium, .large]) |
You must now see a drag bar indicating that the sheet is resizable. If you wish to disguise the drag indicator, connect the presentationDragIndicator
modifier and set it to .hidden
:
.presentationDragIndicator(.hidden) |
Apart from the preset detents similar to .medium
, you possibly can create a customized detent utilizing .peak
and .fraction
. Right here is one other instance:
.presentationDetents([.fraction(0.1), .medium, .large]) |
When the underside sheet first seems, it solely takes up round 10% of the display.
MultiDatePicker

The newest model of SwiftUI comes with a brand new date picker for customers to decide on a number of dates. Beneath is the pattern code:
@State non-public var selectedDates: Set<DateComponents> = []
var physique: some View {
MultiDatePicker(“Select your most popular dates”, choice: $selectedDates)
.body(peak: 300)
}
}
struct MultiDatePickerDemo: View {
@State non-public var selectedDates: Set<DateComponents> = []
var physique: some View { MultiDatePicker(“Select your most popular dates”, choice: $selectedDates) .body(peak: 300) } } |
NavigationStack and NavigationSplitView
NavigationView
is deprecated in iOS 16. As a substitute, it’s changed by the brand new NavigationStack
and NavigationSplitView
. Previous to iOS 16, you employ NavigationView
to create a navigation-based interface:
.navigationTitle(“Navigation Demo”)
}
NavigationView { Record { ForEach(1...10, id: .self) { index in NavigationLink(vacation spot: Textual content(“Merchandise #(index) element”)) { Textual content(“Merchandise #(index)“) } } } .listStyle(.plain)
.navigationTitle(“Navigation Demo”) } |
By pair it with NavigationLink
, you possibly can create a push and pop navigation.

Since NavigationView
is deprecated, iOS 16 gives a brand new view referred to as NavigationStack
. This new view permits builders to create the identical kind of navigation-based UIs. Right here is an instance:
.navigationTitle(“Navigation Demo”)
}
NavigationStack { Record { ForEach(1...10, id: .self) { index in NavigationLink { Textual content(“Merchandise #(index) Element”) } label: { Textual content(“Merchandise #(index)“) } } } .listStyle(.plain)
.navigationTitle(“Navigation Demo”) } |
The code is similar to the previous strategy, besides that you just use NavigationStack
as an alternative of NavigationView
. So, what’s the enhancement of NavigationStack
?
Let’s try one other instance:
NavigationLink(worth: Shade.purple) {
Textual content(“Purple colour”)
}
}
.listStyle(.plain)
.navigationTitle(“Navigation Demo”)
.navigationDestination(for: Shade.self) { merchandise in
merchandise.clipShape(Circle())
}
.navigationDestination(for: String.self) { merchandise in
Textual content(“That is the element view for (merchandise)”)
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | NavigationStack { Record { NavigationLink(worth: “Textual content Merchandise”) { Textual content(“Textual content Merchandise”) }
NavigationLink(worth: Shade.purple) { Textual content(“Purple colour”) } } .listStyle(.plain)
.navigationTitle(“Navigation Demo”) .navigationDestination(for: Shade.self) { merchandise in merchandise.clipShape(Circle()) } .navigationDestination(for: String.self) { merchandise in Textual content(“That is the element view for (merchandise)“) } } |
The record above is simplified with solely two rows: Textual content merchandise and Purple colour. Nonetheless, the underlying kind of those two rows are usually not the identical. One is a textual content merchandise and the opposite is definitely a Shade
object.
In iOS 16, the NavigationLink
view is additional improved. As a substitute of specifying the vacation spot view, it might take a price that represents the vacation spot. When this pairs with the brand new navigationDestination
modifier, you possibly can simply management the vacation spot view. Within the code above, we have now two navigationDestination
modifiers: one for the textual content merchandise and the opposite is for the colour object.
When a consumer selects a specific merchandise within the navigation stack, SwiftUI checks the merchandise kind of the worth
of the navigation hyperlink. It then calls up the vacation spot view which associates with that particular merchandise kind.

That is how the brand new NavigationStack
works. That stated, it’s only a fast overview of the brand new NavigationStack
. With the brand new navigationDestination
modifier, you possibly can programmatically management the navigation. Say, you possibly can create a button so that you can soar on to the primary view from any element views of the navigation stack. We could have one other tutorial for that.
ShareLink for Knowledge Sharing
iOS 16 introduces the ShareLink
management for SwiftUI, permitting builders to current a share sheet. It’s very straightforward to make use of ShareLink
. Right here is an instance:
var physique: some View {
ShareLink(merchandise: url)
}
}
struct ShareLinkDemo: View { non-public let url = URL(string: “https://www.appcoda.com”)!
var physique: some View { ShareLink(merchandise: url) } } |
Mainly, you present the ShareLink
management with the merchandise to share. This presents a default share button. When tapped, the app exhibits a share sheet.

You’ll be able to customise the share button by offering your personal textual content and picture like this:
ShareLink(merchandise: url) { Label(“Share”, systemImage: “hyperlink.icloud”) } |
To regulate the dimensions of the share sheet, you possibly can connect the presentationDetents
modifier:
ShareLink(merchandise: url) { Label(“Share”, systemImage: “hyperlink.icloud”) } .presentationDetents([.medium, .large]) |
Desk for iPadOS
A brand new Desk
container is launched for iPadOS that makes it simpler to current knowledge in tabular kind. Here’s a pattern code which exhibits a desk with 3 columns:
non-public let members: [Staff] = [
.init(name: “Vanessa Ramos”, position: “Software Engineer”, phone: “2349-233-323”),
.init(name: “Margarita Vicente”, position: “Senior Software Engineer”, phone: “2332-333-423”),
.init(name: “Yara Hale”, position: “Development Manager”, phone: “2532-293-623”),
.init(name: “Carlo Tyson”, position: “Business Analyst”, phone: “2399-633-899”),
.init(name: “Ashwin Denton”, position: “Software Engineer”, phone: “2741-333-623”)
]
var physique: some View {
Desk(members) {
TableColumn(“Title”, worth: .title)
TableColumn(“Place”, worth: .place)
TableColumn(“Cellphone”, worth: .telephone)
}
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | struct TableViewDemo: View {
non-public let members: [Staff] = [ .init(name: “Vanessa Ramos”, position: “Software Engineer”, phone: “2349-233-323”), .init(name: “Margarita Vicente”, position: “Senior Software Engineer”, phone: “2332-333-423”), .init(name: “Yara Hale”, position: “Development Manager”, phone: “2532-293-623”), .init(name: “Carlo Tyson”, position: “Business Analyst”, phone: “2399-633-899”), .init(name: “Ashwin Denton”, position: “Software Engineer”, phone: “2741-333-623”) ]
var physique: some View { Desk(members) { TableColumn(“Title”, worth: .title) TableColumn(“Place”, worth: .place) TableColumn(“Cellphone”, worth: .telephone) } } } |
You’ll be able to create a Desk
from a group of knowledge (e.g. an array of Employees
). For every column, you employ TableColumn
to specify the column title and values.

Desk
works nice on iPadOS and macOS. The identical desk may be routinely rendered on iOS however solely the primary column is displayed.
Expandable Textual content Area
TextField
on iOS 16 is enormously improved. Now you can use the axis
parameter to inform iOS whether or not the textual content discipline must be expanded. Right here is an instance:
Kind { Part(“Remark”) { TextField(“Please kind your suggestions right here”, textual content: $inputText, axis: .vertical) .lineLimit(5) } } |
The lineLimit
modifier specifies the utmost variety of traces allowed. The code above initially renders a single-line textual content discipline. As you kind, it routinely expands however limits its dimension to five traces.

You’ll be able to change the preliminary dimension of the textual content discipline by specifying a variety within the lineLimit
modifier like this:
Kind { Part(“Remark”) { TextField(“Please kind your suggestions right here”, textual content: $inputText, axis: .vertical) .lineLimit(3...5) } } |
On this case, iOS shows a textual content discipline shows a three-line textual content discipline by default.

Gauge
SwiftUI introduces a brand new view referred to as Gauge
for displaying progress. The only method to make use of Gauge
is like this:
var physique: some View {
Gauge(worth: progress) {
Textual content(“Add Standing”)
}
}
}
struct GaugeViewDemo: View { @State non-public var progress = 0.5
var physique: some View { Gauge(worth: progress) { Textual content(“Add Standing”) } } } |
In probably the most primary kind, a gauge has a default vary from 0 to 1. If we set the worth
parameter to 0.5
, SwiftUI renders a progress bar indicating the duty is 50% full.

Optionally, you possibly can present labels for the present, minimal, and most values:
Gauge(worth: progress) { Textual content(“Add Standing”) } currentValueLabel: { Textual content(progress.formatted(.p.c)) } minimumValueLabel: { Textual content(0.formatted(.p.c)) } maximumValueLabel: { Textual content(100.formatted(.p.c)) } |
As a substitute of utilizing the default vary, you may as well specify a customized vary like this:
Gauge(worth: progress, in: 0...100) { . . . } |
The Gauge
view gives quite a lot of kinds so that you can customise. Apart from the linear model as proven within the determine above, you possibly can connect the gaugeStyle
modifier to customise the model:

ViewThatFits
ViewThatFits
is a good addition to SwiftUI permitting builders to create extra versatile UI layouts. It is a particular kind of view that evaluates the out there area and presents probably the most appropriate view on display.
Right here is an instance. We use ViewThatFits
to outline two potential layouts of the button group:
Button(motion: {}) {
Textual content(“Cancel”)
.body(maxWidth: .infinity)
.padding()
}
.tint(.grey)
.buttonStyle(.borderedProminent)
.padding(.horizontal)
}
.body(maxHeight: 200)
HStack {
Button(motion: {}) {
Textual content(“Purchase”)
.body(maxWidth: .infinity)
.padding()
}
.buttonStyle(.borderedProminent)
.padding(.main)
Button(motion: {}) {
Textual content(“Cancel”)
.body(maxWidth: .infinity)
.padding()
}
.tint(.grey)
.buttonStyle(.borderedProminent)
.padding(.trailing)
}
.body(maxHeight: 100)
}
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | struct ButtonGroupView: View { var physique: some View { ViewThatFits { VStack { Button(motion: {}) { Textual content(“Purchase”) .body(maxWidth: .infinity) .padding() } .buttonStyle(.borderedProminent) .padding(.horizontal)
Button(motion: {}) { Textual content(“Cancel”) .body(maxWidth: .infinity) .padding() } .tint(.grey) .buttonStyle(.borderedProminent) .padding(.horizontal) } .body(maxHeight: 200)
HStack { Button(motion: {}) { Textual content(“Purchase”) .body(maxWidth: .infinity) .padding() } .buttonStyle(.borderedProminent) .padding(.main)
Button(motion: {}) { Textual content(“Cancel”) .body(maxWidth: .infinity) .padding() } .tint(.grey) .buttonStyle(.borderedProminent) .padding(.trailing) } .body(maxHeight: 100)
} } } |
One group of the buttons is aligned vertically utilizing the VStack
view. The opposite group of buttons is aligned horizontally. The maxHeight
of the vertical group is ready to 200
, whereas that of the horizontal group is ready to 100
.
What ViewThatFits
does is that it evaluates the peak of the given area and presents one of the best match view on display. Say, you set the body peak to 100
like this:
ButtonGroupView() .body(peak: 100) |
ViewThatFits
determines that it’s finest to current the horizontally-aligned button group. Let’s say, you modify the body’s peak to 150
. The ViewThatFits
view presents the vertical button group.

Gradient and Shadow

The brand new model of SwiftUI enables you to simply add linear gradient. Merely add the gradient
modifier to Shade
and SwiftUI routinely generates the gradients. Right here is an instance:
Picture(systemName: “trash”) .body(width: 100, peak: 100) .background(in: Rectangle()) .backgroundStyle(.purple.gradient) .foregroundStyle(.white.shadow(.drop(radius: 1, y: 3.0))) .font(.system(dimension: 50)) |
You may also use the shadow
modifier to use shadow impact. Right here is the road of code for including a drop shadow model:
.foregroundStyle(.white.shadow(.drop(radius: 1, y: 3.0))) |
Grid API
SwiftUI 4.0 introduces a brand new Grid
API for composing grid-based structure. You’ll be able to organize the identical structure through the use of VStack
and HStact
. The Grid
view, nevertheless, makes it rather a lot simpler.

To create a 2×2 grid, you possibly can write the code like this:
GridRow {
IconView(systemName: “trash”)
IconView(systemName: “trash”)
}
}
Grid { GridRow { IconView(systemName: “trash”) IconView(systemName: “trash”) }
GridRow { IconView(systemName: “trash”) IconView(systemName: “trash”) } } |
Contained in the Grid
view, it’s a group of GridRow
that embeds the grid cells.

Let’s say, the second row presents a single icon view and also you need it to span throughout two columns. You’ll be able to connect the gridCellColumns
modifier and set the worth to 2
:
GridRow {
IconView(systemName: “trash”)
.gridCellColumns(2)
}
}
Grid { GridRow { IconView(systemName: “trash”) IconView(systemName: “trash”) }
GridRow { IconView(systemName: “trash”) .gridCellColumns(2) } } |
The Grid
view may be nested to compose extra advanced layouts just like the one displayed under:

AnyLayout and Structure Protocol
The brand new model of SwiftUI gives AnyLayout
and the Structure
protocol for builders to create custom-made and complicated layouts. AnyLayout
is a type-erased occasion of the structure protocol. You should utilize AnyLayout
to create a dynamic structure that responds to customers’ interactions or surroundings adjustments.
As an example, your app initially arranges two pictures vertically utilizing VStack
. When a consumer faucets the stack view, it adjustments to a horizontal stack. With AnyLayout
, you possibly can implement the structure like this:
@State non-public var changeLayout = false
var physique: some View {
let structure = changeLayout ? AnyLayout(HStack(spacing: 0)) : AnyLayout(VStack(spacing: 0))
structure {
Picture(“macbook-1”)
.resizable()
.scaledToFill()
.body(maxWidth: 300, maxHeight: 200)
.clipped()
Picture(“macbook-2”)
.resizable()
.scaledToFill()
.body(maxWidth: 300, maxHeight: 200)
.clipped()
}
.animation(.default, worth: changeLayout)
.onTapGesture {
changeLayout.toggle()
}
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | struct AnyLayoutDemo: View {
@State non-public var changeLayout = false
var physique: some View { let structure = changeLayout ? AnyLayout(HStack(spacing: 0)) : AnyLayout(VStack(spacing: 0))
structure { Picture(“macbook-1”) .resizable() .scaledToFill() .body(maxWidth: 300, maxHeight: 200) .clipped()
Picture(“macbook-2”) .resizable() .scaledToFill() .body(maxWidth: 300, maxHeight: 200) .clipped()
} .animation(.default, worth: changeLayout) .onTapGesture { changeLayout.toggle() }
} } |
We will outline a structure
variable to carry an occasion of AnyLayout
. Relying on the worth of changeLayout
, this structure
adjustments between horizontal and vertical layouts.
By attaching the animation
to the structure
, the structure change will probably be animated.

The demo lets customers change the structure by tapping the stack view. In some functions, you could wish to change the structure based mostly on the system’s orientation and display dimension. On this case, you possibly can seize the orientation change through the use of the .horizontalSizeClass
variable:
@Surroundings(.horizontalSizeClass) var horizontalSizeClass |
And then you definately replace the structure
variable like this:
let structure = horizontalSizeClass == .common ? AnyLayout(HStack(spacing: 0)) : AnyLayout(VStack(spacing: 0)) |
Say, for instance, you rotate an iPhone 13 Professional Max to panorama, the structure adjustments to horizontally stack view.

Generally, we use SwiftUI’s built-in structure containers like HStack
and VStack
to compose layouts. What if these structure containers are usually not ok for arranging the kind of layouts you want? The Structure
protocol launched in iOS 16 permits you to outline your personal customized structure. It is a extra advanced subject, so we are going to focus on this new protocol in one other tutorial.
Abstract
This yr, Apple as soon as once more delivered tons of nice options for the SwiftUI framework. The Charts API, the revamp of navigation view, and the introduction of AnyLayout
will certainly provide help to construct extra elegant and interesting UIs. I’m nonetheless exploring the brand new APIs of SwiftUI. If I miss any nice updates, please do go away a remark under and let me know.
Notice: We’re updating our Mastering SwiftUI guide for iOS 16. If you wish to begin studying SwiftUI, try the guide right here. You’ll obtain a free replace later this yr.