Earlier this week, Apple kicked off WWDC 22. The SwiftUI framework continues to be one of many foremost 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 now not must construct your individual 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 just some traces of code.
In short, 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 need 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 information. 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 information for x-axis. Within the code above, the label of the x-axis is ready to Day. The y
axis exhibits the full variety of steps.
Should you enter the code in Xode 14, the preview routinely shows the bar chart with two vertical bars.
The code above exhibits you the best approach to create a bar chart. Nevertheless, as a substitute of hardcoding the chart information, you normally use the Charts API with a set of information. Right here is an instance:
By default, the Charts API renders the bars in the identical shade. To show a special shade 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 utilize the annotation
modifier like this:
.annotation { Textual content(“(steps[index])“) } |
By making these modifications, 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 utilize foregroundStyle
to vary the colour of the road chart. To vary the road width, you can even connect the lineStyle
modifier.
The Charts API is so versatile you can overlay a number of charts in the identical view. Right here is an instance.
Apart from BarMark
and LineMark
, the SwiftUI Charts framework additionally supplies 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 need to use it in SwiftUI, we’ve got to write down extra code to combine the part into SwiftUI tasks. This 12 months, Swift comes with a brand new modifier known as presentationDetents
for presenting a resizable backside sheet .
To make use of this modifier, you simply want to put 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 personal 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 a must to present a couple of detent for the presentationDetents
modifier:
.presentationDetents([.medium, .large]) |
It’s best to 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 comparable 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 personal var selectedDates: Set<DateComponents> = []
var physique: some View {
MultiDatePicker(“Select your most well-liked dates”, choice: $selectedDates)
.body(peak: 300)
}
}
struct MultiDatePickerDemo: View {
@State personal var selectedDates: Set<DateComponents> = []
var physique: some View { MultiDatePicker(“Select your most well-liked 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 utilize NavigationView
to create a navigation-based interface:
.navigationTitle(“Navigation Demo”)
}
NavigationView { Checklist { 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 supplies a brand new view known as NavigationStack
. This new view permits builders to create the identical sort of navigation-based UIs. Right here is an instance:
.navigationTitle(“Navigation Demo”)
}
NavigationStack { Checklist { 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 outdated strategy, besides that you just use NavigationStack
as a substitute of NavigationView
. So, what’s the enhancement of NavigationStack
?
Let’s try one other instance:
NavigationLink(worth: Colour.purple) {
Textual content(“Purple shade”)
}
}
.listStyle(.plain)
.navigationTitle(“Navigation Demo”)
.navigationDestination(for: Colour.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 { Checklist { NavigationLink(worth: “Textual content Merchandise”) { Textual content(“Textual content Merchandise”) }
NavigationLink(worth: Colour.purple) { Textual content(“Purple shade”) } } .listStyle(.plain)
.navigationTitle(“Navigation Demo”) .navigationDestination(for: Colour.self) { merchandise in merchandise.clipShape(Circle()) } .navigationDestination(for: String.self) { merchandise in Textual content(“That is the element view for (merchandise)“) } } |
The checklist above is simplified with solely two rows: Textual content merchandise and Purple shade. Nevertheless, the underlying sort of those two rows aren’t the identical. One is a textual content merchandise and the opposite is definitely a Colour
object.
In iOS 16, the NavigationLink
view is additional improved. As a substitute of specifying the vacation spot view, it may take a worth 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’ve got two navigationDestination
modifiers: one for the textual content merchandise and the opposite is for the colour object.
When a person selects a selected merchandise within the navigation stack, SwiftUI checks the merchandise sort of the worth
of the navigation hyperlink. It then calls up the vacation spot view which associates with that particular merchandise sort.
That is how the brand new NavigationStack
works. That mentioned, 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 bounce on to the principle view from any element views of the navigation stack. We can 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 simple to make use of ShareLink
. Right here is an instance:
var physique: some View {
ShareLink(merchandise: url)
}
}
struct ShareLinkDemo: View { personal let url = URL(string: “https://www.appcoda.com”)!
var physique: some View { ShareLink(merchandise: url) } } |
Principally, 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 may customise the share button by offering your individual textual content and picture like this:
ShareLink(merchandise: url) { Label(“Share”, systemImage: “hyperlink.icloud”) } |
To manage the scale 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 information in tabular type. Here’s a pattern code which exhibits a desk with 3 columns:
personal 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(“Identify”, worth: .title)
TableColumn(“Place”, worth: .place)
TableColumn(“Telephone”, worth: .telephone)
}
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
struct TableViewDemo: View {
personal 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(“Identify”, worth: .title) TableColumn(“Place”, worth: .place) TableColumn(“Telephone”, worth: .telephone) } } } |
You may create a Desk
from a set of information (e.g. an array of Workers
). For every column, you utilize TableColumn
to specify the column title and values.
Desk
works nice on iPadOS and macOS. The identical desk will 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 subject ought to be expanded. Right here is an instance:
Kind { Part(“Remark”) { TextField(“Please sort 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 subject. As you sort, it routinely expands however limits its measurement to five traces.
You may change the preliminary measurement of the textual content subject by specifying a variety within the lineLimit
modifier like this:
Kind { Part(“Remark”) { TextField(“Please sort your suggestions right here”, textual content: $inputText, axis: .vertical) .lineLimit(3...5) } } |
On this case, iOS shows a textual content subject shows a three-line textual content subject by default.
Gauge
SwiftUI introduces a brand new view known as Gauge
for displaying progress. The only means to make use of Gauge
is like this:
var physique: some View {
Gauge(worth: progress) {
Textual content(“Add Standing”)
}
}
}
struct GaugeViewDemo: View { @State personal var progress = 0.5
var physique: some View { Gauge(worth: progress) { Textual content(“Add Standing”) } } } |
In probably the most fundamental type, 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 can even specify a customized vary like this:
Gauge(worth: progress, in: 0...100) { . . . } |
The Gauge
view supplies 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 superb addition to SwiftUI permitting builders to create extra versatile UI layouts. It is a particular sort 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 doable 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 the perfect match view on display. Say, you set the body peak to 100
like this:
ButtonGroupView() .body(peak: 100) |
ViewThatFits
determines that it’s greatest 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 allows you to simply add linear gradient. Merely add the gradient
modifier to Colour
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(measurement: 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 may prepare the identical structure through the use of VStack
and HStact
. The Grid
view, nevertheless, makes it lots 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 set 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 may 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 will be nested to compose extra complicated layouts just like the one displayed under:
AnyLayout and Format Protocol
The brand new model of SwiftUI supplies AnyLayout
and the Format
protocol for builders to create personalized and complicated layouts. AnyLayout
is a type-erased occasion of the structure protocol. You should use AnyLayout
to create a dynamic structure that responds to customers’ interactions or surroundings modifications.
As an example, your app initially arranges two photos vertically utilizing VStack
. When a person faucets the stack view, it modifications to a horizontal stack. With AnyLayout
, you possibly can implement the structure like this:
@State personal 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 personal 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
modifications between horizontal and vertical layouts.
By attaching the animation
to the structure
, the structure change shall be animated.
The demo lets customers change the structure by tapping the stack view. In some purposes, it’s possible you’ll need to change the structure based mostly on the system’s orientation and display measurement. 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 modifications to horizontally stack view.
Most often, we use SwiftUI’s built-in structure containers like HStack
and VStack
to compose layouts. What if these structure containers aren’t adequate for arranging the kind of layouts you want? The Format
protocol launched in iOS 16 means that you can outline your individual customized structure. It is a extra complicated matter, so we’ll focus on this new protocol in one other tutorial.
Abstract
This 12 months, 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 show you how to construct extra elegant and fascinating 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 12 months.