Building Engaging User Interfaces with SwiftUI

Mar 12 2025 · Swift 5.9, iOS 17.0, XCode 15.0

Lesson 02: Implementing Complex UI Layouts

Showing Flight Progress

Episode complete

Play next episode

Next

Heads up... You’re accessing parts of this content for free, with some sections shown as obfuscated text.

Heads up... You’re accessing parts of this content for free, with some sections shown as obfuscated text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Next, you’ll add an indicator of a flight’s progress to the card. The status of a flight will usually be either before departure or after landing. In between, there’s a time when the flight will be partway between airports.

func minutesBetween(_ start: Date, and end: Date) -> Int {
  // 1
  let diff = Calendar.current.dateComponents(
    [.minute], from: start, to: end
  )
  // 2
  guard let minute = diff.minute else {
    return 0
  }
  // 3
  return abs(minute)
}
func flightTimeFraction(flight: FlightInformation) -> Double {
  // 1
  let now = Date()
  // 2
  if flight.direction == .departure {
    // 3
    if flight.localTime > now {
      return 0.0
    // 4
    } else if flight.otherEndTime < now {
      return 1.0
    } else {
      // 5
      let timeInFlight = minutesBetween(
        flight.localTime, and: now
      )
      // 6
      let fraction =
        Double(timeInFlight) / Double(flight.flightTime)
      return fraction
    }
  } else {
    if flight.otherEndTime > now {
      return 0.0
    } else if flight.localTime < now {
      return 1.0
    } else {
      let timeInFlight = minutesBetween(
        flight.otherEndTime, and: now
      )
      let fraction =
        Double(timeInFlight) / Double(flight.flightTime)
      return fraction
    }
  }
}

Adding Inline Drawings

Now, you’ll add a view to show the flight’s progress. Create a new SwiftUI view named FlightProgressView inside the Timeline group. Change the view to:

struct FlightProgressView: View {
  var flight: FlightInformation
  var progress: CGFloat

  var body: some View {
    // 1
    GeometryReader { proxy in
      Image(systemName: "airplane")
        .resizable()
        // 2
        .offset(x: proxy.size.width * progress)
        .frame(width: 25, height: 25)
        .foregroundColor(flight.statusColor)
      // 3
    }.padding([.trailing], 20)
  }
}

#Preview {
  FlightProgressView(
    flight: FlightData.generateTestFlight(date: Date()),
    progress: 0.67
  )
}
FlightProgressView(
  flight: flight,
  progress: flightTimeFraction(
    flight: flight
  )
)
.padding()
.background(
  Color.gray.opacity(0.3)
)
.clipShape(
  RoundedRectangle(cornerRadius: 20)
)
.overlay(
  RoundedRectangle(cornerRadius: 20)
    .stroke()
)
See forum comments
Cinema mode Download course materials from Github
Previous: Building Reusable Views Next: Using a View Builder