Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
SwiftUI Cookbook
SwiftUI Cookbook

SwiftUI Cookbook: Discover solutions and best practices to tackle the most common problems while building SwiftUI apps

Arrow left icon
Profile Icon Giordano Scalzo Profile Icon Nzokwe
Arrow right icon
$54.99
Full star icon Full star icon Full star icon Full star icon Half star icon 4.8 (10 Ratings)
Paperback Oct 2020 614 pages 1st Edition
eBook
$29.99 $43.99
Paperback
$54.99
Subscription
Free Trial
Renews at $19.99p/m
Arrow left icon
Profile Icon Giordano Scalzo Profile Icon Nzokwe
Arrow right icon
$54.99
Full star icon Full star icon Full star icon Full star icon Half star icon 4.8 (10 Ratings)
Paperback Oct 2020 614 pages 1st Edition
eBook
$29.99 $43.99
Paperback
$54.99
Subscription
Free Trial
Renews at $19.99p/m
eBook
$29.99 $43.99
Paperback
$54.99
Subscription
Free Trial
Renews at $19.99p/m

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Product feature icon AI Assistant (beta) to help accelerate your learning
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Table of content icon View table of contents Preview book icon Preview Book

SwiftUI Cookbook

Chapter 2: Going Beyond the Single Component with Lists and Scroll Views

In this chapter, we'll learn how to display lists in SwiftUI. List views are similar to UITableViews in UIKit but are significantly simpler to use. No storyboards or prototype cells are required, and we do not need to know how many rows there are. SwiftUI's lists are designed to be modular so that you can build bigger things from smaller components.

We'll also look at lazy stacks and lazy grids, which are used to optimize the display of large amounts of data by loading only the subset of the content that is currently being displayed or is about to be displayed. Lastly, we'll take a look at how to present hierarchical data in an expanding list with sections that can be expanded or collapsed.

By the end of this chapter, you will understand how to display lists of static or dynamic items, add or remove rows from lists, edit lists, add sections to list views, and much more.

In this chapter, we will cover the following recipes:

  • Using scroll views
  • Creating a list of static items
  • Using custom rows in a list
  • Adding rows to a list
  • Deleting rows from a list
  • Editing a list
  • Moving rows in a list
  • Adding sections to a list
  • Using LazyHStack and LazyVStack (iOS 14+)
  • Using LazyHGrid and LazyVGrid (iOS 14+)
  • Using ScrollViewReader (iOS 14+)
  • Using expanding lists (iOS 14+)
  • Using DisclosureGroup to hide and show content (iOS 14+)

Technical requirements

The code in this chapter is based on Xcode 12 and iOS 13. However, some recipes are only compatible with iOS 14 and higher.

You can find the code in the book's GitHub repository at https://github.com/PacktPublishing/SwiftUI-Cookbook/tree/master/Chapter02%20-%20Lists%20and%20ScrollViews.

Using scroll views

SwiftUI scroll views are used to easily create scrolling containers. They automatically size themselves to the area where they are placed. Scroll views are vertical by default and can be made to scroll horizontally or vertically by passing in the .horizontal() or .vertical() modifiers as the first parameter to the scroll view.

Getting ready

Let's start by creating a SwiftUI project called ScrollViewApp.

Optional: Download the San Francisco (SF) Symbols app here: https://developer.apple.com/sf-symbols/.

SF Symbols is a set of over 2,400 symbols provided by Apple. They follow Apple's San Francisco system font and automatically ensure optical vertical alignment for different sizes and weights.

How to do it…

We will add two scroll views to a VStack component: one horizontal and one vertical. Each scroll view will contain SF symbols for the letters A–L:

  1. Between the ContentView struct declaration and its body, create an array of SF symbol names called imageNames. Add the strings for SF symbols A–L:
    let imageNames = [
            "a.circle.fill",
            "b.circle.fill",
            "c.circle.fill",
            "d.circle.fill",
            "e.circle.fill",
            "f.circle.fill",
            "g.circle.fill",
            "h.circle.fill",
            "i.circle.fill",
            "j.circle.fill",
            "k.circle.fill",
            "l.circle.fill",
        ]
  2. Add a VStack component and scroll views to the app:
        var body: some View {
            VStack{
                ScrollView {
                        ForEach(self.imageNames, id: \.self)                    { name in
                            Image(systemName: name)
                                .font(.largeTitle)
                                .foregroundColor(Color.                                yellow)
                                .frame(width: 50, height: 50)
                                .background(Color.blue)
                        }
                }
                .frame(width:50, height:200)
                
                ScrollView(.horizontal, showsIndicators:               false) {
                    HStack{
                        ForEach(self.imageNames, id: \.self)                    { name in
                            Image(systemName: name)
                                .font(.largeTitle)
                                .foregroundColor(Color.                               yellow)
                                .frame(width: 50, height: 50)
                                .background(Color.blue)
                        }
                    }
                }
            }
        }

    The result is a view with a vertical and horizontal scroll view:

Figure 2.1 – App with horizontal and vertical scroll views

Figure 2.1 – App with horizontal and vertical scroll views

How it works…

VStack allows us to display multiple scroll views within the ContentView struct's body.

By default, scroll views display items vertically. The first ScrollView component in VStack displays items in a vertical way, even though no axis was specified.

Within the first ScrollView component, a ForEach loop is used to iterate over a static array and display the contents. In this case, the ForEach loop takes two parameters: the array we are iterating over and an identifier, id: \.self, used to distinguish between the items being displayed. The id parameter would not be required if the collection used conformed to the Identifiable protocol.

