Open library Shortcut (with Canvas)
Front
Active users
5
All-time users
6
Favorites
0
Last updated
4 years ago
Date created
Sep 25, 2020
Unsectioned
(45 cards)
Open library Shortcut (with Canvas)
⇧ + ⌘ + L
Toggle Comments
Show SwiftUI Inspector...(Code)
Group - Preview Provider
You can use a Group
to return multiple previews from a preview provider. Xcode renders the group’s child views as separate previews in the canvas.
A view’s children inherit the view’s contextual settings, such as preview configurations.
struct LandmarkRow_Previews: PreviewProvider {
static var previews: some View {
Group {
LandmarkRow(
landmark: landmarkData[0])
LandmarkRow(
landmark: landmarkData[1])
}
.previewLayout(
.fixed(width: 300, height: 70))
}
}
Spacer
()
A spacer expands to make its containing view use all of the space of its parent view, instead of having its size defined only by its contents.
Group
Group
is a container for grouping view content. Group creates several views to act as one, also to avoid Stack's 10 View maximum limit.
VStack {
Group {
Text("Hello")
Text("Hello")
Text("Hello")
}
Group {
Text("Hello")
Text("Hello")
}
}
To load icons from Apple’s SF Symbols set
SwiftUI’s Image
view lets us load any of the 2000+ icons from SF Symbols, with many of them working in multi-color too.
use the Image(systemName:)
initializer, passing in the icon string to load.
Image(systemName: "cloud.heavyrain.fill")
A preview provider returns
A preview provider returns one or more views, with options to configure the size and device.
SwiftUI Map
import MapKit
import SwiftUI
struct ContentView: View {
@State private var region =
MKCoordinateRegion(
center:
CLLocationCoordinate2D(
latitude: 51.507222,
longitude: -0.1275),
span:
MKCoordinateSpan(
latitudeDelta: 0.5,
longitudeDelta: 0.5))
var body: some View {
Map(coordinateRegion: $region)
}
}
NavigationLink
)the views it comprises need to use a stored property as the source for their data. Starting with the child views, you’ll convert all the views to display data that’s passed in. This is a common pattern when building views using SwiftUI. Your custom views will often wrap and encapsulate a series of modifiers for a particular view. Pass the required data down to your custom types. Finally, call the navigationBarTitle(_:displayMode:)
modifier to give the navigation bar a title when showing the detail view.
struct LandmarkDetail: View
. . .
var landmark: Landmark
. . .
var body: some View {
VStack {
. . .
}
.navigationBarTitle(landmark.name, displayMode: .inline)
}
struct LandmarkList: View
. . .
NavigationView{
List(landmarkData) { landmark in
NavigationLink(
destination:
LandmarkDetail(
landmark: landmark)){
LandmarkRow(landmark: landmark)
}
}
.navigationTitle("Landmarks")
}
navigationBarTitle(_:displayMode:)
The navigationBarTitle(_:displayMode:)
modifier to give the navigation bar a title when showing the detail view.
struct LandmarkDetail: View {
var landmark: Landmark
var body: some View {
VStack {
. . .
}
.navigationBarTitle(
landmark.name,
displayMode: .inline)
}
}
Embed in VStack
Command-click the view’s to show the structured editing popover, and then choose Embed in VStack
binding
A binding acts as a reference to a mutable state. The control uses the binding to update the view’s state accordingly.
You use the $
prefix to access a binding to a state variable, or one of its properties.
struct LandmarkList: View {
@State var showFavoritesOnly = true
var body: some View {
NavigationView {
List {
Toggle(isOn: $showFavoritesOnly) {
Text("Favorite Only")
}
ForEach(landmarkData) { landmark in
. . .
}
}
previewLayout(_:)
Use previewLayout(_:)
to set a custom frame size.
struct LandmarkRow_Previews: PreviewProvider {
static var previews: some View {
LandmarkRow(
landmark: landmarkData[1])
.previewLayout(
.fixed(width: 300,
height: 70))
}
}
You can combine and embed multiple views in
stacks, which group views together horizontally, vertically, or back-to-front.
Identifiable
protocol
When conforming to the protocol Identifiable we have to implement a variable called id however this variable does not have to be an Int. The protocol only requires that the type of the variable id is actually Hashable.
Note: Int, Double, String and a lot more types are Hashable
struct Landmark: Hashable, Codable, Identifiable {
var id: Int
var name: String
var state: String
var park: String
}
Use an Observable Object for Storage
An observable object is a custom object for your data that can be bound to a view from storage in SwiftUI’s environment. SwiftUI watches for any changes to observable objects that could affect a view, and displays the correct version of the view after a change.
ObservableObject
protocol from the Combine framework.@Published
attribute to each property in the model.import Foundation
import Combine
final class UserData: ObservableObject {
@Published var showFavoritesOnly = false
@Published var landmarks = landmarkData
}
the body
property only returns
a single view
previewDisplayName(_:)
The previewDisplayName(_:)
modifier, lets you put a name under a device in the preview window.
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView()
.previewDevice(
PreviewDevice(rawValue: "iPhone SE"))
.previewDisplayName("iPhone SE")
ContentView()
.previewDevice(
PreviewDevice(rawValue: "iPhone XS Max"))
.previewDisplayName("MY CUSTOM NAME")
}
}
}
#endif
scale up an icons from Apple’s SF Symbols set
Image(systemName: "cloud.heavyrain.fill")
.font(.largeTitle)
PreviewProvider customization
You can customize the returned content from a preview provider to render exactly the previews that are most helpful to you.
The code you write in a preview provider only changes what Xcode displays in the canvas.
When using a List
or ForEach
on primitive types that don’t conform to the Identifiable
protocol
When using a List
or ForEach
on primitive types that don’t conform to the Identifiable
protocol, such as an array of strings or integers. You should use id: \.self
as the second parameter to your List
or ForEach
.
let data =
["Robert", "Esmery",
"Mami", "Papi"]
VStack {
ForEach(data, id: \.self){ pos in
Text("Hello \(pos)")
}
}
Color an icons from Apple’s SF Symbols set
Image(systemName: "cloud.heavyrain.fill")
.foregroundColor(.red)
Create a new button using an if
-else
conditional statement.
In the button’s action
closure, the code uses landmarkIndex
with the userData
object to update.
Button(
action:
{self.userData.landmarks[self.landmarkIndex]
.isFavorite.toggle()}) {
if self.userData.landmarks[self.landmarkIndex]
.isFavorite {
Image(systemName: "star.fill")
.foregroundColor(.yellow)
} else {
Image(systemName: "star")
.foregroundColor(.gray)
}
Format Code / Balance indentation
⌘ + A, ⌃ + I
By default, previews render at the size of the device in the active scheme. You can change the preview device by calling the previewDevice(_:)
modifier method.
ForEach
operates on collections the same way as the list, which means you can use it anywhere you can use a child view, such as in stacks, lists, groups, and more. When the elements of your data are simple value types — like the strings you’re using here — you can use \.self
as key path to the identifier.
Use the previewDisplayName(_:)
modifier to add the device names as labels for the previews.
struct LandmarkList_Previews: PreviewProvider {
static var previews: some View {
ForEach(
["iPhone 11", "iPhone 11 Pro Max"],
id: \.self) {
deviceName in
LandmarkList()
.previewDevice(
PreviewDevice(
rawValue: deviceName))
.previewDisplayName(deviceName)
}
}
}
combining static and dynamic views in a list, or to combine two or more different groups of dynamic views.
To combine static and dynamic views in a list, or to combine two or more different groups of dynamic views, use the ForEach
type instead of passing your collection of data to List
.
struct LandmarkList: View {
@State var showFavoritesOnly = true
var body: some View {
NavigationView {
List {
Toggle(isOn: $showFavoritesOnly) {
Text("Favorite Only")
}
ForEach(landmarkData) { landmark in
if !self.showFavoritesOnly ||
landmark.isFavorite {
NavigationLink(
destination:
LandmarkDetail(landmark: landmark)) {
LandmarkRow(landmark: landmark)
}
}
}
}
.navigationBarTitle("Landmarks")
Toggle Minimap
navigationBarTitle
the navigationBarTitle(_:)
modifier method to set the title of the navigation bar when displaying the list.
NavigationView{
List(landmarkData) { landmark in
LandmarkRow(landmark: landmark)
}
.navigationTitle("Landmarks")
}
State
State is a value, or a set of values, that can change over time, and that affects a view’s behavior, content, or layout. You use a property with the @State
attribute to add state to a view.
struct LandmarkList: View {
@State var showFavoritesOnly = false
Show SwiftUI Inspector...(Canvas)
⌃ + ⌥ + Click
Refresh Canvas/Resume Preview
New File
Dynamic List
Instead of specifying a list’s elements individually, you can generate rows directly from a collection.
You can create a list that displays the elements of collection by passing your collection of data and a closure that provides a view for each element in the collection. The list transforms each element in the collection into a child view by using the supplied closure. Lists work with identifiable data. You can make your data identifiable in one of two ways: by passing along with your data a key path to a property that uniquely identifies each element, or by making your data type conform to the Identifiable
protocol.
List(landmarkData, id: \.id) { landmark in
LandmarkRow(landmark: landmark)
}
List(landmarkData) { landmark in
LandmarkRow(landmark: landmark)
}
in SwiftUI view, you describe its content, layout, and behavior in
the view’s body
property;
Applying various properties to an icons from Apple’s SF Symbols set.
Image(systemName: "cloud.sun.rain.fill")
.renderingMode(.original)
.font(.largeTitle)
.padding()
.background(Color.black)
.clipShape(Circle())
List
SwiftUI’s List
type, can display a platform-specific list of views. The elements of the list can be static, or dynamically generated. You can even mix static and dynamically generated views.
List {
LandmarkRow(landmark: landmarkData[0])
LandmarkRow(landmark: landmarkData[1])
}
Tab Library
Show Code Actions
Once you have the Model object, you need to update your views to adopt it as the data store for your app.
replace the properties declaration with an @EnvironmentObject
property, and add an environmentObject(_:)
modifier to the preview.
This property gets its value automatically, as long as the environmentObject(_:)
modifier has been applied to a parent.
Make sure the code is accessing the new property.
Just like on @State
properties, you can access a binding to a member of the new property object by using the $
prefix.
You’ll use landmarkIndex
when accessing or updating the landmark’s favorite status, so that you’re always accessing the correct version of that data.
Xcode keyboard shortcuts
You add navigation capabilities to a list by embedding it in a NavigationView
, and then nesting each row in a NavigationLink
to set up a transtition to a destination view.
NavigationView{
List(landmarkData) { landmark in
NavigationLink(
destination: LandmarkDetail()){
LandmarkRow(landmark: landmark)
}
}
.navigationTitle("Landmarks")
}