Mobile Development Lab 3

Similar documents
Mobile Development - Lab 2

Sensors. Mobile Application Development in ios School of EECS Washington State University Instructor: Larry Holder

ITP 342 Mobile App Dev. Locations and Maps

Maps, locations & sensors in ios

ITP 342 Mobile App Dev. Connections

ITP 342 Mobile App Dev. Animation

Developing Applications for ios

UI Design and Storyboarding

ITP 342 Mobile App Dev. Animation

Building Mapping Apps for ios With Swift

1 Build Your First App. The way to get started is to quit talking and begin doing. Walt Disney

A Mad Libs app that you will navigate through 3 UIViewControllers to add text that will be shown in a story on the fourth UIViewController.

Create an App that will drop PushPins onto a map based on addresses that the user inputs.

Stream iphone Sensor Data to Adafruit IO

Tip Calculator App Introducing Swift, Text Fields, Sliders, Outlets, Actions, View Controllers, Event Handling, NSDecimalNumber,

CSC 581: Mobile App Development Spring 2018

Document Version Date: 1st March, 2015

Building the App - Part 5 - Adding a Link

Gerontechnology II. Collecting Smart Phone Sensor Data for Gerontechnology. Using ios

App. Chapter 19 App. App (ViewController) App. Single View Application Single View Application View. (View Controller)

Enhancing your apps for the next dimension of touch

CSC 581: Mobile App Development Spring 2019

Topics in Mobile Computing

iphone App Basics iphone and ipod touch Development Fall 2009 Lecture 5

ITP 342 Mobile App Dev. Animation

Social Pinboard: ios(swift) Application

ios Development - Xcode IDE

Why Using Location and Map? iphone Application Programming L12: Location and Maps. Why Using Location and Map? Determine where you are

ITP 342 Mobile App Dev. Connections

CSE 438: Mobile Application Development Lab 2: Virtual Pet App

A Vertical Slider for iphone

S A M P L E C H A P T E R

Structuring an App Copyright 2013 Apple Inc. All Rights Reserved.

Implementing UI Designs in Interface Builder

Intro to Development for ios. Dave Koziol Arbormoon Software, Inc.

Stanford CS193p. Developing Applications for ios. Winter CS193p. Winter 2017

Stanford CS193p. Developing Applications for ios. Spring CS193p. Spring 2016

Understanding the Terms Of Service

Stanford CS193p. Developing Applications for ios. Fall Stanford CS193p. Fall 2011

Event Delivery: The Responder Chain

ITP 342 Mobile App Dev. Fundamentals

Objectives. Submission. Register for an Apple account. Notes on Saving Projects. Xcode Shortcuts. CprE 388 Lab 1: Introduction to Xcode

ITP 342 Mobile App Dev. Collection View

ITP 342 Mobile App Dev. Interface Builder in Xcode

Announcements. Today s Topics

} override func didreceivememorywarning() { 26 super.didreceivememorywarning() 27 } 28 } Pause Stop

Cocoa Touch Best Practices

ITP 342 Mobile App Dev. Delegates

ios 9 SDK Development

Covers ios 6. Bear Cahill. Includes 98 Techniques MANNING

What s New in Core Location

Advanced Notifications

Introductory ios Development

My First iphone App (for Xcode version 6.4)

Assignment III: Graphing Calculator

Praktikum Entwicklung von Mediensystemen mit

Widget Tour. iphone and ipod touch Development Fall 2009 Lecture 7

Learn to make desktop LE

Tables. Mobile Application Development in ios School of EECS Washington State University Instructor: Larry Holder

Stanford CS193p. Developing Applications for ios. Fall CS193p. Fall

A Mobile Mapping Application

ADVANCED M A. Learn SiriKit, imessage apps, rich notifications, and more. with real-world projects HACKING WITH SWIFT COMPLETE TUTORIAL COURSE

Assignment III: Graphing Calculator

Stanford CS193p. Developing Applications for ios. Winter CS193p. Winter 2017

Stanford CS193p. Developing Applications for iphone 4, ipod Touch, & ipad Fall Stanford CS193p Fall 2010

InterfaceBuilder and user interfaces

ios DeCal : Lecture 2 Structure of ios Applications: MVC and Auto Layout

lecture 10 UI/UX and Programmatic Design cs : spring 2018

Building GUIs with UIKit. Kevin Cathey

Types of Views. View category Purpose Examples of views. Display a particular type of content, such as an image or text.

Stanford CS193p. Developing Applications for ios. Spring CS193p. Spring 2016

Stanford CS193p. Developing Applications for ios. Winter CS193p. Winter 2017

