/SwiftUI

Primary LanguageSwift

SwiftUI

Creating and Combining Views

Create a New Project and Explore the Canvas

  • @main: app's entry point

  • By default value, two structures are declared in Swift UIView

    • First Strcture: conform to the View protocol and describe view content & layout
    • Second Structure: declares a preview for that view

Customize the Text View

  • Edit code to change padding and text color
    Text("Turtle Rock")
      .font(.title)
      .foregroundColor(.green)
  • How to open SwiftUI Inspector: command + click

Combine Views Using Stacks

  • The reason of view returning only a single view, we can combine and embed multiple views in stacks which group views together horizontally, vertically, or back-to-front

  • Use of Vertical Stack: Embed in VStack

    By default, stacks center their contents along their axis and provide context-appropriate spacing, so we can edit the VStack to align views by their leading edges

      VStack(alignment: .leading){
        Text("Turtle Rock")
          .font(.title)
      }
  • Use of Horizontal Stack: Embed in HStack

    Hstack {
       Text("Joshua Tree National Park")
        .font(.subheadline)
       Text("California")
        .font(.subheadline)
    }
  • Full width of the device, separate the elements -> Using 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.

  • To give a little more space, we can use .padding at the end of stack

Create a Custom Image View

  • To place images on the device, we can use Image(_: modifier
  • To clip shape cirle, we can call to clipShape(Circle())
  • Create a circle with a gray stroke, and then add it as an overlay to give image border.
    Image("turtlerock")
      .clipShape(Circle())
      .overlay(Circle().stroke(Color.gray, lineWidth: 4))

Use SwiftUI Views From other Frameworks

  • Add an import statement for Mapkit

    import MapKit
  • Create a private state variable that holds the regin information for the map

    You use @State attribute to establish a source of truth for data in your app that you can modify from more than on view

    @State private var region = MKCoordinateRegion(
      center: CLLocationCoordinate2D(latitude: 34.011_286, longitude: -116.166_868),
      span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2)
    )
  • Takes a bining to the region

    var body: some View {
      Map(coordinateRegion: $region)
    }

Compse the Detail View

  • Add custom MapView to the top of the stack.

    Set the size of the MapView with frame(width: height:)

    Add the CircleImage view to the stack

    var body: some View {
      VStack {
        MapView()
          .ignoreSafeArea(edges: .top)
          .frame(height: 300)
        CircleImage()
          .offset(y: -130)
          .padding(.bottom, 130)
      }
    }

Building Lists and Navigation

Create a Landmark Model

  • Define a Landmark structure

    import Foundation
    import SwiftUI
    import CoreLocation
    
    struct Landmark: Hashable, Codable {
      var id: Int
      var name: String
      var park: String
      var state: String
      var description: String
      
      private var imageName: String
      var image: Image {
        Image(imageName)
      }
      
      private var coordinates: Coordinates
      var locationCoordinate: CLLocationCoordinate2D {
        CLLocationCoordinate2D(
          latitude: coordinates.latitude,
          longitude: coordinates.longitude
        )
      }
      struct Coordinate: Hashable, Codable {
        var latitude: Double
        var longitude: Double
       }
    }
  • Load data

    import Foundation
    
    var landmarks: [Landmark] = load("landmarkData.json")
    
    func load<T: Decodable>(_ filename: String) -> T {
        let data: Data
    
        guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
        else {
            fatalError("Couldn't find \(filename) in main bundle.")
        }
    
        do {
            data = try Data(contentsOf: file)
        } catch {
            fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
        }
    
        do {
            let decoder = JSONDecoder()
            return try decoder.decode(T.self, from: data)
        } catch {
            fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
        }
    }

Create the Row View

  • Add landmark as a stored property of LandmarkRow
    import SwiftUI
    
    struct LandmarkRow: View {
      var landmark: Landmark
      
      var body: some View {
        HStack {
        landmark.image
          .resizable()
          .frame(width: 50, height: 50)
          Text(landmark.name)
          
          Spacer()
        }
      }
    }
    
    struct LandmarkRow_Previews: PreviewProvider {
      static var previews: some View {
        LandmarkRow(landmark: landmarks[0])
      }
    }

Customize the Row Preview

  • Wrap the returned row in a Group, and add the first row back again
    import SwiftUI
    
    struct LandmarkRow: View {
      var landmark: Landmark
      
      var body: some View {
        HStack {
        landmark.image
          .resizable()
          .frame(width: 50, height: 50)
          Text(landmark.name)
          
          Spacer()
        }
      }
    }
    
    struct LandmarkRow_Previews: PreviewProvider {
      static var previews: some View {
      Group {
          LandmarkRow(landmark: landmarks[0])
          LandmarkRow(landmark: landmarks[1]) 
        }.previewLayout(.fixed(width: 300, height: 70))
      }
    }

Create the List of Landmarks

  • Create static views
    struct LandmarkList: View {
      var body: some View {
        List {
          LandmarkRow(landmark: landmark[0])
          LandmarkRow(landmark: landmark[1])
        }
      }
    }

Make the List Dynamic

  • We can generate rows directly from a collection

    struct LandmarkList: View {
      var body: some View {
        List(landmarks){
          Landmark in LandmarkRow(landmark: landmark)
        }
      }
    }
    // Landmark.swift
    struct Landmark: Hashable, Codable, Identifiable {