CS193p Notes - Lecture 3: Reactive UI + Protocols + Layout

lankinen profile image Lankinen ・3 min read
  • ViewModel is never calling View but View calls ViewModel
    • One ViewModel can be used by many Views and that is why it's a class
  • Holding option and clicking a variable name allows to do all kind of things like rename.
  • mutating keyword is used front of func when it's changing self
  • in struct EmojiMemoryGameView: View { View is called protocol (constrains and gains). The idea is that we gain something (functions) from using it but it also contrains us to do something like have var body: some View


  • ObservableObject is constraint and gain that gives a class (ViewModel) ability to follow changes. Every time something changes it updates the UI.
    • objectWillChange.send() is can be added to functions where some object will change.
    • It's more common to add @Published property wrapper front of the variables that will change because that way you don't forget to add objectWillChange.send() to some position.
      • Behind the scenes it checks that every time that variable is changes it calls objectWillChange.send()
  • @ObservedObject is added front of ModelView in View class to redraw it every time the ModelView changes.
  • protocol is like stuct/class that can contain vars and functions but it doesn't had implementation to them.
    • Classes and structs can use protocol following way class Car: Vehicle It's possible to implement multiple protocols by separating them with commas.
    • Protocols can also inherit other protocols protocol Vehicle: Moveable ...
    • protocol offers constrains and gains
    • The idea in protocols is that multiple structs/classes can use it and then if it's extended all of them get the new feature
  • extension is used to add new functionalities to structs, classes, protocols
  • Protocols are useful because they say what the types (structs/classes/other protocols) are capable of and also demand certain behavior.
protocol Greatness {
    func isGreaterThan(other: Self) -> Bool

extension Array where Element: Greatness {
    var greatest: Element {
        // for-loop through all the elements
        // which (inside this extension) we know each implementes the Greatness protocol
        // and figure out which one is greatest by calling isGreaterThan(other:) on them
        return the greatest by calling isGreaterThan on each Element
  • Self means that type comes from the thing that is implementing that protocol.

Layout (49:57)

  • HStack and VStack
    • Offers space to its "least flexible" subviews first
      • Image: "inflexible"
      • Text: "slightly more flexible"
      • RoudedRectangle: "flexible"
    • Defaults work a little bit differently in different devices and that is why we should keep them and not put specific numbers because those probably doesn't work as many device
    • Spacer takes the space given and draws nothing
    • Divider Draws line (horizontal or vertical depending the stack)
HStack {
    Image(systemName: "arrow.up") // default layout priority is 0
  • Important text takes the space it wants
  • Then image takes the space it wants
  • Finally Unimportant text takes the remaining space

Demo (1:12:04)

struct CardView: View {
  GeometryReader { geometry in 
    ZStack {
      if self.card.isFaceUp {
        RoundedRectangle(cornerRadius: 10.0).fill(Color.white)
        RoundedRectangle(cornerRadius: 10.0).stroke(lineWidth: 3)
      } else {
        RoundedRectangle(cornerRadius: 10.0).fill()
    .font(Font.system(size: min(geometry.size.width, geometry.size.height) * 0.75))

It's common to move the numbers to a "control panel". In one place it's easy to modify them and the main code becomes cleaner.


Originally published here

Posted on by:


Editor guide