let w = UIWindow(frame: UIScreen.mainScreen().bounds)

MVC & Onwards. CS 442: Mobile App Development Michael Saelee

Assignment II: Calculator Brain

SWIFT - CLOSURES. Global Functions Nested Functions Closure Expressions. Have a name. Capture values from enclosing function

Intro to Native ios Development. Dave Koziol Arbormoon Software, Inc.

HACKING WITH SWIFT. Practical. ios 10 COMPLETE TUTORIAL COURSE. Learn to develop apps. for ios 10 by building MP. real-world projects E S

News- ipad: ios(swift) Application

User Interfaces. Lecture 15. Application Programming on Mac OS. Hamza Bennani September 4, 2018

COMPLETE TUTORIAL COURSE. Learn to make tvos LE. apps with real-worldam S F

Assignment I: Concentration

CS193P: HelloPoly Walkthrough

Mastering UIKit on tvos

Introduction to WatchKit. CS193W - Spring Lecture 1

Stanford CS193p. Developing Applications for ios. Fall CS193p. Fall

COMP327 Mobile Computing Session:

Quick Interaction Techniques for watchos

My First iphone App. 1. Tutorial Overview

Apple s new Swift language

Assignment III: Graphing Calculator

iphone Application Programming Lab 3: Swift Types and Custom Operator + A02 discussion

What s New in MapKit. App Frameworks #WWDC17. Fredrik Olsson

ios Certified Associate Developer (ICAD)

Writing Energy Efficient Apps

ITP 342 Mobile App Dev. Web View

Stanford CS193p. Developing Applications for ios. Fall CS193p. Fall

View Concepts. iphone Application Programming Lecture 4: User Interface Design. SDK provide many types of Views to show your content

View Concepts. iphone Application Programming Lecture 4: User Interface Design. SDK provide many types of Views to show your content

Chapter 2 Welcome App

Transcription:

