Mobile App Dev 2021W: Tutorial 4: Difference between revisions
No edit summary |
|||
(2 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
In this tutorial you will be playing with [https://homeostasis.scs.carleton.ca/~soma/mad-2021w/code/ | In this tutorial you will be playing with [https://homeostasis.scs.carleton.ca/~soma/mad-2021w/code/picviewer-1.zip picviewer-1], a simple image viewer in Swift. Note that this code has two embedded images in it, so it is a bit bigger than past ones. | ||
Submit your answers by 11:59 PM on Sunday, February 21, 2021 via cuLearn. Please submit a text file following [https://homeostasis.scs.carleton.ca/~soma/mad-2021w/templates/tut4-template.txt this template]. Note that you only need to cite sources that weren't covered in lecture and aren't listed below. | Submit your answers by 11:59 PM on Sunday, February 21, 2021 via cuLearn. Please submit a text file following [https://homeostasis.scs.carleton.ca/~soma/mad-2021w/templates/tut4-template.txt this template]. Note that you only need to cite sources that weren't covered in lecture and aren't listed below. | ||
Line 28: | Line 28: | ||
==Code== | ==Code== | ||
[https://homeostasis.scs.carleton.ca/~soma/mad-2021w/code/ | [https://homeostasis.scs.carleton.ca/~soma/mad-2021w/code/picviewer-1.zip picviewer-1.zip] | ||
<syntaxhighlight lang="swift" line> | <syntaxhighlight lang="swift" line> | ||
Line 119: | Line 119: | ||
self.position = s.location | self.position = s.location | ||
} | } | ||
} | |||
} | |||
struct ContentView_Previews: PreviewProvider { | |||
static var previews: some View { | |||
ContentView() | |||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==Solution== | |||
[https://homeostasis.scs.carleton.ca/~soma/mad-2021w/solutions/tut4-solutions.txt Tutorial 4 Solutions] |
Latest revision as of 16:27, 22 February 2021
In this tutorial you will be playing with picviewer-1, a simple image viewer in Swift. Note that this code has two embedded images in it, so it is a bit bigger than past ones.
Submit your answers by 11:59 PM on Sunday, February 21, 2021 via cuLearn. Please submit a text file following this template. Note that you only need to cite sources that weren't covered in lecture and aren't listed below.
Key Concepts & APIs
- Assets & image sets
- Image
- Fitting images into available space
- scaledToFit
- GeometryReader
- rotationEffect
- scaleEffect
- RotationGesture
- MagnificationGesture
- SimultaneousGestures
Questions & Tasks
- Build and run the program. Select different images from the menu. Drag, rotate, scale, and tap on the images. What happens? (Note that you can do pinch and rotate operations in the simulator by holding down the option key and dragging the circles that appear.)
- Add an image to the app's assets. Add that image to the menu so it can be selected like the others. What did you have to do?
- If you remove the VStack on line 16, does that change the program's behavior? Why or why not?
- Why are so many state variables declared in ContentView but used in ActiveImage? Why not keep them local to ActiveImage? Explain for each parameter.
- Why is the moved variable needed? Why not always just set the position to the position variable?
- What's the purpose of resetState()?
- Change the program so that when you single tap it switches between rotating or magnifying modes (rather than doing both at once). Make double tap reset the image. Hint: Don't try to add and remove handlers when switching between modes, that won't work. Instead, change how the magnifying and rotating handlers work.
Code
import SwiftUI
struct ContentView: View {
@State private var theImage = "kittens"
@State private var moved = false
@State private var finalAmount: CGFloat = 1
@State private var angle = Angle(degrees: 0.0)
func resetState() {
moved = false
finalAmount = 1
angle = Angle(degrees: 0.0)
}
var body: some View {
VStack {
Menu("Animals!") {
Button("Kittens", action: {
theImage = "kittens"
resetState()
})
Button("Sad Dog", action: {
theImage = "sadDog"
resetState()
})
}
ActiveImage(theImage: $theImage, moved: $moved, finalAmount: $finalAmount, angle: $angle)
}
}
}
struct ActiveImage: View {
@State private var position = CGPoint(x: 0, y: 0)
@State private var currentAmount: CGFloat = 0
@Binding var theImage: String
@Binding var moved: Bool
@Binding var finalAmount: CGFloat
@Binding var angle: Angle
var body: some View {
GeometryReader {g in
Image(theImage)
.resizable()
.scaledToFit()
.position(moved ? position :
CGPoint(x: g.size.width / 2, y: g.size.height / 2))
.scaleEffect(finalAmount + currentAmount)
.gesture(dragging)
.gesture(SimultaneousGesture(magnifying, rotating))
.rotationEffect(self.angle)
.onTapGesture(count: 1, perform: tapReset)
}
}
func tapReset() {
self.finalAmount = 1
self.moved = false
self.angle = Angle(degrees: 0.0)
}
var rotating: some Gesture {
RotationGesture()
.onChanged { angle in
self.angle = angle
}
}
var magnifying: some Gesture {
MagnificationGesture().onChanged { amount in
self.currentAmount = amount - 1
}
.onEnded { amount in
self.finalAmount += self.currentAmount
self.currentAmount = 0
}
}
var dragging: some Gesture {
DragGesture()
.onChanged {s in
self.moved = true
self.position = s.location
}
.onEnded {s in
self.moved = true
self.position = s.location
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}