Mobile App Dev 2022W: Assignment 2: Difference between revisions
No edit summary |
No edit summary |
||
Line 1: | Line 1: | ||
'''This assignment is still being developed.''' | '''This assignment is still being developed.''' | ||
Please answer all of the following questions in the [https://homeostasis.scs.carleton.ca/~soma/mad-2022w/templates/comp1601- | Please answer all of the following questions in the [https://homeostasis.scs.carleton.ca/~soma/mad-2022w/templates/comp1601-assign2-template.txt supplied template]. Your uploaded file should be named "comp1601-assign2-<i>MCOname</i>.txt" where MCOname is your MyCarletonOne username (i.e., the username you use to login to Brightspace) and it should be a UNIX text file (LF line endings). | ||
<b>You may use [https://homeostasis.scs.carleton.ca/~soma/mad-2022w/validators/a2-validator.html this validator page] to make sure your answer file is properly named and formatted.</b> | <b>You may use [https://homeostasis.scs.carleton.ca/~soma/mad-2022w/validators/a2-validator.html this validator page] to make sure your answer file is properly named and formatted.</b> |
Revision as of 03:52, 11 February 2022
This assignment is still being developed.
Please answer all of the following questions in the supplied template. Your uploaded file should be named "comp1601-assign2-MCOname.txt" where MCOname is your MyCarletonOne username (i.e., the username you use to login to Brightspace) and it should be a UNIX text file (LF line endings).
You may use this validator page to make sure your answer file is properly named and formatted.
The questions are based on remotePicViewer2, which is an enhanced version of remotePicViewer from Tutorial 4. There are 11 questions worth 20 points total.
Do not turn in modified applications. Instead, when answering code writing questions, include code snippets and explain how they should be used to modify the application. Use complete sentences when answering. Be sure to list collaborators and outside sources that you used.
Submit your answers via Brightspace by February 18, 2021 by 11:59 PM.
Questions
When explaining what code does, be sure to explain both the mechanics of what the code does and how that code fits into the overall functionality of the program.
- [1] Explain how to add this image from Wikipedia to the image list under the name "Boxer".
- [2] What name is added to the menu if we manually enter a URL and select "Remember Image"? Why? Explain with an example.
- [2] There is an image URL on line 67. When is this image displayed during normal program operation? Why?
- [2] If you delete line 28 and instead replace line 20 with "var imageNames = [String] (images.keys)", the program will compile and run. Will it function any differently? Why?
- [2] Why does the program have both currentAmount and finalAmount for implementing the magnification gesture? Specifically, how does the program behave if we use finalAmount in the onChanged handler and do nothing in the onEnded handler of magnifying?
- [2] What do each line do in lines 50-53? These implement the action function of the "Remember Image" menu option.
- [2] The dragging gesture handler (lines 127-137) has both an onChanged and onEnded handler that have exactly the same code in them. What does their code do? How does the program function with just one or the other?
- [1] How does the program's behavior change if we replace line 90 with ".onTapGesture(count: 3, perform: tapReset)"?
- [2] When typing in a new URL, when is AsyncImage updated? Does it try loading the URL as you type, or does it wait until the URL is fully entered? Explain briefly.
- [2] How could you change the program so that the double click zoom command only zooms in three times and on the fourth it goes back to a magnification of 1 (no zoom)? Your solution shouldn't add any new variables.
- [2] How does the program's behavior change if you move lines 86 and 89 to just before 83? Why?
Code: remotePicViewer2 ContentView.swift
// remotePicViewer2
//
// code for Assignment 2
// COMP 1601 2022W, Carleton University
//
// Anil Somayaji, February 10, 2022
// Code is licenced under the FSF GPLv3 or newer
//
// Note this code is for teaching purposes only. In particular
// it is not robust and does not handle error conditions properly.
//
import SwiftUI
var images: [String: String] = [
"Kittens": "https://homeostasis.scs.carleton.ca/~soma/mad-2022w/images/kittens.jpeg",
"Sad Dog":
"https://homeostasis.scs.carleton.ca/~soma/mad-2022w/images/roshi.jpeg",
]
@available(macOS 12.0, *)
struct ContentView: View {
@State private var imageName = "Kittens"
@State private var theImage = getImage("Kittens")
@State private var moved = false
@State private var finalAmount: CGFloat = 1
@State private var angle = Angle(degrees: 0.0)
@State private var imageNames = [String] (images.keys)
func resetState() {
moved = false
finalAmount = 1
angle = Angle(degrees: 0.0)
}
var body: some View {
VStack {
Text("Image Viewer")
.font(.title)
TextField("Enter an image URL", text: $theImage)
Menu("Known Images") {
ForEach(imageNames, id: \.self) {s in
Button(s, action: {
theImage = getImage(s)
resetState()
})
}
Divider()
Button("Remember Image", action: {
let urlParts = theImage.split(separator: "/")
let newName = String(urlParts[urlParts.count - 1])
imageNames.append(newName)
images[newName] = theImage
})
}
Divider().background(Color.black)
ActiveImage(theImage: $theImage, imageName: $imageName, moved: $moved, finalAmount: $finalAmount, angle: $angle)
}
}
}
func getImage(_ imageName: String) -> String {
return images[imageName] ?? "https://homeostasis.scs.carleton.ca/~soma/year.jpg"
}
@available(macOS 12.0, *)
struct ActiveImage: View {
@State private var position = CGPoint(x: 0, y: 0)
@State private var currentAmount: CGFloat = 0
@Binding var theImage: String
@Binding var imageName: String
@Binding var moved: Bool
@Binding var finalAmount: CGFloat
@Binding var angle: Angle
var body: some View {
GeometryReader {g in
AsyncImage(url: URL(string: theImage), content: {image in
image
.resizable()
.scaledToFit()
.position(moved ? position :
CGPoint(x: g.size.width / 2,
y: g.size.height / 2))
.scaleEffect(finalAmount + currentAmount)
.gesture(dragging)
.gesture(magnifying)
.rotationEffect(self.angle)
.onLongPressGesture(perform: tapReset)
.onTapGesture(count: 2, perform: zoom)
.onTapGesture(count: 1, perform: rotateImage)
}, placeholder: {
ProgressView()
.position(moved ? position :
CGPoint(x: g.size.width / 2,
y: g.size.height / 2))
})
}
}
func zoom() {
self.finalAmount = self.finalAmount * 2
}
func tapReset() {
self.finalAmount = 1
self.moved = false
self.angle = Angle(degrees: 0.0)
}
func rotateImage() {
let oldDegrees = self.angle.degrees
self.angle = Angle(degrees: oldDegrees + 45)
}
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()
}
}