Mobile Development Lab 3 Objectives Illustrate closures through examples Have fun with maps, location and geolocation Have fun with animations Closures implemented in Swift Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages. Closures are used in Swift/Cocoa on various frameworks, it is useful to understand their use for various tasks. In this lab work, we will introduce closures for both geolocation and animations purposes. There are three kinds of closures: global functions have a name and cannot capture any values nested functions have a name and can capture values from their enclosing functions closure expressions don t have a name and can capture values from their context We show below multiple valid syntaxes for closures: implicits and shorthands allow to simplify syntax but with a loose of readability. For clarity, we declare the closures, but usually they are used inline, i.e.: directly as an argument function. var oneparameterandreturnvalue = { (x: Int) -> Int in return x % 10 var oneparameterandreturnvalueimplicit = { (x) -> Int in return x % 10 var oneparameterandreturnvalueshortcut = { return $0 % 10 var multipleparametersandreturnvalueimplicit = { (first, second) -> String in return first + " " + second var multipleparametersandreturnvalueshorthand = { return $0 + " " + $1 Closures are also useful to work with arrays. Similarly to lambdas in Python language, closures allow to map, filter, reduce arrays items. They are also useful to apply any function taking a function as argument (ie: sorting an array). - Consider that we need to sort an array of numbers. let numbers = [10, 3, 2, 5, 1, 4] - We could declare a closure for that and call it within sorted array s function: var sortfunc: (Int, Int) -> Bool = { (num1, num2) in return num1 < num2 numbers.sorted(by: sortfunc) Note: As sorted function is called on an array of Int, Xcode s completion asks for the by argument to have type (Int, Int) -> Bool - The closure could also be defined inline: numbers.sorted(by: { (num1, num2) in

return num1 < num2 ) - And syntax could be minimalized with implicits and shorthands: numbers.sorted{ $0 < $1 In the previous lab work, remember that we had to modify one by one the items of an array (we had to escape-decoding an array obtained from a JSON dictionary). And we used enumeration: for (i, name) in namesarray.enumerated() { // replace escape characters namesarray[i] = name.removingpercentencoding! Closures could have been useful with array s map function: namesarray.map({ (name) -> String in name.removingpercentencoding! ) With trailing closure, we don t need parenthesis: namesarray.map{ (name) -> String in name.removingpercentencoding! And with shorthand, it s much more shortened: namesarray.map{$0.removingpercentencoding! A geocoding and geolocation project We will now make a mobile application that go further in using closures on Cocoa frameworks. Maps, location and geolocation are features that can (more or less) only be implemented in mobile apps. We will first use Apple s Geocoder service to obtain latitude/longitude of a given address. Then, using delegation, we will track user s location and display on a map view. Still with closures, we will add UIView s animations to obtain a nice looking app. - Create a new project with template Single View Application ; name it MyGeoTracker - As we will use maps, we need to link our project with MapKit framework: several option for this. One simple, in Project s setting window, select tab Capabilities, and swich on Maps. - In top of ViewController.swift, add: import MapKit - Still in ViewController.swift, declare outlets for one UITextField and a MKMapView, add the corresponding UI object in Storyboard and connect them. @IBOutlet var addresstextfield: UITextField! @IBOutlet var mymapview: MKMapView! - We ll need to trigger a function when Enter key is pressed from adressetextfield: add the needed delegate. Handling geocoding - Declare a variable property of class CLGeocoder and read its documentation s description. let geocoder = CLGeocoder() Let s first implement a simple forward-geocoding feature: given a address, move the map to the corresponding location. - Add a function forwardgeocoding(withaddress:) taking a String as argument:

func fowardgeocodding(withaddress address:string) { - Call this function from textfieldshouldreturn(). Let s now implement our function forwardgeocoding(withaddress:) and use the geocoder object. The needed function uses a closure as completion handler. In closure, note that we only need its first variable, so we just ignore the second (with _ ). The first closure s variable placemarks is an optional array of CLPlacemark. The array may have zero item if nothing is found, or may have multiple items in case where multiple locations respond to the same given address. For safety, we need to use it within a if-let statement, and as we then get the first item (which is optional too), we unwrap everything in an optional chaining. To change the location of UIMapView, we can either set its property centercoordinate of type CLLocationCoordinate2D ; or call its function setregion() (that uses a MKCoordinateRegion and allow to animate the transition). And, an object of MKCoordinateRegion is constructed from CLLocationCoordinate2D and region s size in meters: geocoder.geocodeaddressstring(address, completionhandler: { placemarks, _ in ) if let placemark = placemarks?.first { let coordinateregion = MKCoordinateRegionMakeWithDistance((placemark.location?.coordinate)!, 1000, 1000) self.mymapview.setregion(coordinateregion, animated: true) With closure shorthand, it s equivalent to: geocoder.geocodeaddressstring(address) { let placemarks = $0.0 // shorthand's first member if let placemark = placemarks?.first { let coordinateregion = MKCoordinateRegionMakeWithDistance((placemark.location?.coordinate)!, 1000, 1000) self.mymapview.setregion(coordinateregion, animated: true) Great! We have our first geocoding feature. Let s now add geolocation Manage Geolocation We will add a geolocation functionality to our application. The application will count the distance traveled (since his last start or reset) and will display the current position and trace the path on the map. Setting Xcode for asking user s location We first must tell Xcode to allow our app to access user s location. To protect user privacy, an ios app which accesses the user s location information, must statically declare the intent to do so. We must include the NSLocationWhenInUseUsageDescription key in the app s Info.plist file and provide a purpose string for this key. Add the following key (anywhere between existing keys): <key>nslocationwheninuseusagedescription</key> <string>this app tracks your current location, please allow!</string> The first time the app needs to access user s location, it should ask the user to allow for accessing current location with the given message. This decision can be changed at any time by the user from the Setting app. Initialization and configuration of CLLocationManager The framework CoreLocation contains a class CLLocationManager which allows to access, start and stop the different device s location resources (GPS, Heading). We need to instantiate an object of this class.

- In ViewController.swift, declare a variable (property) of type CLLocationManager and instanciate it with default initializer. let locationmanager = CLLocationManager() - Find in the documentation (or browse header files) to get informations about how CLLocationManager works. You found the protocol associated with this class? Indeed, CLLocationManager also uses delegation to operate. Functions startupdatinglocation() and stopupdatinglocation() are respectively used to start and stop the location service, while delegate s function locationmanager(didupdatelocations:) of CLLocationManagerDelegate is called every time the position has changed. - In our case, the class ViewController will be the delegate of CLLocationManager. Change the declaration in ViewController.swift accordingly: class ViewController: UIViewController, UITextFieldDelegate, CLLocationManagerDelegate - In viewdidload(), we can configure our locationmanager (look especially its properties distancefilter and desiredaccuracy), then add: locationmanager.delegate = self Since ios requires an authorization to access user s location and the user may change its decision at any time, we must first check the authorization status. If it is still undetermined (ie: first time launching), it is necessary to request the right permission. If the status is already valid, we can start the location service. - In viewdidload(), after locationmanager configuration, add the following statements: let status = CLLocationManager.authorizationStatus() if (status ==.notdetermined) { locationmanager.requestwheninuseauthorization() else if (status ==.authorizedwheninuse) { locationmanager.startupdatinglocation() else { print("not allowed to access current location") - At the first launch (when status is undetermined), the delegate s function locationmanager(didchangeauthorizationstatus:) is called. If everything is ok, it is time to start the location service. Add the following function: func locationmanager(_ manager: CLLocationManager, didchangeauthorization status: CLAuthorizationStatus) { if (status ==.authorizedwheninuse) { locationmanager.startupdatinglocation() else { print("not allowed to access current location") - To go further: https://developer.apple.com/library/content/documentation/userexperience/conceptual/locationaware nesspg/introduction/introduction.html

Looks great but nothing happens Sure, we didn t yet implement any tracking feature. Indeed, we only prepare the location manager, we will add tracking in next section. Notice that it is possible to simulate location movement on the Simulator: Menu Debug/Location/City Run... Tracking and distance travelled We will now focus on monitoring the user s location. To illustrate it, the application will count the distance traveled since the start of the service. - In ViewController.swift, add a variable totaldistance (of type Double) initialized to 0. When the position is updated, the function locationmanager(didupdatelocations:) is called. The second argument is an array that contains CLLocation items stored in chronological order (the last one is the most recent). We can compute the get the distance in meters between two CLLocation through its instance function distance(from:). - In locationmanager(didupdatelocations:) get the last item of the array locations and get its distance to the previous location. We also need to store the previous location in a class variable previouslocation. let currentlocation = locations.last! totaldistance += currentlocation.distance(from: previouslocation) previouslocation = currentlocation - Add a new UILabel to your view and display the distance traveled. Be careful, the first few calls to this method return often very far locations. You can check the estimated accuracy from properties horizontalaccuracy and verticalaccuracy, or you can just ignore the first five calls to this method. - Implement tracking in the map: the map region should follow the current location (see above how to change map s region) - Add a UISlider named zoomslider and an associated IBAction to change the map s scale. From Storyboard, set minimum value to 10 and maximum value to 2000. Change your code in order to make map s region match with slider s value. - Add a UISwitch named geofeatureswitch to pause (starts / stops) geolocation and connect it to the IBAction switchchangedvalue(). Searching for an address (geocoding) will only work when the switch is off. You might also want to write "Enter an address" in the UILabel when the switch is off and hide the UITextField when the switch is on Is it working? Great, let s now draw on the map Drawing the path If you did not already, you must first conform ViewController with the protocol MKMapViewDelegate, then add the following statement in viewdidload() mymapview.delegate = self

To draw on the map view, several solutions are available to us. We will use the MKMapView and MKMapViewDelegate drawing overlay feature. The "viewable" objects on the map must implement the protocol MKOverlay: we will add items of MKPolyline that implement this protocol. - In the function locationmanager(didupdatelocations:), initialize a variable MKPolyline from two points: previous and current locations. MKPolyline needs two arguments, an array of CLLocationCoordinate2D (got from CLLocation s property coordinate, and the number of items in the array (here, it is 2). Then, pass it to your mymapview using function addoverlay(). The visual rendering of these objects is determined in a second step by calling the delegate method mapview(rendererforoverlay:) (of delegate MKMapViewDelegate). Here is a way to render a MKPolyline object: func mapview(_ mapview: MKMapView, rendererfor overlay: MKOverlay) -> MKOverlayRenderer { let linerenderer = MKPolylineRenderer(overlay: overlay) linerenderer.strokecolor = UIColor.green linerenderer.linewidth = 5 return linerenderer So cool! We are now drawing on the map Let s add one more feature: gesture shaking. Reset tracking by shake gesture detection Before completing this lab, we will add one more functionality to our application: we want to reset tracking (and distance) by shaking the device. Reset consists in: - Set totaldistance variable to 0 - Remove the MKOverlay objects to the map. Look at the documentation: a MKMapView object can delete multiple MKOverlay passed as arguments in an array. We will then basically remove the array of overlays associated to our map, through mymapview.overlays. - To avoid unexpected behaviors, do reset only when UISwitch is off. Several solutions are available to us for detecting a shake. We will see a natural way to do it, based on the detection of "Gesture" (Alternatively, a lowest level solution would use CoreMotion framework). Our class ViewController inherits from UIViewController which extends UIResponder. UIResponder is the class responsible for event management (Touch, Motion,...). Because of this, we can directly override any of the methods of UIResponder. - Browse the documentation and search for the name of the function that will capture the event "Shake Motion. - Add this function in ViewController and implement the behavior described above. Let s now add a simple visual feedback to the user. The visual feedback could consist in displaying a red message "Reset tracking" for a second in our distancelabel. It means that we first change distancelabel to show the red message, then a second later we turn distancelabel to its previous settings...

As we seen previously in Lab 2, we could use a Timer for this purpose. We already used a timer using a selector, let s use a timer in a more modern and convenient way, using closures (here with trailing syntax): // make new string instance from existing string let previoustext = String(describing:distanceLabel.text!) distancelabel.text = "Reset tracking..." distancelabel.textcolor = UIColor.red Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in self.distancelabel.text = previoustext self.distancelabel.textcolor = UIColor.black Notice that we also could use Grand Central Dispatch framework to manage asynchronous and delayed tasks, we will use it in next lab work. It should work Let s slightly improve our UI, using animations! Improving the User Interface This section aims at enriching the visual rendering of the application for a better interaction with the user. We already added a simple visual feedback on shake gesture. We'll now add few animations to show/hide our UITextField according to the UISwitch s state. Then we will change the behavior of the map: in addition to track the position, we will rotate the map with the user s direction. Animations provide visual effects required for better interaction with the user, enabling better understanding, and thus contribute to a better overall perception of application. Obviously, the animations are only related to graphic elements of UIKit. In particular, some properties are "animatable". By using simple closures, we just define the final state in which the object should be, and CoreAnimation does the intermediate rendering. Here are the animatable properties, according to the official documentation. Animating closures are called from UIView s static methods. For example, the following code changes the alpha property of adressetextfield during 0.5 seconds to the value 0:

UIView.animate(withDuration: 0.5, animations: { self.adressetextfield.alpha = 0 ) And again, as closure is the last argument, we can use the trailing syntax: UIView.animate(withDuration: 0.5) { self.adressetextfield.alpha = 0 The next example illustrates the use of a completion handler, which execute a second closure once animation is complete. UIView.animate(withDuration: 0.5, animations: { self.adressetextfield.alpha = 0, completion: { _ in print("addresstextfield is now hidden") ) Let s change the design of the view. Here is just a suggestion: - From Storyboard, set our UIMapView to fit the entire size of the screen; extend the UILabel, UISlider and UITextField to the entire width; put them on top of the map; set a light transparency to the UILabel and UITextField; UITextField is just below the UILabel, UISlider is between them; UISwitch button is on top-right of the screen, on top of the UILabel. - Add an animation on addresstextfield to make it visible only when the UISwitch button is off. A suggestion is to make it move to the left until disappearance.. We now focus on the map animation; we want mymapview to rotate based on the user s movements. For this purpose, we will use the heading sensor, which is also managed by CoreLocation framework. Methods locationmanager(startupdatingheading:) and stopupdatingheading() operate similarly as start/stop updating location functions: they ask locationmanager to get new heading value, which can be grabbed by locationmanager(didupdateheading:.

- Find these methods in the documentation. - Add the start/stop heading sensor simultaneously with the start/stop location sensor. - Add the delegate method that get update heading. Note that we obtain an object of class CLHeading whose trueheading property is particularly interesting. In order to rotate the map with the direction of the user, we will apply an affine transformation on our object MKMapView (remember that transform property is animatable). An affine transformation object (structure) is available from CoreGraphics framework, its name then starts by CG - Look in the documentation the transform property. Found it? It s a property of the UIView class (which inherits MKMapView) of type CGAffineTransform. Browse the reference to find the various initializers which define any affine transformation. There is one interesting to make rotation. - From StoryBoard, resize the map so that it is three times the size (width and height) and centered on the center of the view. So when we apply a rotation, the map will always appear in "full screen". - In the delegate function locationmanager(didupdateheading:), change the mapview s transform property to make it rotate. Beware, trueheading is in degree while CGAffineTransform uses radian. - As transform is an animatable property, you could add an animation closure to make the rotation more natural. Finally, let s tell Xcode to disable both status bar (the small bar at top of screen) and autorotation (by default, autorotation is enabled to switch between portrait and landscape modes). Override the following properties in ViewController (as you see they are computed properties ): override var prefersstatusbarhidden: Bool { return true override var shouldautorotate: Bool { return false Great, we used our first animation and our app looks great! Of course, to test this feature, you must use a real device, do not try to rotate your computer ;-) Notice that we designed our screen to one single screen s size, which is define in bottom of Storyboard ( View as: section). For a single screen, you must then arrange your UIObjects according to your preferred device (and/or simulator). For multiple screens compatibility, Xcode uses AutoLayout functionality (working with constraints), which is out of the scope here