Difference between revisions of "Mobile App Dev 2021W: Assignment 2"

From Soma-notes
Jump to navigation Jump to search
Line 38: Line 38:
     @State private var magnification: CGFloat = 1
     @State private var magnification: CGFloat = 1
     @State private var angleS = "0"
     @State private var angleS = "0"
 
   
     func resetState() {
     func resetState() {
         moved = false
         moved = false
Line 44: Line 44:
         angleS = "0"
         angleS = "0"
     }
     }
       
   
     var body: some View {
     var body: some View {
         VStack {
         VStack {
             Menu("Animals!") {
             Menu("Animals!") {
                    Button("Kittens", action: {
                Button("Kittens", action: {
                        theImage = "kittens"
                    theImage = "kittens"
                        resetState()
                    resetState()
                    })
                })
                    Button("Sad Dog", action: {
                Button("Sad Dog", action: {
                        theImage = "sadDog"
                    theImage = "sadDog"
                        resetState()
                    resetState()
                    })
                })
                }
            }
             ActiveImage(theImage: $theImage, moved: $moved, finalAmount: $magnification,
             ActiveImage(theImage: $theImage, moved: $moved, finalAmount: $magnification,
                         angleS: $angleS)
                         angleS: $angleS)
Line 74: Line 74:
     @State private var position = CGPoint(x: 0, y: 0)
     @State private var position = CGPoint(x: 0, y: 0)
     @State private var currentAmount: CGFloat = 0
     @State private var currentAmount: CGFloat = 0
 
   
     @Binding var theImage: String
     @Binding var theImage: String
     @Binding var moved: Bool
     @Binding var moved: Bool
Line 83: Line 83:
         GeometryReader {g in
         GeometryReader {g in
             Image(theImage)
             Image(theImage)
                    .resizable()
                .resizable()
                    .scaledToFit()
                .scaledToFit()
                    .position(moved ? position :
                .position(moved ? position :
                             CGPoint(x: g.size.width / 2, y: g.size.height / 2))
                             CGPoint(x: g.size.width / 2, y: g.size.height / 2))
                    .scaleEffect(finalAmount + currentAmount)
                .scaleEffect(finalAmount + currentAmount)
                    .rotationEffect(currentAngle())
                .rotationEffect(currentAngle())
                    .gesture(dragging)
                .gesture(dragging)
                    .gesture(SimultaneousGesture(rotating, magnifying))
                .gesture(SimultaneousGesture(rotating, magnifying))
                    .onTapGesture(count: 1, perform: tapReset)
                .onTapGesture(count: 1, perform: tapReset)
         }
         }
     }
     }
 
   
     func currentAngle() -> Angle {
     func currentAngle() -> Angle {
         if let a = Double(self.angleS) {
         if let a = Double(self.angleS) {
Line 108: Line 108:
         self.angleS = "0"
         self.angleS = "0"
     }
     }
       
   
     var rotating: some Gesture {
     var rotating: some Gesture {
         RotationGesture()
         RotationGesture()
Line 115: Line 115:
             }
             }
     }
     }
 
   
     var magnifying: some Gesture {
     var magnifying: some Gesture {
         MagnificationGesture().onChanged { amount in
         MagnificationGesture().onChanged { amount in

Revision as of 14:26, 15 February 2021

This assignment is still being developed.

Questions

  1. [3] At the bottom of the screen it shows the current rotation of the image in degrees. This rotation information stored in a different format as it was in Tutorial 4's 1601picviewer-1.
    1. [1] What is the difference?
    2. [2] Change the program to store the rotation information the same way 1601picviewer-1 did. Be sure to add no new state variables. (Hint: Look at the documentation for TextField.)
  2. [3] currentAngle() (lines 71-77):
    1. [1] What does currentAngle() do? (Explain what the function does in English.)
    2. [1] When is it called?
    3. [1] Replace the call to currentAngle() with an expression using the ?? operator (the "Nil-Coalescing Operator").
  3. [8] Add TextFields to the bottom of the screen displaying the current X and Y position and the magnification factor (displayed as M) in a similar fashion to the existing one for degrees. The X and Y should be displayed as whole numbers, while the magnification factor should be shown to two decimal places. Each of these values should be editable, and when changes are made the image's state should be updated. Be sure to show an explain each of the changes you made to the code. (4 points for X and Y, 4 for magnification)

Code

picviewer-2.zip

//
//  ContentView.swift
//  picviewer-2
//  COMP 1601 Winter 2021, Carleton University
//

import SwiftUI

struct ContentView: View {
    @State private var theImage = "kittens"
    @State private var moved = false
    @State private var magnification: CGFloat = 1
    @State private var angleS = "0"
    
    func resetState() {
        moved = false
        magnification = 1
        angleS = "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: $magnification,
                        angleS: $angleS)
            HStack{
                Text("D:")
                TextField("", text: $angleS, onCommit: {
                    if Double(angleS) == nil {
                        angleS = "0"
                    }
                })
            }.padding()
        }
    }
}

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 angleS: String
    
    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)
                .rotationEffect(currentAngle())
                .gesture(dragging)
                .gesture(SimultaneousGesture(rotating, magnifying))
                .onTapGesture(count: 1, perform: tapReset)
        }
    }
    
    func currentAngle() -> Angle {
        if let a = Double(self.angleS) {
            return Angle(degrees: a)
        } else {
            return Angle(degrees: 0)
        }
    }
    
    func tapReset() {
        self.finalAmount = 1
        self.moved = false
        self.angleS = "0"
    }
    
    var rotating: some Gesture {
        RotationGesture()
            .onChanged { angle in
                self.angleS = String(format: "%.0f", angle.degrees)
            }
    }
    
    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()
    }
}