Two parameters are passed to the second ScrollView component: the axis and showIndicators (ScrollView(.horizontal, showsIndicators: false). The .horizontal axis parameter causes content to be horizontal, and showIndictors:false prevents the scrollbar indicator from appearing in the view.

See also

Refer to the Apple ScrollView documentation at https://developer.apple.com/documentation/swiftui/scrollview.

Creating a list of static items

Lists are similar to scroll views in that they are used to view a collection of items. Lists are used for larger datasets, whereas scroll views are used for smaller datasets; the reason being that list views do not load the whole dataset in memory at once and thus are more efficient at handling large data.

Getting ready

Let's start by creating a new SwiftUI app called StaticList.

How to do it…

We'll create a struct to hold weather information and an array of weather data. The data will then be used to create a view that provides weather information from several cities. Proceed as follows:

  1. Before the ContentView struct, create a struct called WeatherInfo with four properties: id, image, temp, and city:
    	struct WeatherInfo: Identifiable {
                var id = UUID()
                var image: String
                var temp: Int
                var city: String
    }
  2. Within the ContentView struct, create a weatherData property as an array of WeatherInfo:
        let weatherData: [WeatherInfo] = [
        WeatherInfo(image: "snow", temp: 5, city:"New York"),
        WeatherInfo(image: "cloud", temp:5, city:"Kansas       City"),
        WeatherInfo(image: "sun.max", temp: 80, city:"San       Francisco"),
        WeatherInfo(image: "snow", temp: 5, city:"Chicago"),
        WeatherInfo(image: "cloud.rain", temp: 49,      city:"Washington DC"),
        WeatherInfo(image: "cloud.heavyrain", temp: 60,       city:"Seattle"),
        WeatherInfo(image: "sun.min", temp: 75,       city:"Baltimore"),
        WeatherInfo(image: "sun.dust", temp: 65,       city:"Austin"),
        WeatherInfo(image: "sunset", temp: 78,       city:"Houston"),
        WeatherInfo(image: "moon", temp: 80, city:"Boston"),
        WeatherInfo(image: "moon.circle", temp: 45,      city:"denver"),
        WeatherInfo(image: "cloud.snow", temp: 8,      city:"Philadelphia"),
        WeatherInfo(image: "cloud.hail", temp: 5,       city:"Memphis"),
        WeatherInfo(image: "cloud.sleet", temp:5,       city:"Nashville"),
        WeatherInfo(image: "sun.max", temp: 80,       city:"San Francisco"),
        WeatherInfo(image: "cloud.sun", temp: 5,       city:"Atlanta"),
        WeatherInfo(image: "wind", temp: 88,       city:"Las Vegas"),
        WeatherInfo(image: "cloud.rain", temp: 60,       city:"Phoenix"),
        ]
  3. Add the List view to the ContentView body. The list should display the information contained in our weatherData array. Also add the font(size: 25) and padding() modifiers:
    List {
                ForEach(self.weatherData){ weather in
                    HStack {
                        Image(systemName: weather.image)
                            .frame(width: 50, alignment:                                .leading)
                        Text("\(weather.temp)°F")
                            .frame(width: 80, alignment:                                .leading)
                        Text(weather.city)
                    }
                    .font(.system(size: 25))
                    .padding()
                }
            }

    The resulting preview should be as follows:

Figure 2.2 – List in the weather app

Figure 2.2 – List in the weather app

How it works…

We started the recipe by creating a WeatherInfo struct to hold and model our weather data. The id = UUID() property creates a unique identifier for each variable we create from the struct. Adding the id property to our struct allows us to later use it within a ForEach loop without providing id: .\self parameter as seen in the previous recipe.

The ForEach loop within the List view iterates through the items in our model data and copies one item at a time to the weather variable declared on the same line.

The ForEach loop contains HStack because we would like to display multiple items on the same line – in this case, all the information contained in the weather variable.

The font(.system(size: 25)) and padding() modifiers add some padding to the list rows to improve the design and readability of the information.

The images displayed are based on SF Symbols, explained in Chapter 1, Using the Basic SwiftUI Views and Controls.

Using custom rows in a list

The number of lines of code required to display items in a list view row could vary from one to several lines of code. A custom list row is used when working with several lines of code within a list view row. Implementing custom lists improves modularity and readability, and allows code reuse.

Getting ready

Let's start by creating a new SwiftUI app called CustomRows.

How to do it…

We will reuse part of the code in the static list and clean it up to make it more modular. We create a separate file to hold the WeatherInfo struct, a separate SwiftUI file for the custom WeatherRow, and finally, we implement the components in the ContentView.swift file. The steps are as follows:

  1. Create a new Swift file called WeatherInfo by selecting File | New | File | Swift File or by pressing the ⌘ + N keys.
  2. Define the WeatherInfo struct within the WeatherInfo.swift file:
    struct WeatherInfo: Identifiable {
        var id = UUID()
        var image: String
        var temp: Int
        var city: String
    }
  3. Create a weatherData variable that holds an array of WeatherInfo:
    let weatherData: [WeatherInfo] = [
         WeatherInfo(image: "snow", temp: 5,        city:"New York"),
         WeatherInfo(image: "cloud", temp:5,        city:"Kansas City"),
         WeatherInfo(image: "sun.max", temp: 80,        city:"San Francisco"),
         WeatherInfo(image: "snow", temp: 5, city:"Chicago"),
         WeatherInfo(image: "cloud.rain", temp: 49,        city:"Washington DC"),
         WeatherInfo(image: "cloud.heavyrain", temp: 60,        city:"Seattle"),
         WeatherInfo(image: "sun.min", temp: 75,       city:"Baltimore"),
         WeatherInfo(image: "sun.dust", temp: 65,       city:"Austin"),
         WeatherInfo(image: "sunset", temp: 78,       city:"Houston"),
         WeatherInfo(image: "moon", temp: 80, city:"Boston"),
         WeatherInfo(image: "moon.circle", temp: 45,         city:"denver"),
         WeatherInfo(image: "cloud.snow", temp: 8,        city:"Philadelphia"),
         WeatherInfo(image: "cloud.hail", temp: 5,       city:"Memphis"),
         WeatherInfo(image: "cloud.sleet", temp:5,       city:"Nashville"),
         WeatherInfo(image: "sun.max", temp: 80,        city:"San Francisco"),
         WeatherInfo(image: "cloud.sun", temp: 5,        city:"Atlanta"),
         WeatherInfo(image: "wind", temp: 88,        city:"Las Vegas"),
         WeatherInfo(image: "cloud.rain", temp: 60,        city:"Phoenix"),
         ]
  4. Create a new SwiftUI file by selecting File | New | File | SwiftUI View or by pressing the + N keys.
  5. Name the file WeatherRow.
  6. Add the following code to design the look and functionality of the weather row:
    struct WeatherRow: View {
        var weather: WeatherInfo
        var body: some View {
            HStack {
                Image(systemName: weather.image)
                    .frame(width: 50, alignment: .leading)
                Text("\(weather.temp)°F")
                    .frame(width: 80, alignment: .leading)
                Text(weather.city)
            }
            .font(.system(size: 25))
            .padding()
        }
    }
  7. Add the following code to the WeatherRow_Previews struct to display information from a sample WeatherInfo struct instance:
    static var previews: some View {
            WeatherRow(weather: WeatherInfo(image: "snow",           temp: 5, city:"New York"))
        }
  8. The resulting WeatherRow.swift canvas preview should look as follows:
    Figure 2.3 – WeatherRow preview

    Figure 2.3 – WeatherRow preview

  9. Open the ContentView.swift file and create a list to display data using the custom WeatherRow component:
    var body: some View {
                List {
                    ForEach(weatherData){ weather in
                        WeatherRow(weather: weather)
                    }
                }
            }

    The resulting canvas preview should look as follows:

Figure 2.4 – CustomRowApp preview

Figure 2.4 – CustomRowApp preview

Run the app live preview and admire the work of your own hands.

How it works…

The WeatherInfo Swift file contains the description of the WeatherInfo struct. Our dataset, an array of WeatherInfo variables, was also declared in the file to make it available to other sections of the project.

The WeatherRow SwiftUI file contains the design we will use for each weather row. The weather property within WeatherRow will hold the WeatherInfo arguments passed to the view. HStack in the body of WeatherRow is used to display data from weather variables since a SwiftUI view can only return one view at a time. When displaying multiple views – an image view and two text views, in this case – the views are all encased in an HStack component. The .frame(width: 50, alignment: .leading) modifier added to the image and first text view sets the width used by the element it modifies to 50 units and the alignment to the .leading parameter.

Finally, the .font(.system(size: 25)) and .padding() modifiers are added to HStack to increase the text and image font sizes and add padding to all sides of WeatherRow.

Adding rows to a list

Lists are usually used to add, edit, remove, or display content from an existing dataset. In this section, we will go over the process of adding items to an already existing list.

Getting ready

Let's start by creating a new SwiftUI app called AddRowsToList.

How to do it…

To implement the add functionality, we will enclose the List view in NavigationView, and add a button to navigationBarItems that triggers the add function we will create. The steps are as follows:

  1. Create a state variable in the ContentView struct that holds an array of integers:
    @State var numbers = [1,2,3,4]
  2. Add a NavigationView component containing a List view to the ContentView body:
    NavigationView{
                List{
                    ForEach(self.numbers, id:\.self){                  number in
                        Text("\(number)")
                    }
                }
    }
  3. Add a navigationBarItems modifier to the list closing brace that contains a button that triggers the addItemToRow() function:
    .navigationBarItems(trailing: Button(action: {
                        self.addItemToRow()
                    }){
                        Text("Add")
                    })
  4. Implement the addItemToRow() function, which appends a random integer to the numbers array. Place the function within the ContentView struct, immediately after the body variable's closing brace:
    private func addItemToRow() {
            self.numbers.append(Int.random(in: 0 ..< 100))
        }
  5. For the beauty and aesthetics, add a navigationBarTitle modifier to the end of the list so as to make it display a title at the top of the list:
    .navigationBarTitle("Number List", displayMode: .inline)
  6. The resulting code should be as follows:
    struct ContentView: View {
        @State var numbers = [1,2,3,4]
        var body: some View {
            NavigationView{
                List{
                    ForEach(self.numbers, id:\.self){                  number in
                        Text("\(number)")
                    }
                }.navigationBarTitle("Number List",                displayMode: .inline)
                    .navigationBarItems(trailing:                   Button("Add", action: addItemToRow))
            }
        }
        private func addItemToRow() {
            self.numbers.append(Int.random(in: 0 ..< 100))
        }
    }

    The resulting preview should be as follows:

Figure 2.5 – AddRowToList preview

Figure 2.5 – AddRowToList preview

Run the app live preview and admire the work of your own hands!

How it works…

The array of numbers is declared as a @State variable because we want the view to be refreshed each time the value of the items in the array changes – in this case, each time we add an item to the numbers array.

The .navigationBarTitle("Number List", displayMode: .inline) modifier adds a title to the list using the .inline display mode parameter.

The .navigationBarItems(trailing: Button(…)…) modifier adds a button to the trailing end of the display, which triggers the addItemToRow function when clicked.

The addItemToRow function generates a random number in the range 0–99 and appends it to the numbers array.

Deleting rows from a list

In this section, we will display a list of countries and use a swipe motion to delete items from the list one at a time.

Getting ready

Let's start by creating a SwiftUI app called DeleteRowFromList.

How to do it…

We use the List view's .onDelete(perform: …) modifier to implement list deletion. The process is as follows:

  1. Add a state variable to the ContentView struct called countries. The variable should contain an array of countries:
    @State var countries = ["USA", "Canada",  "England","Cameroon", "South Africa", "Mexico" ,     "Japan", "South Korea"]
  2. Create a List view in the ContentView body that uses a ForEach loop to display the contents of the countries array:
    	 List {
              ForEach(countries, id: \.self) { country in
                  Text(country)
               	}
      	}
  3. Add a .onDelete(perform: self.deleteItem) modifier to the ForEach loop.
  4. Implement the deleteItem() function. The function should be placed below the body variable's closing brace:
    	private func deleteItem(at indexSet: IndexSet){
            self.countries.remove(atOffsets: indexSet)
        }
  5. Optionally, enclose the List view in a navigation view and add a .navigationBarTitle("Countries", displayMode: .inline) modifier to the list. The resulting ContentView struct should be as follows:
    struct ContentView: View {
        @State var countries = ["USA", "Canada",     "England","Cameroon", "South Africa", "Mexico" ,        "Japan", "South Korea"]
        var body: some View {
            NavigationView{
                List {
                    ForEach(countries, id: \.self) {                  country in
                        Text(country)
                    }
                    .onDelete(perform: self.deleteItem)
                }
                .navigationBarTitle("Countries",                displayMode: .inline)
            }
        }
        private func deleteItem(at indexSet: IndexSet){
            self.countries.remove(atOffsets: indexSet)
        }

    Run the canvas review by clicking the play button next to the canvas preview. A swipe from right to left on a row causes a Delete button to appear. Click on the button to delete the row:

Figure 2.6 – DeleteRowFromList preview execution

Figure 2.6 – DeleteRowFromList preview execution

Run the app live preview and admire the work of your own hands!

How it works…

Navigation views and list views were discussed earlier. Only the .onDelete(…) modifier is new. The .onDelete(perform: self.deleteItem) modifier triggers the deleteItem() function when the user swipes from right to left.

The deleteItem(at indexSet: IndexSet) function takes a single parameter, IndexSet, which represents the index of the row to be removed/deleted. The .onDelete() modifier automatically knows to pass the IndexSet parameter to deleteItem(…).

There's more…

Deleting an item from a list view can also be performed by embedding the list in a navigation view and adding an EditButton component.

Editing a list

Implementing the EditButton component in a list is very similar to implementing the delete button in the previous recipe. An edit button offers the user the option to quickly delete items by clicking a minus sign to the left of each list row.

Getting ready

Let's start by creating a SwiftUI app called EditListApp.

How to do it…

We will reuse some of the code from the previous recipe to complete this project. Take the following steps:

  1. Replace the EditListApp app's ContentView struct with the following content from the DeleteRowFromList app:
    struct ContentView: View {
        @State var countries = ["USA", "Canada",     "England","Cameroon", "South Africa", "Mexico" ,        "Japan", "South Korea"]
        var body: some View {
            NavigationView{
                List {
                    ForEach(countries, id: \.self) {                  country in
                        Text(country)
                    }
                    .onDelete(perform: self.deleteItem)
                }
                .navigationBarTitle("Countries",                displayMode: .inline)
            }
        }
        private func deleteItem(at indexSet: IndexSet){
            self.countries.remove(atOffsets: indexSet)
        }
    }
  2. Add a .navigationBarItems(trailing: EditButton()) modifier to the List view, just below the .navigationBarTitle("Countries", displayMode:.inline) modifier.

    Run the preview and click the Edit button at the top-right corner of the screen. Minus (-) signs enclosed in red circles appear to the left of each row:

Figure 2.7 – EditListApp preview execution

Figure 2.7 – EditListApp preview execution

Run the app live preview and admire the work of your own hands!

How it works…

The .navigationBarItems(trailing: EditButton()) modifier adds an edit button to the right corner of the display that, once clicked, creates the view shown in the preceding screenshot. A click on the delete button executes the .onDelete(perform: self.deleteItem) modifier. The modifier then executes the deleteItem function, which removes/deletes the selected row.

There's more…

To move the edit button to the right of the navigation bar, change the modifier to .navigationBarItems(leading: EditButton()).

Moving rows in a list

In this section, we will create an app that implements a list view and allows the user to move/reorganize rows.

Getting ready

Let's start by creating a new SwiftUI app in Xcode called MovingListRows.

How to do it…

To allow users to move rows, we need to add a .onMove(..) modifier to the end of the list view's ForEach loop. We also need to embed the list in a navigation view and add a navigationBarItems modifier that implements an EditButton component. The steps are as follows:

  1. Open ContentView.swift. Within the ContentView struct, add a @State variable in Content called countries that contains an array of countries:
    @State var countries = ["USA", "Canada",   "England","Cameroon", "South Africa", "Mexico" ,    "Japan", "South Korea"]
  2. Replace the Text view in the body with a navigation view:
    NavigationView{
            }
  3. Add a list and a ForEach loop within NavigationView that displays the content of the countries variable:
    List {
         	ForEach(countries, id: \.self) { country in
       		Text(country)
           	}.onMove(perform: moveRow)
          }
  4. Add a .onMove(…) modifier to the ForEach loop that calls the moveRow function:
    	ForEach(countries, id: \.self) { country in
       		Text(country)
           	}.onMove(perform: moveRow)
  5. Add the .navigationBarTitle("Countries", displayMode: .inline) and .navigationBarItems(trailing: EditButton()) modifiers to the end of the List view:
    List {
                    .
    			.
    			.
          }
          .navigationBarTitle("Countries",          isplayMode: .inline)
          .navigationBarItems(trailing: EditButton())
  6. Implement the moveRow function at the end of the body variable's closing brace:
    private func moveRow(source: IndexSet, destination: Int){
            countries.move(fromOffsets: source, toOffset:           destination)
        }
  7. The completed ContentView struct should be as follows:
    struct ContentView: View {
        @State var countries = ["USA", "Canada",      "England","Cameroon", "South Africa", "Mexico" ,         "Japan", "South Korea"]
        var body: some View {
            NavigationView{
                List {
                    ForEach(countries, id: \.self) {                   country in
                        Text(country)
                    }
                    .onMove(perform: moveRow)
                }
                .navigationBarTitle("Countries",                displayMode: .inline)
                .navigationBarItems(trailing: EditButton())
            }
        }
        private func moveRow(source: IndexSet,       destination: Int){
            countries.move(fromOffsets: source,           toOffset: destination)
        }
    }

    Run the application in the canvas, on a simulator, or on a physical device. A click on the Edit button at the top-right corner of the screen displays a hamburger symbol to the right of each row. Click and drag the symbol to move the row on which the country is displayed:

Figure 2.8 – MovingListRows preview when running

Figure 2.8 – MovingListRows preview when running

Run the app live preview and move the rows up or down. Nice work!

How it works…

To move list rows, you need to add the list to a navigation view, add the .onMove(perform:) modifier to the ForEach loop, and add a .navigationBarItems(trailing: EditButton()) modifier to the list.

The moveRow(source: IndexSet, destination: Int) function takes two parameters: the source, an IndexSet argument representing the current index of the item to be moved, and the destination, an integer representing the destination of the row. The .onMove(perform:) modifier automatically passes those arguments to the function.

Adding sections to a list

In this section, we will create an app that implements a static list with sections. The app will display a partial list of countries grouped by continent.

Getting ready

Let's start by creating a new SwiftUI app in Xcode and name it ListWithSections.

How to do it…

We will add section views to our list view. We will then add a few countries to each of the sections. Proceed as follows:

  1. (Optional) open the ContentView.swift file and replace the Text view in the body with a navigation view. Wrapping the list in a navigation view allows you to add a title and navigation items to the view:
    NavigationView {
    }
  2. Add a list and section to the navigation view:
    List {
      	Section(header: Text("North America")){
        		Text("USA")
               Text("Canada")
               Text("Mexico")
               Text("Panama")
               Text("Anguilla")
          }
    }
  3. Add a listStyle(..) modifier to the end of the list to change its style from the default plain style to GroupedListStyle():
    List {
      	.
    	.
    	.
     }
    .listStyle(GroupedListStyle())
    .navigationBarTitle("Continents and Countries",   displayMode: .inline)
  4. (Optional) add a navigationBarTitle(..) modifier to the list, just below the list style. Add this section if you included the navigation view in step 1.
  5. Add more sections representing various continents to the navigation view. The resulting ContentView struct should look as follows:
    struct ContentView: View {
        var body: some View {
            NavigationView{
                List {
                    Section(header: Text("North America")){
                        Text("USA")
                        Text("Canada")
                        Text("Mexico")
                        Text("Panama")
                        Text("Anguilla")
                    }
                    Section(header: Text("Africa")){
                        Text("Nigeria")
                        Text("Ghana")
                        Text("Kenya")
                        Text("Senegal")
                    }
                    Section(header: Text("Europe")){
                        Text("Spain")
                        Text("France")
                        Text("Sweden")
                        Text("Finland")
                        Text("UK")
                    }
                }
            .listStyle(GroupedListStyle())
                .navigationBarTitle("Continents and                Countries", displayMode: .inline)
            }
        }
    }

    The canvas preview should now show a list with sections, as follows:

Figure 2.9 – ListWithSections preview

Figure 2.9 – ListWithSections preview

Good work! Now, run the app live preview and admire the work of your own hands.

How it works…

SwiftUI's sections are used to separate items into groups. In this recipe, we used sections to visually group countries into different continents. Sections can be used with a header, as follows:

Section(header: Text("Europe")){
                    Text("Spain")
                    Text("France")
                    Text("Sweden")
                    Text("Finland")
                    Text("UK")
                }

They can also be used without a header:

Section {
               Text("USA")
               Text("Canada")
               Text("Mexico")
               Text("Panama")
               Text("Anguilla")
         }

When using a section embedded in a list, we can add a .listStyle() modifier to change the styling of the whole list.

Using LazyHStack and LazyVStack (iOS 14+)

SwiftUI 2.0 introduced the LazyHStack and LazyVStack components. These components are used in a similar way to regular HStack and VStack components but offer the advantage of lazy loading. Lazy components are loaded just before the item becomes visible on the device's screen during a device scroll, therefore reducing latency.

We will create an app that uses LazyHStack and LazyVStack and observe how it works.

Getting ready

Let's create a new SwiftUI app called LazyStacks:

  1. Open Xcode and click Create New Project.
  2. In the Choose template window, select iOS and then App.
  3. Click Next.
  4. Enter LazyStacks in the Product Name field and select SwiftUI App from the Life Cycle field.
  5. Click Next and select a location on your computer where the project should be stored.

How to do it…

We will implement a LazyHStack and LazyVStack view within a single SwiftUI view file by embedding both in a VStack component. The steps are as follows:

  1. Click on the ContentView.swift file to view its content in Xcode's editor pane.
  2. Let's create a ListRow SwiftUI view that will have two properties: an ID and a type. ListRow should also print a statement showing what item is currently being initialized:
    struct ListRow: View {
        let id: Int
        let type: String
        init(id: Int, type: String){
            print("Loading \(type) item \(id)")
            self.id = id
            self.type = type
        }
        var body: some View {
            Text("\(type) \(id)").padding()
        }
    }
  3. Replace the initial Text view with VStack:
    VStack {
    }
  4. Add a horizontal scroll view inside the VStack component and use a .frame() modifier to limit the view's height:
    ScrollView(.horizontal){
    }.frame(height: 100, alignment: .center)
  5. Add LazyHStack inside the scroll view with a ForEach view that iterates through the numbers 110000 and displays them using our ListRow view:
    LazyHStack {
         ForEach(1...10000, id:\.self){ item in
           ListRow(id: item, type: "Horizontal")
               }
                    }
  6. Add a second vertical scroll view to VStack with a LazyVStack struct that loops through numbers 110000:
    ScrollView {
      LazyVStack {
        ForEach(1...10000, id:\.self){ item in
          ListRow(id: item, type: "Vertical")
                 }
            }
        }
  7. Now, let's observe lazy loading in action. If the Xcode debug area is not visible, click on View | Debug Area | Show Debug Area:
    Figure 2.10 – Show Debug Area

    Figure 2.10 – Show Debug Area

  8. Select a simulator to use for running the app. The app should look as follows:
    Figure 2.11 – Selecting the simulator from Xcode

    Figure 2.11 – Selecting the simulator from Xcode

  9. Click the play button to run the code in the simulator:
    Figure 2.12 – The LazyStacks app running on the simulator

    Figure 2.12 – The LazyStacks app running on the simulator

  10. Scroll through the items in LazyHStack (located at the top). Observe how the print statements appear in the debug area just before an item is displayed on the screen. Each item is initialized just before it is displayed.
  11. Scroll through the items in LazyVStack. Observe how the print statements appear in the debug area just before an item is displayed.

How it works…

We started this recipe by creating the ListRow view because we wanted to clearly demonstrate the advantage of lazy loading over the regular method where all items get loaded at once. The ListRow view has two properties: an ID and a string. We add a print statement to the init() function so that we can observe when each item gets initialized:

    init(id: Int, type: String){
        print("Loading \(type) item \(id)")
        self.id = id
        self.type = type
    }

The ListRow view body presents a Text view with the ID and type parameters passed to it.

Moving up to the ContentView struct, we replace the initial Text view in the body variable with a VStack component. This allows us to implement both LazyHStack and LazyVStack within the same SwiftUI view.

We implement LazyHStack by first wrapping it in a scroll view, then using a ForEach struct to iterate over the range of values we want to display. For each of those values, a new ListRow view is initialized just before it becomes visible when the user scrolls down:

ScrollView {
  LazyVStack {
   ForEach(1...10000, id:\.self){ item in
    ListRow(id: item, type: "Vertical")
             }
          }
       }

Run the app using a device emulator to view the print statements before each item is initialized. Nothing will be printed if the app is run in live preview mode on Xcode.

There's more…

Try implementing the preceding app using a regular HStack or VStack component and observe the performance difference. The app will be significantly slower since all the rows are initialized at once.

Using LazyHGrid and LazyVGrid (iOS 14+)

Just like lazy stacks, lazy grids use lazy loading to display content on the screen. They initialize only the subset of the items that would soon be displayed on the screen as the user scrolls. SwiftUI 2 introduced the LazyVGrid and LazyHGrid views. Let's implement a lazy grid that displays some text in this recipe.

Getting ready

Create a new SwiftUI iOS project and name it UsingLazyGrids.

How to do it…

We'll implement a LazyVGrid and LazyHGrid view inside a Stack view so as to observe both views in action on a single page. The steps are as follows:

  1. Above the ContentView struct's body variable, let's create an array of GridItem columns. The GridItem struct is used to configure the layout of LazyVGrid:
      let columns = [
        GridItem(.adaptive(minimum: 100))
      ]
  2. Create an array of GridItem rows that define the layout that would be used in LazyHGrid:
      let rows = [
           GridItem(.flexible()),
           GridItem(.flexible()),
           GridItem(.flexible())
      ]
  3. Create an array of colors. This will be used for styling some items in our view:
    let colors: [Color] = [.green,.red, .yellow,.blue]
  4. Replace the initial Text view in the body view with a VStack component and a scroll view.
  5. Within the scroll view, add a LazyVGrid view and a ForEach struct that iterates over the numbers 1999 and displays the number in a Text view:
     VStack {
       ScrollView {
        LazyVGrid(columns: columns, spacing:20) {
         ForEach(1...999, id:\.self){ index in
          Text("Item \(index)")
         }
        }
       }
     }
  6. The resulting view displays the numbers in a grid, but the content looks very bland. Let's add some zest by styling the text.
  7. Add a padding() modifier, a background modifier that uses the value of the current index to pick a color from our color array, and the clipshape() modifier to give each Text view a capsule shape:
    …
    ForEach(1...999, id:\.self){ index in
             Text("Item \(index)")
             .padding(EdgeInsets(top: 30, leading: 15,             bottom: 30, trailing: 15))
             .background(colors[index % colors.count])
              .clipShape(Capsule())
              }
    …
  8. Now, let's add a scroll view and LazyHStack. You will notice that everything else is the same as the LazyVGrid view except for the column parameter that's changed to a row parameter:
    VStack {
             …
              ScrollView(.horizontal) {
               LazyHGrid(rows: rows, spacing:20) {
                ForEach(1...999, id:\.self){ index in
                 Text("Item \(index)")
                      .foregroundColor(.white)
                      .padding(EdgeInsets(top: 30, leading:                     15, bottom: 30, trailing: 15))
                      .background(colors[index % colors.                     count])
                      .clipShape(Capsule())
                        }
                    }
                }
            }

    The resulting preview should look as follows:

Figure 2.13 – The UsingLazyStacks app

Figure 2.13 – The UsingLazyStacks app

Run the app in the preview and vertically scroll through LazyVGrid and horizontally scroll through LazyHGrid. Observe how smoothly the scrolling occurs despite the fact that our data source contains close to 2,000 elements. Scrolling stays responsive because all our content is lazily loaded.

How it works…

One of the fundamental concepts of using lazy grids involves understanding how to define the LazyVGrid columns or the LazyHGrid rows. The LazyVGrid column variable was defined as an array containing a single GridItem component but causes three rows to be displayed. GridItem(.adaptive(minimum: 80)) tells SwiftUI to use at least 80 units of width for each item and place as many items as it can along the same row. Thus, the number of items displayed on a row may increase when the device is changed from portrait to landscape orientation, and vice versa.

GridItem(.flexible()), on the other hand, fills up each row as much as possible:

let rows = [
        GridItem(.flexible()),
        GridItem(.flexible()),
        GridItem(.flexible())
    ]

Adding three GridItem(.flexible()) components divides the available space into three equal rows for data display. Remove or add more GridItem(.flexible()) components to the row array and observe how the number of rows decreases or increases.

We provide two parameters to our lazy grids: a columns/rows parameter that specifies the layout of the grid and a spacing parameter that defines the spacing between the grid and the next item in the parent view.

Using ScrollViewReader (iOS 14+)

ScrollViewReader can be used to programmatically scroll to a different section of a list that might not be currently visible. In this recipe, we will create an app that displays a list of characters from A to L. The app will also have a button at the top for programmatically scrolling to the last element in the list and a button at the bottom for programmatically scrolling to an element in the middle of the list.

Getting ready

Create a new SwiftUI app using the UIKit App Delegate life cycle:

Figure 2.14 – UIKit App Delegate in Xcode

Figure 2.14 – UIKit App Delegate in Xcode

Name the app UsingScrollViewReader.

How to do it…

We will start by creating an array of structs with a name and an ID. The array will be used to display SF symbols for the characters A–L. We will then proceed to implement ScrollViewReader and programmatically move to the top or the bottom of the list.

The steps are as follows:

  1. Create a struct called ImageStore, just above the ContentView_Previews struct. The struct should implement the Identifiable protocol:
    struct ImageStore: Identifiable {
        var name: String
        var id: Int
    }
  2. Within the ContentView struct, just before the body variable, declare an array called imageNames. Initialize the array with ImageStore structs whose name parameters represent the letters A–Q from SF Symbols:
        let imageNames = [
            ImageStore(name:"a.circle.fill",id:0),
            ImageStore(name:"b.circle.fill",id:1),
            ImageStore(name:"c.circle.fill",id:2),
            ImageStore(name:"d.circle.fill",id:3),
            ImageStore(name:"e.circle.fill",id:4),
            ImageStore(name:"f.circle.fill",id:5),
            ImageStore(name:"g.circle.fill",id:6),
            ImageStore(name:"h.circle.fill",id:7),
            ImageStore(name:"i.circle.fill",id:8),
            ImageStore(name:"j.circle.fill",id:9),
            ImageStore(name:"k.circle.fill",id:10),
            ImageStore(name:"l.circle.fill",id:11),
            ImageStore(name:"m.circle.fill",id:12),
            ImageStore(name:"n.circle.fill",id:13),
            ImageStore(name:"o.circle.fill",id:14),
            ImageStore(name:"p.circle.fill",id:15),
            ImageStore(name:"q.circle.fill",id:16),
        ]
  3. Replace the TextView component in the body variable with a ScrollView component, ScrollViewReader, and a Button component to navigate to the letter Q in the list:
    ScrollView {
         ScrollViewReader { value in
         Button("Go to letter Q") {
         value.scrollTo(16)
               }
            }         
        }
  4. Now use a ForEach struct to iterate over the imageNames array and display its content using the SwiftUI's Image struct:
     ForEach(imageNames){ image in
       Image(systemName: image.name)
         .id(image.id)
         .font(.largeTitle)
         .foregroundColor(Color.yellow)
         .frame(width: 90, height: 90)
         .background(Color.blue)
         .padding()
       }
  5. Now, add another button at the bottom that causes the list to scroll back to the top:
    Button("Go back to A") {
            value.scrollTo(0)
       }
  6. The app should be able to run now, but let's add some padding and background to the bottom and top buttons to improve their appearance:
    Button("Go to letter Q") {
           value.scrollTo(0)
          }
          .padding()
          .background(Color.yellow)
    	Button("Go to G") {
            value.scrollTo(6, anchor: .bottom)
          }
           .padding()
           .background(Color.yellow)

    The resulting app preview should look as follows:

Figure 2.15 – The UsingScrollViewReader app

Figure 2.15 – The UsingScrollViewReader app

Run the app in Xcode live preview and tap the button at the top to programmatically scroll down to the letter Q.

Scroll to the bottom of the view and tap the button to scroll up to the view where the letter G is at the bottom of the visible area.

How it works…

We start this recipe by creating the ImageStore struct that defines the properties of each image we want to display:

struct ImageStore: Identifiable {
    var name: String
    var id: Int
}

The id parameter is required for ScrollViewReader as a reference for the location to scroll to, just like a house address provides the final destination for driving. By making the struct extend the Identifiable protocol, we are able to iterate over the imageNames array without specifying an id parameter to the ForEach struct:

ForEach(imageNames){ image in
                    Image(systemName: image.name)
				…
                }

ScrollViewReader should be embedded inside a scroll view. This provides a scrollTo() method that can be used to programmatically scroll to the item whose index is specified in the method:

ScrollViewReader { value in
                Button("Go to letter Q") {
                    value.scrollTo(16)
                }
			…
}

The scrollTo() method also has an anchor parameter that is used to specify the position of the item we are scrolling to; for example, scrollTo(6, anchor: .top), in this case, causes the app to scroll until the ImageStore item with ID 6 at the bottom of the view.

Using expanding lists (iOS 14+)

Expanding lists can be used to display hierarchical structures within a list. Each list item can be expanded to view its contents. The expanding ability is achieved by creating a struct that holds some information and an optional array of items of the same type as the struct itself. Let's examine how expanding lists work by creating an app that displays the contents of a backpack.

Getting ready

Create a new SwiftUI app and name it UsingExpandingLists.

How to do it…

We start by creating the Backpack struct that describes the properties of the data we want to display. We'll then create a number of Backpack constants and display the hierarchical information using a List view containing a children property.

The steps are as follows:

  1. At the bottom of the ContentView.swift file, define a Backpack struct that has an id, name, icon, and content property. The content property's type should be an optional array of Backpack structs:
    struct Backpack: Identifiable {
        let id = UUID()
        let name: String
        let icon: String
        var content: [Backpack]?
    }
  2. Below the Backpack struct declaration, create three variables: two currency types and an array of currencies:
    let dollar = Backpack(name: "Dollar", icon: "dollarsign.   circle")
    let yen = Backpack(name: "Yen",icon: "yensign.circle")
    let currencies = Backpack(name: "Currencies", icon:    "coloncurrencysign.circle", content: [dollar, yen])
  3. Create a pencil, hammer, paperclip, and glass constant:
    let pencil = Backpack(name: "Pencil",icon: "pencil.  circle")
    let hammer = Backpack(name: "Hammer",icon: "hammer")
    let paperClip = Backpack(name: "Paperclip",icon:   "paperclip")
    let glass = Backpack(name: "Magnifying glass",   icon: "magnifyingglass")
  4. Create a bin Backpack constant that contains paperclip and glass, as well as a tool constant that holds pencil, hammer, and bin:
    let bin  = Backpack(name: "Bin", icon: "arrow.up.bin",  content: [paperClip, glass])
    let tools = Backpack(name: "Tools", icon: "folder",   content: [pencil, hammer,bin])
  5. Going back to the ContentView struct, add an instance property; items, which contains an array of two items, the currencies and tools constants defined earlier:
    struct ContentView: View {
        let items = [currencies,tools]
        …
    }
  6. Within the body variable, replace the Text view with a List view that displays the content of the items array:
        var body: some View {
            List(items, children: \.content){ row in
                Image(systemName: row.icon)
                Text(row.name)
            }
        }

    The resulting Xcode live preview should look as follows when expanded:

Figure 2.16 – UsingExpandingLists

Figure 2.16 – Using expanding lists

Run the Xcode live preview and click on the arrows to expand and collapse the views.

How it works…

We start by creating a struct of the proper format for expanding lists. Expanding lists require one of the struct properties to be an optional array of the same type as the struct itself. Our Backpack content property holds an optional array of Backpack items:

struct Backpack: Identifiable {
    let id = UUID()
    let name: String
    let icon: String
    var content: [Backpack]?
}

The UUID() function generates a random identifier and stores it in the id property. We can manually provide the name, icon, and content variables for the items we add to the backpack.

We create our first hierarchical structure by creating the Dollar and Yen variables:

let dollar = Backpack(name: "Dollar", icon: "dollarsign.  circle")
let yen = Backpack(name: "Yen",icon: "yensign.circle")

Observe that no content property has been provided for both variables. This is possible because the content is an optional parameter.

We then create a currencies constant that that has name and icon. The Dollar and Yen variables created earlier are added to its content array.

We use a similar composition to add elements to our tool constant.

After setting up the data we want to display, we create an item property in our ContentView struct that holds an array of Backpack constants created earlier:

let items = [currencies,tools]

Finally, we replace the Text view in the ContentView body with a List view that iterates over the items. The list is made expandable by adding the children parameter, which expects an array of the same time as the struct passed to the List view.

There's more…

The tree structure used for expandable lists can also be generated on a single line, as follows:

let tools = Backpack(name: "Tools", icon: "folder", content:
   [Backpack(name: "Pencil",icon: "pencil.circle"),
    Backpack(name: "Hammer",icon: "hammer"),
    Backpack(name: "Bin", icon: "arrow.up.bin", content:
        [Backpack(name: "Paperclip",icon: "paperclip"),
         Backpack(name: "Magnifying glass", icon:           "magnifyingglass")
           ])
       ])

The SwiftUI List view provides out-of-the-box support for OutlineGroup in iOS 14. OutlineGroup computes views and disclosure groups on demand from an underlying collection.

See also

Refer to the WWDC20 video on stacks, grids, and outlines at https://developer.apple.com/videos/play/wwdc2020/10031/.

Refer to the Apple documentation on OutlineGroup at https://developer.apple.com/documentation/swiftui/outlinegroup.

Using disclosure groups to hide and show content (iOS 14+)

DisclosureGroup is a view used to show or hide content based on the state of a disclosure control. It takes two parameters: a label to identify its content and a binding to control whether its content is shown or hidden. Let's take a closer look at how it works by creating an app that shows and hides content in a disclosure group.

Getting ready

Create a new SwiftUI project and name it UsingDisclosureGroup.

How to do it…

We will create an app the uses DisclosureGroup views to reveal some planets in our solar system, continents on the Earth, and some surprise text. The steps are as follows:

  1. Below the ContentView struct, add a state property called showplanets:
    @State private var showplanets = true
  2. Replace the Text view in the body variable with a DisclosureGroup view that displays some planets:
     DisclosureGroup("Planets", isExpanded: $showplanets){
         Text("Mercury")
         Text("Venus") 
    }
  3. Review the result in Xcode's live preview, then nest another DisclosureGroup view for planet Earth. The group should contain the list of Earth's continents:
      DisclosureGroup("Planets", isExpanded: $showplanets){
    	…
           DisclosureGroup("Earth"){
                    Text("North America")
                    Text("South America")
                    Text("Europe")
                    Text("Africa")
                    Text("Asia")
                    Text("Antarctica")
                    Text("Oceania")
                }
            }
  4. Let's add another DisclosureGroup view using a different way of defining them. Since the body variable can't display two views, let's embed our parent DisclosureGroup view in a VStack component. Hold down the key and click on the parent DisclosureGroup view and select Embed in VStack from the list of actions.
  5. Below our parent DisclosureGroup view, add another one that reveals surprise text when clicked:
    VStack {
        DisclosureGroup("Planets", isExpanded: $showplanets){
             Text("Mercury")
             Text("Venus")
                    
             DisclosureGroup("Earth"){
                Text("North America")
                Text("South America")
                Text("Europe")
                Text("Africa")
                Text("Asia")
                Text("Antarctica")
                Text("Oceania")
             }
          }
          DisclosureGroup{
            Text("Surprise! This is an alternative                  way of using DisclosureGroup")
          } label : {
              Label("Tap to reveal", systemImage: "cube.box")
                    .font(.system(size:25, design: .rounded))
                    .foregroundColor(.blue)
                }
          }

    The resulting preview should be as follows:

Figure 2.17 – The UsingDisclosureGroup app

Figure 2.17 – The UsingDisclosureGroup app

Run the Xcode live preview and click on the arrows to expand and collapse the views.

How it works…

Our initial DisclosureGroup view presents a list of planets. By passing in a binding, we are able to read the state change and know whether the DisclosureGroup view is in an open or closed state:

DisclosureGroup("Planets", isExpanded: $showplanets){
     Text("Mercury")
     Text("Venus") 
} 

We can also use DisclosureGroup views without bindings, depending solely on the default UI:

DisclosureGroup("Earth"){
    Text("North America")
    Text("South America")
     …
 }

Lastly, we used the new Swift 5.3 closure syntax to separate the Text and Label portions into two separate views. This allows more customization of the Label portion:

DisclosureGroup{
  Text("Surprise! This is an alternative way of using      DisclosureGroup")
  } label : {
      Label("Tap to reveal", systemImage: "cube.box")
           .font(.system(size:25, design: .rounded))
           .foregroundColor(.blue)
  }

DisclosureGroup views are very versatile as they can be nested and used to display content in a hierarchical way.

Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • Apply the declarative programming paradigm for building cross-platform UIs for Apple devices
  • Learn to integrate UIKit, Core Data, Sign in with Apple, and Firebase with SwiftUI
  • Adopt the new SwiftUI 2.0 features to build visually appealing UIs at speed

Description

SwiftUI is an innovative and simple way to build beautiful user interfaces (UIs) for all Apple platforms, right from iOS and macOS through to watchOS and tvOS, using the Swift programming language. In this recipe-based book, you’ll work with SwiftUI and explore a range of essential techniques and concepts that will help you through the development process. The recipes cover the foundations of SwiftUI as well as the new SwiftUI 2.0 features introduced in iOS 14. Other recipes will help you to make some of the new SwiftUI 2.0 components backward-compatible with iOS 13, such as the Map View or the Sign in with Apple View. The cookbook begins by explaining how to use basic SwiftUI components. Then, you’ll learn the core concepts of UI development such as Views, Controls, Lists, and ScrollViews using practical implementation in Swift. By learning drawings, built-in shapes, and adding animations and transitions, you’ll discover how to add useful features to the SwiftUI. When you’re ready, you’ll understand how to integrate SwiftUI with exciting new components in the Apple development ecosystem, such as Combine for managing events and Core Data for managing app data. Finally, you’ll write iOS, macOS, and watchOS apps while sharing the same SwiftUI codebase. By the end of this SwiftUI book, you'll have discovered a range of simple, direct solutions to common problems found in building SwiftUI apps.

Who is this book for?

This book is for mobile developers who want to learn SwiftUI as well as experienced iOS developers transitioning from UIKit to SwiftUI. The book assumes knowledge of the Swift programming language. Knowledge of object-oriented design and data structures is useful but not necessary.

What you will learn

  • Explore various layout presentations in SwiftUI such as HStack, VStack, LazyHStack, and LazyVGrid
  • Create a cross-platform app for iOS, macOS, and watchOS
  • Get up to speed with drawings in SwiftUI using built-in shapes, custom paths, and polygons
  • Discover modern animation and transition techniques in SwiftUI
  • Add user authentication using Firebase and Sign in with Apple
  • Handle data requests in your app using Core Data
  • Solve the most common SwiftUI problems, such as integrating a MapKit map, unit testing, snapshot testing, and previewing layouts
Estimated delivery fee Deliver to United States

Economy delivery 10 - 13 business days

Free $6.95

Premium delivery 6 - 9 business days

$21.95
(Includes tracking information)

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Oct 19, 2020
Length: 614 pages
Edition : 1st
Language : English
ISBN-13 : 9781838981860
Vendor :
Apple
Category :
Languages :
Tools :

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Product feature icon AI Assistant (beta) to help accelerate your learning
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Estimated delivery fee Deliver to United States

Economy delivery 10 - 13 business days

Free $6.95

Premium delivery 6 - 9 business days

$21.95
(Includes tracking information)

Product Details

Publication date : Oct 19, 2020
Length: 614 pages
Edition : 1st
Language : English
ISBN-13 : 9781838981860
Vendor :
Apple
Category :
Languages :
Tools :

Packt Subscriptions

See our plans and pricing
Modal Close icon
$19.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
$199.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts
$279.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total $ 148.97
iOS 14 Programming for Beginners
$54.99
SwiftUI Projects
$38.99
SwiftUI Cookbook
$54.99
Total $ 148.97 Stars icon

Table of Contents

14 Chapters
Chapter 1: Using the Basic SwiftUI Views and Controls Chevron down icon Chevron up icon
Chapter 2: Going Beyond the Single Component with Lists and Scroll Views Chevron down icon Chevron up icon
Chapter 3: Viewing while Building with SwiftUI Preview Chevron down icon Chevron up icon
Chapter 4: Creating New Components and Grouping Views in Container Views Chevron down icon Chevron up icon
Chapter 5: Presenting Extra Information to the User Chevron down icon Chevron up icon
Chapter 6: Drawing with SwiftUI Chevron down icon Chevron up icon
Chapter 7: Animating with SwiftUI Chevron down icon Chevron up icon
Chapter 8: Driving SwiftUI with Data Chevron down icon Chevron up icon
Chapter 9: Driving SwiftUI with Combine Chevron down icon Chevron up icon
Chapter 10: Handling Authentication and Firebase with SwiftUI Chevron down icon Chevron up icon
Chapter 11: Handling Core Data in SwiftUI Chevron down icon Chevron up icon
Chapter 12: Cross-Platform SwiftUI Chevron down icon Chevron up icon
Chapter 13: SwiftUI Tips and Tricks Chevron down icon Chevron up icon
Other Books You May Enjoy Chevron down icon Chevron up icon

Customer reviews

Top Reviews
Rating distribution
Full star icon Full star icon Full star icon Full star icon Half star icon 4.8
(10 Ratings)
5 star 90%
4 star 0%
3 star 10%
2 star 0%
1 star 0%
Filter icon Filter
Top Reviews

Filter reviews by




L. Stone Jan 31, 2021
Full star icon Full star icon Full star icon Full star icon Full star icon 5
I've been studying Swift 5 and SwiftUI for a little while now, as a hobby, mostly using Udemy tutorials and a couple of basic books. This particular book, however, is helping me "put it all together" in my mind better than anything I've read or watched so far. The author takes a sort of layered, cyclical approach to topics: you'll see something show up early in the book, with little comment but it's easy to see what it's doing, so when you get to the chapter devoted to that thing you're not starting from total scratch. You also revisit old topics when learning new topics, so things can sink in better. So far I'm on page 165, and can hardly wait to go through the drawing and animation chapters (pp. 198-326). Several chapters follow for working with Combine and Firebase and so on, though I probably won't get to those for several months yet.
Amazon Verified review Amazon
Amazon Customer Mar 11, 2021
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Perfect for my needs!
Amazon Verified review Amazon
Frank Linux Mar 23, 2021
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Good variety of common topics. As mentioned in the advertising, the book works well for developers transitioning from UIKit to SwiftUI.
Amazon Verified review Amazon
Amazon Customer Dec 17, 2020
Full star icon Full star icon Full star icon Full star icon Full star icon 5
I like this book a lot. This book helps me get up and running pretty fast with SwiftUI. Highly recommend.
Amazon Verified review Amazon
Nicholas Jan 13, 2021
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Hello, I am an iOS developer so I have experience working in the traditional UIKit framework. Before I talk about the many great things in this book (theres a bunch!) I want to first discuss who its for. I attached an excerpt from the book on who this is for because if you dont know anything about swift it might make the topics within harder to grasp. From what I read so far you dont need mastery of the Swift language, just a basic understanding should get you through it.SwiftUI has a new way of implementing stack views and this book begins by covering that topic. There aren't really storyboards the way you are used to seeing them in xcode which may come as a shock to some users, unless you build your user interface programmatically already. Each chapter will explore new features such as Scroll Views, Container Views, and Alerts.It has a dedicated chapter on Core Data which I found an interesting readThe eBook version has color illustrations, which I appreciate very much and I found the example code presented within the book to be relevant to the topics being covered.If you follow along with the examples in this book you will build a tic-tac-toe game, a cute little progress ring, bar graphs, pie chart, and a lot more.I found swiftUI to be a really unique coding experience and I feel great having this book as a companion.*One last note, you will also need a semi beefy computer to fully utilize the preview mode that shows you what you're coding as you code. Its a great feature but I have seen it bog down my less powerful mac.Overall this book is 5 stars and will set you up right.
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is the delivery time and cost of print book? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela
What is custom duty/charge? Chevron down icon Chevron up icon

Customs duty are charges levied on goods when they cross international borders. It is a tax that is imposed on imported goods. These duties are charged by special authorities and bodies created by local governments and are meant to protect local industries, economies, and businesses.

Do I have to pay customs charges for the print book order? Chevron down icon Chevron up icon

The orders shipped to the countries that are listed under EU27 will not bear custom charges. They are paid by Packt as part of the order.

List of EU27 countries: www.gov.uk/eu-eea:

A custom duty or localized taxes may be applicable on the shipment and would be charged by the recipient country outside of the EU27 which should be paid by the customer and these duties are not included in the shipping charges been charged on the order.

How do I know my custom duty charges? Chevron down icon Chevron up icon

The amount of duty payable varies greatly depending on the imported goods, the country of origin and several other factors like the total invoice amount or dimensions like weight, and other such criteria applicable in your country.

For example:

  • If you live in Mexico, and the declared value of your ordered items is over $ 50, for you to receive a package, you will have to pay additional import tax of 19% which will be $ 9.50 to the courier service.
  • Whereas if you live in Turkey, and the declared value of your ordered items is over € 22, for you to receive a package, you will have to pay additional import tax of 18% which will be € 3.96 to the courier service.
How can I cancel my order? Chevron down icon Chevron up icon

Cancellation Policy for Published Printed Books:

You can cancel any order within 1 hour of placing the order. Simply contact customercare@packt.com with your order details or payment transaction id. If your order has already started the shipment process, we will do our best to stop it. However, if it is already on the way to you then when you receive it, you can contact us at customercare@packt.com using the returns and refund process.

Please understand that Packt Publishing cannot provide refunds or cancel any order except for the cases described in our Return Policy (i.e. Packt Publishing agrees to replace your printed book because it arrives damaged or material defect in book), Packt Publishing will not accept returns.

What is your returns and refunds policy? Chevron down icon Chevron up icon

Return Policy:

We want you to be happy with your purchase from Packtpub.com. We will not hassle you with returning print books to us. If the print book you receive from us is incorrect, damaged, doesn't work or is unacceptably late, please contact Customer Relations Team on customercare@packt.com with the order number and issue details as explained below:

  1. If you ordered (eBook, Video or Print Book) incorrectly or accidentally, please contact Customer Relations Team on customercare@packt.com within one hour of placing the order and we will replace/refund you the item cost.
  2. Sadly, if your eBook or Video file is faulty or a fault occurs during the eBook or Video being made available to you, i.e. during download then you should contact Customer Relations Team within 14 days of purchase on customercare@packt.com who will be able to resolve this issue for you.
  3. You will have a choice of replacement or refund of the problem items.(damaged, defective or incorrect)
  4. Once Customer Care Team confirms that you will be refunded, you should receive the refund within 10 to 12 working days.
  5. If you are only requesting a refund of one book from a multiple order, then we will refund you the appropriate single item.
  6. Where the items were shipped under a free shipping offer, there will be no shipping costs to refund.

On the off chance your printed book arrives damaged, with book material defect, contact our Customer Relation Team on customercare@packt.com within 14 days of receipt of the book with appropriate evidence of damage and we will work with you to secure a replacement copy, if necessary. Please note that each printed book you order from us is individually made by Packt's professional book-printing partner which is on a print-on-demand basis.

What tax is charged? Chevron down icon Chevron up icon

Currently, no tax is charged on the purchase of any print book (subject to change based on the laws and regulations). A localized VAT fee is charged only to our European and UK customers on eBooks, Video and subscriptions that they buy. GST is charged to Indian customers for eBooks and video purchases.

What payment methods can I use? Chevron down icon Chevron up icon

You can pay with the following card types:

  1. Visa Debit
  2. Visa Credit
  3. MasterCard
  4. PayPal
What is the delivery time and cost of print books? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela