Chapters

Hide chapters

SwiftUI by Tutorials

Second Edition · iOS 13 · Swift 5.2 · Xcode 11

Before You Begin

Section 0: 3 chapters
Show chapters Hide chapters

Section II: Building Blocks of SwiftUI

Section 2: 6 chapters
Show chapters Hide chapters

12. Conditional Views
Written by Bill Morefield

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

In the previous chapter, you learned how to use the standard navigation among views in your app. However, sometimes you need to display a view to the user only under certain conditions. This is especially useful for showing important messages that interrupt the user’s current context and need direct feedback or response before continuing.

Presenting a view outside the navigation stack lets the user’s focus remain on the task they initiated. It also provides a way for your app to provide critical information or request essential feedback.

Open the starter project for this chapter; you’ll find the project from the end of the last chapter, where you explored the standard tab- and navigation-based view structures.

In this chapter, you’ll expand the app to use different conditional views in SwiftUI.

Displaying a modal sheet

You’ll start by using a modal sheet to display information in response to a user action. Modal sheets are useful when you want to focus the user’s attention on the current view.

You’ll now change the navigation to show the flight details using a modal sheet.

The modal sheet slides the view up over the current view. Since the sheet no longer needs to sit in the view hierarchy, you don’t need to wrap the rows as NavigationLinks.

Open FlightBoard.swift and change the List to:

List(flightData) { flight in
  FlightRow(flight: flight)
}
.navigationBarTitle(boardName)
.navigationBarItems(trailing:
  Toggle(isOn: $hideCancelled, label: {
    Text("Hide Cancelled")
  })
)

SwiftUI provides two ways to display a modal based on a @State variable. The first method uses a Bool variable that you set to true when the sheet should display. The second method uses an optional state variable that displays when the variable becomes non-nil. You’ll use the Bool method for this modal.

All modals provide these two options; you’ll see an example using an optional variable later in this chapter.

Open FlightRow.swift and add the following new variable after flight:

@State private var isPresented = false

This line defines a @State variable that indicates when to show the modal sheet. Now change the view to:

Button(action: {
  // 1
  self.isPresented.toggle()
}) {
  HStack {
    Text("\(flight.airline) \(flight.number)")
      .frame(width: 120, alignment: .leading)
    Text(flight.otherAirport)
      .frame(alignment: .leading)
    Spacer()
    Text(flight.flightStatus)
      .frame(alignment: .trailing)
    
  }
  // 2
  .sheet(isPresented: $isPresented, onDismiss: {
    // 3
    print("Modal dismissed. State now: \(self.isPresented)")
  }) {
    // 4
    FlightBoardInformation(flight: self.flight)
  }
}

Here’s the new wrapping for the modal sheet:

  1. Here, you wrap the HStack for the row inside a button. The button’s action toggles the isPresented state variable.
  2. To tell SwiftUI that you want to display a modal, you call sheet(isPresented:onDismiss:content:). Here, you pass the isPresented state variable telling SwiftUI to show the modal when the variable becomes true. When the user dismisses the modal, SwiftUI sets the state back to false.
  3. The optional onDismiss: includes a closure you can use to execute code after the user dismisses the modal. Here, you print a message to the console and show that the value of the state variable is now false.
  4. You provide the view to show on the modal sheet as the closure for sheet(isPresented:onDismiss:content:).

Build and run, navigate to Arrivals and tap any row to see the modal appear. Swipe down on the modal to dismiss it. In the debug console you’ll see the state variable become false after you dismiss the modal:

Programmatically dismissing a modal

You probably noticed that the navigation view disappeared. That’s because a modal sheet takes over the whole screen and no longer wraps the view in any existing navigation view. You can create a new navigation view on the modal, but doing so creates a whole new navigation view stack.

@Binding var showModal: Bool
Button("Done", action: {
    self.showModal = false
})
FlightBoardInformation(flight: FlightInformation.generateFlight(0),
                       showModal: .constant(true))
FlightBoardInformation(flight: self.flight,
                       showModal: self.$isPresented)

Creating an alert

Alerts bring something important to the user’s attention, such as a warning about a problem or a request to confirm an action that could have severe consequences.

@State private var rebookAlert: Bool = false
// 1
if flight.isRebookAvailable() {
  // 2
  Button("Rebook Flight", action: {
    self.rebookAlert = true
  })
  // 3
  .alert(isPresented: $rebookAlert) {
    // 4
    Alert(title: Text("Contact Your Airline"),
          message: Text("We cannot rebook this flight." +
            "Please contact the airline to reschedule this flight."))
  }
}

Adding an action sheet

An action sheet should appear in response to a user action, and the user should expect it to appear. For example, you might want to use an action sheet to confirm an action or to let the user select from multiple options.

struct CheckInInfo: Identifiable {
  let id = UUID()
  let airline: String
  let flight: String
}
@State private var checkInFlight: CheckInInfo?
// 1
if flight.isCheckInAvailable() {
  Button("Check In for Flight", action: {
    // 2
    self.checkInFlight =
      CheckInInfo(airline: self.flight.airline, flight: self.flight.number)
    }
  )
  // 3
  .actionSheet(item: $checkInFlight) { flight in
    // 4
    ActionSheet(
      title: Text("Check In"),
      message: Text("Check in for \(flight.airline)" + 
        "Flight \(flight.flight)"),
      // 5
      buttons: [
        // 6
        .cancel(Text("Not Now")),
        // 7
        .destructive(Text("Reschedule"), action: {
          print("Reschedule flight.")
        }),
        // 8
        .default(Text("Check In"), action: {
          print("Check-in for \(flight.airline) \(flight.flight).")
        })
      ]
    )
  }
}

Showing a popover

Like the action sheet, you usually display a popover in response to a user action. Popovers work best on larger-screen devices, such as iPads and Macs. On devices with smaller screens, a full-screen view, such as a modal sheet, better serves your needs. If the screen is too small, SwiftUI renders the popover as a modal sheet instead.

@State private var showFlightHistory = false
Button("On-Time History") {
  self.showFlightHistory.toggle()
}
.popover(isPresented: $showFlightHistory, arrowEdge: .top) {
  FlightTimeHistory(flight: self.flight)
}

Key points

  • Modal sheets display on top of the view. You can use either a Bool state variable or an optional state variable that implements the Identifiable protocol to tell SwiftUI to display them.
  • The alert, action sheet and popover views provide a standard way to display information to the user and collect feedback from them.
  • Alerts generally display information about unexpected situations or confirm actions that have severe consequences.
  • Action sheets and popovers display in response to a user action. You use action sheets for smaller screen devices and popovers on larger screens.

Where to go from here?

As mentioned in the previous chapter, the first stop for information on user interfaces on Apple platforms should be the Human Interface Guidelines on Modality for the appropriate SwiftUI operating systems:

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now