Mobile App Development 2022W Lecture 8

From Soma-notes

Video

Video from the lecture given on February 4, 2022 is now available:

Video is also available through Brightspace (Resources->Zoom Meetings (Recordings, etc.)->Cloud Recordings tab). Note that here you'll also see chat messages.

Notes

Lecture 8
---------

In SwiftUI, you *really* don't want to do explicit typing because the types under the hood get crazy complicated
  - I did some forced conversions and got a runtime error with a type that was several lines long
  - use implicit typing wherever you can, everything works better

In lines 62-64 of remotePicViewer, there's a ternary operator checking the "moved" boolean variable
                    .position(moved ? position :
                                CGPoint(x: g.size.width / 2,
				        y: g.size.height / 2))

What this is saying is that if moved is true, return position, otherwise return
the CGPoint

When will moved be true?
 - once the user has dragged the image

When will it be false?
 - when the app starts up and when a new image has been selected

Question: why not just initialize position to be the fixed CGPoint value
  (that is in the center of the GeometryView box)?

Why not just
 - set position to the centered point when position is initialized?
 - and reset position when we change to a new image?
     - well, g is out of scope

What about at the top of GeometryReader?
  - But we can't, we get an error "Type '()' cannot conform to 'View'"

What this error means is we're trying to do normal Swift things where we should be doing SwiftUI things
 - i.e., everything inside a View has to be a View

The body of a View is not a normal function
 - you can't set arbitrary variables, put in loops, etc
 - everything needs to generate a View

The body of a View is written in a domain-specific language, SwiftUI

But that isn't the real reason

The real reason is, how often is the body of a view being evaluated?
 - potentially VERY frequently
 - every time the screen is updated (and with animation that can be very often)

To make View updates efficient (and more importantly, optimizable), the state of a View can't change while it is being calculated
 - so you *can't* change @State variables in the body of a View, because
   that would change the View's state, and so would change how it should be rendered

So if you see the error "Type '()' cannot conform to 'View'", you probably tried to modify a @State variable inside the body of a View.
 - you have to do it in closures/functions that will be evaluated later

When you declare a function, you are really saying "here is code that should be run at a later time"
 - so declaring a function inside of a view makes sure it won't be run
   while the view is being updated

To setup gestures, we call the .gesture method of the view and pass it "some Gesture" (i.e., some struct that will follow the Gesture protocol)

Normally you won't define these manually, you'll instead use pre-defined ones for standard gestures
  - MagnificationGesture
  - DragGesture
  - RotationGesture
  - TapGesture
You can also combine these with SimultaneousGesture

There are also faster ways of declaring these with things like "onTapGesture"
  - then we just have to give the actions rather than create a whole Gesture struct


For gestures, we declare functions that are called at various states during and after the gesture, such as
 - onChanged <-- as the gestures is changing, this is called (potentially many times)
 - onEnded <-- called once the gesture is ended

For things like .onTapGesture, which doesn't really have a "during" phase, we just have a "perform" function that is called when the action happens