Skip to content
master
Go to file
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
 
 
 
 
 
 

README.md

alt text

Project 0: Landmarks

  • Edit the VStack initializer to align the views by their leading edges.
VStack(alignment: .leading) { ... }
  • 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.

  • Create a custom image view.

struct CircleImage: View {
    var body: some View {
        Image("turtlerock")
            .clipShape(Circle())
            .overlay(
                Circle().stroke(Color.white, lineWidth: 4))
            .shadow(radius: 10)
    }
}
  • To use UIView subclasses from within SwiftUI, you wrap the other view in a SwiftUI view that conforms to the UIViewRepresentable protocol.
import SwiftUI
import MapKit

struct MapView: UIViewRepresentable {
    func makeUIView(context: Context) -> MKMapView {
        MKMapView(frame: .zero)
    }

    func updateUIView(_ view: MKMapView, context: Context) {
        let coordinate = CLLocationCoordinate2D(
            latitude: 34.011286, longitude: -116.166868)
        let span = MKCoordinateSpan(latitudeDelta: 2.0, longitudeDelta: 2.0)
        let region = MKCoordinateRegion(center: coordinate, span: span)
        view.setRegion(region, animated: true)
    }
}
  • To layer the image view on top of the map view, give the image an offset of -130 points vertically, and padding of -130 points from the bottom of the view.
CircleImage()
    .offset(y: -130)
    .padding(.bottom, -130)
  • To extend to the top edge of the screen, add the edgesIgnoringSafeArea(.top) modifier to the map view.

Project 1: ListedPhotosViewer

  • Explore the Combine Framework:
  class DataSource: BindableObject {
    // PassthroughSubject< send nothing and never throw errors >
    var didChange = PassthroughSubject<Void, Never>()
    var photos = [String]()
    
    init() {
      ...
      didChange.send(())
    }
  }
  • Learn about the BindableObject protocol
  • Notify about changes using didChange.send(())
  • Understand the difference between @State and @ObjectBinding
  • Use someArray.identified(by: .self) instead of conforming to the Identifiable protocol

Project 2: FlagGuessing

  • Declare all @State variables as private when possible (recommended by Apple)
  • Let alerts appear based on a boolean @State variable (declarative way):
.presentation($showingAlert) { 
  // Present alert
  
  // SwiftUI will automatically toggle 'showingAlert' variable.
}
  • Creation of an Alert and attaching a custom action to the dismiss button:
Alert(title: Text(alertTitle), message: Text(score), dismissButton: .default(Text("Continue")) {
  self.nextQuestion()
}) 

Project 3: iBeaconDetector

  • How to let a BindableObject conform to a delegate
class BeaconDetector: NSObject, BindableObject, CLLocationManagerDelegate {
    var didChange = PassthroughSubject<Void, Never>()
    ...
    override init() { // overrides NSObject init
        super.init()
        locationManager = CLLocationManager()
        locationManager?.delegate = self
        locationManager?.requestWhenInUseAuthorization()
    }
    ...
    func locationManager(_ manager: CLLocationManager, didRange beacons: [CLBeacon], 
                            satisfying beaconConstraint: CLBeaconIdentityConstraint) {
       ...
    }
    ...
}
  • Modifier Sequence matters (everytime a modifier is added, a new view is created.)
  • To ignore all safe area:
  .edgesIgnoringSafeArea(.all)
  • Creating a custom modifier:
struct BigText: ViewModifier {
    func body(content: Content) -> some View {
        content
            .font(Font.system(size: 72, design: .rounded))
            .frame(minWidth:0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
    }
}
Text("RIGHT HERE")
        .modifier(BigText())
  • Complex logic inside a View may require the 'return' keyword e.g.:
struct ContentView : View {
    @ObjectBinding var detector = BeaconDetector()
    var body: some View {
        if detector.lastDistance == .immediate {
            return Text("RIGHT HERE")
                .modifier(BigText())
                .background(Color.red)
                .edgesIgnoringSafeArea(.all)
        } else if detector.lastDistance == .near {
            return Text("NEAR")
                .modifier(BigText())
                .background(Color.orange)
                .edgesIgnoringSafeArea(.all)
    ...
}

Project 4: BetterRest

  • Setting up a DatePicker:
DatePicker($wakeUp, displayedComponents: .hourAndMinute)
  • Setting up a Stepper:
Stepper(value: $sleepAmount, in: 4...12, step: 0.25) {
    Text("\(sleepAmount, specifier: "%g") hours")
}
  • Rounding up "8.0000.." -> 8 and "8.2500000" -> 8.25:
Text("\(16.0/2.0, specifier: "%g"))
  • Create a trailing navigation bar button:
.navigationBarItems(trailing:
    Button(action: calculateBedtime) {
        Text("Calculate")
    }
)
  • Presenting an alert:
.presentation($showingAlert) {
    Alert(title: Text(alertTitle), message: Text(alertMessage), dismissButton: .default(Text("OK")))
  • Change only hours and minutes from current DateTime():
    static var defaultWakeTime: Date {
        var components = DateComponents()
        components.hour = 8
        components.minute = 0
        return Calendar.current.date(from: components) ?? Date()
    }
  • Use CoreML Model to predict:
func calculateBedtime() {
    let model = SleepCalculator()

    do {
        let components = Calendar.current.dateComponents([.hour, .minute], from: wakeUp)
        let hour = (components.hour ?? 0) * 60 * 60
        let minute = (components.minute ?? 0) * 60

        let prediction = try model.prediction(coffee: Double(coffeeAmount), estimatedSleep: Double(sleepAmount), wake: Double(hour + minute))

        let formatter = DateFormatter()
        formatter.timeStyle = .short

        let sleepTime = wakeUp - prediction.actualSleep
        alertMessage = formatter.string(from: sleepTime)
        alertTitle = "Your ideal bedtime is..."
    } catch {
        alertTitle = "Error"
        alertMessage = "Sorry, there was a problem calculating your bedtime."
    }

    showingAlert = true

}

Project 5: WordScramble

  • Call a function onAppear
VStack {
  ...
}
.onAppear {
    self.startGame()
}
  • Add a 'on commit' closure (on return key pressed) to a textfield and hide the keyboard.
  TextField($newWord) {
      // On commit closure
      self.addNewWord()
      UIApplication.shared.keyWindow?.endEditing(true)
  }
  • Add textfield styles
  TextField($newWord) {
      ...
  }
  .textFieldStyle(.roundedBorder)
  .padding()

Project 6: CupcakeCorner

  • Make a Bindable Object 'Codable' by ignoring the PassthroughSubject
class Order: BindableObject, Codable {
    enum CodingKeys: String, CodingKey {
        case type, quantity, extraFrosting, addSprinkles, name, streetAddress, city, zip
    }
    ...
}
  • Call 'didChange.send(())' using a 'didSet'
    ..
    var type = 0 { didSet { update() } }
    var quantity = 3 { didSet{ update() } }
    
    func update() {
        didChange.send(())
    }

About

Learning about Apple's recently (WWDC 19) announced declarative UI framework, SwiftUI.

Topics

Resources

Releases

No releases published

Packages

No packages published

Languages

You can’t perform that action at this time.