iOS Programming The Big Nerd Ranch Guide, 5th

687
WOW! eBook www.wowebook.org

Transcript of iOS Programming The Big Nerd Ranch Guide, 5th

Page 1: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 2: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 3: iOS Programming The Big Nerd Ranch Guide, 5th

iOSProgramming:TheBigNerdRanchGuidebyChristianKeurandAaronHillegass

WOW! eBook www.wowebook.org

Page 4: iOS Programming The Big Nerd Ranch Guide, 5th

Copyright©2015BigNerdRanch,LLC

Allrightsreserved.PrintedintheUnitedStatesofAmerica.Thispublicationisprotectedbycopyright,andpermissionmustbeobtainedfromthepublisherpriortoanyprohibitedreproduction,storageinaretrievalsystem,ortransmissioninanyformorbyanymeans,electronic,mechanical,photocopying,recording,orlikewise.Forinformationregardingpermissions,contact

BigNerdRanch,LLC200ArizonaAveNEAtlanta,GA30307(770)817-6373http://www.bignerdranch.com/[email protected]

The10-gallonhatwithpropellerlogoisatrademarkofBigNerdRanch,LLC.

ExclusiveworldwidedistributionoftheEnglisheditionofthisbookby

PearsonTechnologyGroup800East96thStreetIndianapolis,IN46240USAhttp://www.informit.com

Theauthorsandpublisherhavetakencareinwritingandprintingthisbookbutmakenoexpressedorimpliedwarrantyofanykindandassumenoresponsibilityforerrorsoromissions.Noliabilityisassumedforincidentalorconsequentialdamagesinconnectionwithorarisingoutoftheuseoftheinformationorprogramscontainedherein.

AppStore,Apple,Cocoa,CocoaTouch,Finder,Instruments,iCloud,iPad,iPhone,iPod,iPodtouch,iTunes,Keychain,Mac,MacOS,Multi-Touch,Objective-C,OSX,Quartz,Retina,Safari,andXcodearetrademarksofApple,Inc.,registeredintheU.S.andothercountries.

Manyofthedesignationsusedbymanufacturersandsellerstodistinguishtheirproductsareclaimedastrademarks.Wherethosedesignationsappearinthisbook,andthepublisherwasawareofatrademarkclaim,thedesignationshavebeenprintedwithinitialcapitallettersorinallcapitals.

ISBN-100134389395

ISBN-13978-0134389394

Fifthedition,firstprinting,December2015ReleaseE.5.1.1

WOW! eBook www.wowebook.org

Page 5: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 6: iOS Programming The Big Nerd Ranch Guide, 5th

AcknowledgmentsWhileournamesappearonthecover,manypeoplehelpedmakethisbookareality.Wewouldliketotakethischancetothankthem.

FirstandforemostwewouldliketothankJoeConwayforhisworkontheearliereditionsofthisbook.Heauthoredthefirstthreeeditionsandcontributedgreatlytothefourtheditionaswell.Manyofthewordsinthisbookarestillhis,andforthat,weareverygrateful.

Afewpeopleinparticularwentaboveandbeyondwiththeirhelponthisbook.TheyareMikeyWard,JuanPabloClaude,andChrisMorris.

TheotherinstructorswhoteachtheiOSBootcampfeduswithanever-endingstreamofsuggestionsandcorrections.TheyareBenScheirman,BolotKerimbaev,BrianHardy,ChrisMorris,JJManton,JohnGallagher,JonathanBlocksom,JosephDixon,JuanPabloClaude,MarkDalrymple,MattBezark,MattMathias,MikeZornek,MikeyWard,PouriaAlmassi,RodStrougo,ScottRitchie,StepChristopher,ThomasWard,TJUsiyan,andTomHarrington.Theseinstructorswereoftenaidedbytheirstudentsinfindingbookerrata,somanythanksareduetoallthestudentswhoattendtheiOSBootcamp.

ThankstoalloftheemployeesatBigNerdRanchwhohelpedreviewthebook,providedsuggestions,andfounderrata.

Ourtirelesseditor,ElizabethHoladay,tookourdistractedmumblingsandmadethemintoreadableprose.

AnnaBentleyjumpedintoprovideproofing.

EllieVolckhausendesignedthecover.(Thephotoisofthebottombracketofabicycleframe.)

ChrisLoperatIntelligentEnglish.comdesignedandproducedtheprintbookandtheEPUBandKindleversions.

TheamazingteamatPearsonTechnologyGrouppatientlyguidedusthroughthebusinessendofbookpublishing.

Thefinalandmostimportantthanksgoestoourstudentswhosequestionsinspiredustowritethisbookandwhosefrustrationsinspiredustomakeitclearandcomprehensible.

WOW! eBook www.wowebook.org

Page 7: iOS Programming The Big Nerd Ranch Guide, 5th

TableofContentsIntroduction

Prerequisites

WhatHasChangedintheFifthEdition?

OurTeachingPhilosophy

HowtoUseThisBook

UsinganeBook

HowThisBookIsOrganized

StyleChoices

TypographicalConventions

NecessaryHardwareandSoftware

1.ASimpleiOSApplication

CreatinganXcodeProject

Model-View-Controller

DesigningQuiz

InterfaceBuilder

BuildingtheInterface

Creatingviewobjects

Configuringviewobjects

Runningonthesimulator

AbriefintroductiontoAutoLayout

Makingconnections

CreatingtheModelLayer

Implementingactionmethods

Loadingthefirstquestion

BuildingtheFinishedApplication

ApplicationIcons

LaunchScreen

2.TheSwiftLanguage

WOW! eBook www.wowebook.org

Page 8: iOS Programming The Big Nerd Ranch Guide, 5th

TypesinSwift

UsingStandardTypes

Inferringtypes

Specifyingtypes

Literalsandsubscripting

Initializers

Properties

Instancemethods

Optionals

Subscriptingdictionaries

LoopsandStringInterpolation

EnumerationsandtheSwitchStatement

Enumerationsandrawvalues

ExploringApple’sSwiftDocumentation

3.ViewsandtheViewHierarchy

ViewBasics

TheViewHierarchy

CreatingaNewProject

ViewsandFrames

Customizingthelabels

TheAutoLayoutSystem

Alignmentrectangleandlayoutattributes

Constraints

AddingconstraintsinInterfaceBuilder

Intrinsiccontentsize

Misplacedviews

Addingmoreconstraints

BronzeChallenge:MoreAutoLayoutPractice

4.TextInputandDelegation

TextEditing

Keyboardattributes

WOW! eBook www.wowebook.org

Page 9: iOS Programming The Big Nerd Ranch Guide, 5th

Respondingtotextfieldchanges

Dismissingthekeyboard

ImplementingtheTemperatureConversion

Numberformatters

Delegation

Conformingtoaprotocol

Usingadelegate

Moreonprotocols

BronzeChallenge:DisallowAlphabeticCharacters

5.ViewControllers

TheViewofaViewController

SettingtheInitialViewController

UITabBarController

Tabbaritems

LoadedandAppearingViews

Accessingsubviews

InteractingwithViewControllersandTheirViews

SilverChallenge:DarkMode

FortheMoreCurious:RetinaDisplay

6.ProgrammaticViews

CreatingaViewProgrammatically

ProgrammaticConstraints

Anchors

Activatingconstraints

Layoutguides

Margins

Explicitconstraints

ProgrammaticControls

BronzeChallenge:AnotherTab

SilverChallenge:User’sLocation

GoldChallenge:DroppingPins

WOW! eBook www.wowebook.org

Page 10: iOS Programming The Big Nerd Ranch Guide, 5th

FortheMoreCurious:NSAutoresizingMaskLayoutConstraint

7.Localization

Internationalization

Formatters

Baseinternationalization

Preparingforlocalization

Localization

NSLocalizedStringandstringstables

BronzeChallenge:AnotherLocalization

FortheMoreCurious:NSBundle’sRoleinInternationalization

FortheMoreCurious:ImportingandExportingasXLIFF

8.ControllingAnimations

BasicAnimations

Closures

AnotherLabel

AnimationCompletion

AnimatingConstraints

TimingFunctions

BronzeChallenge:SpringAnimations

SilverChallenge:LayoutGuides

9.UITableViewandUITableViewController

BeginningtheHomepwnerApplication

UITableViewController

SubclassingUITableViewController

CreatingtheItemClass

Custominitializers

UITableView’sDataSource

Givingthecontrolleraccesstothestore

Implementingdatasourcemethods

UITableViewCells

CreatingandretrievingUITableViewCells

WOW! eBook www.wowebook.org

Page 11: iOS Programming The Big Nerd Ranch Guide, 5th

ReusingUITableViewCells

ContentInsets

BronzeChallenge:Sections

SilverChallenge:ConstantRows

GoldChallenge:CustomizingtheTable

10.EditingUITableView

EditingMode

AddingRows

DeletingRows

MovingRows

DisplayingUserAlerts

DesignPatterns

BronzeChallenge:RenamingtheDeleteButton

SilverChallenge:PreventingReordering

GoldChallenge:ReallyPreventingReordering

11.SubclassingUITableViewCell

CreatingItemCell

ExposingthePropertiesofItemCell

UsingItemCell

DynamicCellHeights

DynamicType

Respondingtouserchanges

BronzeChallenge:CellColors

12.StackViews

UsingUIStackView

Implicitconstraints

Stackviewdistribution

Nestedstackviews

Stackviewspacing

Segues

HookingUptheContent

WOW! eBook www.wowebook.org

Page 12: iOS Programming The Big Nerd Ranch Guide, 5th

PassingDataAround

BronzeChallenge:MoreStackViews

13.UINavigationController

UINavigationController

NavigatingwithUINavigationController

AppearingandDisappearingViews

DismissingtheKeyboard

Eventhandlingbasics

DismissingbypressingtheReturnkey

Dismissingbytappingelsewhere

UINavigationBar

Addingbuttonstothenavigationbar

BronzeChallenge:DisplayingaNumberPad

SilverChallenge:ACustomUITextField

GoldChallenge:PushingMoreViewControllers

14.Camera

DisplayingImagesandUIImageView

Addingacamerabutton

TakingPicturesandUIImagePickerController

Settingtheimagepicker’ssourceType

Settingtheimagepicker’sdelegate

Presentingtheimagepickermodally

Savingtheimage

CreatingImageStore

GivingViewControllersAccesstotheImageStore

CreatingandUsingKeys

WrappingUpImageStore

BronzeChallenge:EditinganImage

SilverChallenge:RemovinganImage

GoldChallenge:CameraOverlay

FortheMoreCurious:NavigatingImplementationFiles

WOW! eBook www.wowebook.org

Page 13: iOS Programming The Big Nerd Ranch Guide, 5th

//MARK:

15.Saving,Loading,andApplicationStates

Archiving

ApplicationSandbox

ConstructingafileURL

NSKeyedArchiverandNSKeyedUnarchiver

Loadingfiles

ApplicationStatesandTransitions

WritingtotheFilesystemwithNSData

ErrorHandling

BronzeChallenge:PNG

FortheMoreCurious:ApplicationStateTransitions

FortheMoreCurious:ReadingandWritingtotheFilesystem

FortheMoreCurious:TheApplicationBundle

16.SizeClasses

AnotherSizeClass

BronzeChallenge:StackedTextFieldandLabels

17.TouchEventsandUIResponder

TouchEvents

CreatingtheTouchTrackerApplication

CreatingtheLineStruct

Structs

Valuetypesvs.referencetypes

CreatingDrawView

DrawingwithDrawView

TurningTouchesintoLines

Handlingmultipletouches

@IBInspectable

SilverChallenge:Colors

GoldChallenge:Circles

FortheMoreCurious:TheResponderChain

WOW! eBook www.wowebook.org

Page 14: iOS Programming The Big Nerd Ranch Guide, 5th

FortheMoreCurious:UIControl

18.UIGestureRecognizerandUIMenuController

UIGestureRecognizerSubclasses

DetectingTapswithUITapGestureRecognizer

MultipleGestureRecognizers

UIMenuController

MoreGestureRecognizers

UILongPressGestureRecognizer

UIPanGestureRecognizerandsimultaneousrecognizers

MoreonUIGestureRecognizer

SilverChallenge:MysteriousLines

GoldChallenge:SpeedandSize

PlatinumChallenge:Colors

FortheMoreCurious:UIMenuControllerandUIResponderStandardEditActions

19.WebServices

StartingthePhotoramaApplication

BuildingtheURL

FormattingURLsandrequests

NSURLComponents

SendingtheRequest

NSURLSession

ModelingthePhoto

JSONData

NSJSONSerialization

Enumerationsandassociatedvalues

ParsingJSONdata

DownloadingandDisplayingtheImageData

TheMainThread

BronzeChallenge:PrintingtheResponseInformation

FortheMoreCurious:HTTP

20.CollectionViewsWOW! eBook

www.wowebook.org

Page 15: iOS Programming The Big Nerd Ranch Guide, 5th

DisplayingtheGrid

CollectionViewDataSource

CustomizingtheLayout

CreatingaCustomUICollectionViewCell

DownloadingtheImageData

Extensions

NavigatingtoaPhoto

SilverChallenge:UpdatedItemSizes

GoldChallenge:CreatingaCustomLayout

21.CoreData

ObjectGraphs

Entities

Modelingentities

Transformableattributes

NSManagedObjectandsubclasses

BuildingtheCoreDataStack

NSManagedObjectModel

NSPersistentStoreCoordinator

NSManagedObjectContext

UpdatingItems

Insertingintothecontext

Savingchanges

UpdatingtheDataSource

Fetchrequestsandpredicates

SavingImagestoDisk

BronzeChallenge:PhotoViewCount

22.CoreDataRelationships

Relationships

AddingTagstotheInterface

Parent-ChildContexts

SilverChallenge:Favorites

WOW! eBook www.wowebook.org

Page 16: iOS Programming The Big Nerd Ranch Guide, 5th

23.Afterword

WhattoDoNext

ShamelessPlugs

Index

WOW! eBook www.wowebook.org

Page 17: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 18: iOS Programming The Big Nerd Ranch Guide, 5th

IntroductionAsanaspiringiOSdeveloper,youfacethreemajortasks:

YoumustlearntheSwiftlanguage.SwiftistherecommendeddevelopmentlanguageforiOS.ThefirsttwochaptersofthisbookaredesignedtogiveyouaworkingknowledgeofSwift.

Youmustmasterthebigideas.Theseincludethingslikedelegation,archiving,andtheproperuseofviewcontrollers.Thebigideastakeafewdaystounderstand.Whenyoureachthehalfwaypointofthisbook,youwillunderstandthesebigideas.

Youmustmastertheframeworks.TheeventualgoalistoknowhowtouseeverymethodofeveryclassineveryframeworkiniOS.Thisisaprojectforalifetime:therearehundredsofclassesandthousandsofmethodsavailableiniOS,andAppleaddsmoreclassesandmethodswitheveryreleaseofiOS.Inthisbook,youwillbeintroducedtoeachofthesubsystemsthatmakeuptheiOSSDK,butyouwillnotstudyeachonedeeply.Instead,ourgoalistogetyoutothepointwhereyoucansearchandunderstandApple’sreferencedocumentation.

WehaveusedthismaterialmanytimesatouriOSbootcampsatBigNerdRanch.ItiswelltestedandhashelpedthousandsofpeoplebecomeiOSdevelopers.Wesincerelyhopethatitprovesusefultoyou.

WOW! eBook www.wowebook.org

Page 19: iOS Programming The Big Nerd Ranch Guide, 5th

PrerequisitesThisbookassumesthatyouarealreadymotivatedtolearntowriteiOSapps.WewillnotspendanytimeconvincingyouthattheiPhone,iPad,andiPodtoucharecompellingpiecesoftechnology.

Wealsoassumethatyouhavesomeexperienceprogrammingandknowsomethingaboutobject-orientedprogramming.Ifthisisnottrue,youshouldprobablystartwithSwiftProgramming:TheBigNerdRanchGuide.

WOW! eBook www.wowebook.org

Page 20: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 21: iOS Programming The Big Nerd Ranch Guide, 5th

WhatHasChangedintheFifthEdition?AllofthecodeinthisbookisSwift,andanearlychapterisdevotedtogettingyouuptospeedwiththisnewlanguage.Throughoutthebook,youwillseehowtouseSwift’scapabilitiesandfeaturestowritebetteriOSapplications.WehavecometoloveSwiftatBigNerdRanchandbelieveyouwill,too.

OtheradditionsincludecollectionviewsandsizeclassesandimprovedcoverageofAutoLayout,webservices,andCoreData.

ThiseditionassumesthatthereaderisusingXcode7.1orlaterandrunningapplicationsonaniOS9orlaterdevice.

Besidestheseobviouschanges,wemadethousandsoftinyimprovementsthatwereinspiredbyquestionsfromourreadersandourstudents.Everychapterofthisbookisjustalittlebetterthanthecorrespondingchapterfromthefourthedition.

WOW! eBook www.wowebook.org

Page 22: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 23: iOS Programming The Big Nerd Ranch Guide, 5th

OurTeachingPhilosophyThisbookwillteachyoutheessentialconceptsofiOSprogramming.Atthesametime,youwilltypeinalotofcodeandbuildabunchofapplications.Bytheendofthebook,youwillhaveknowledgeandexperience.However,alltheknowledgeshouldnot(and,inthisbook,willnot)comefirst.Thatisthetraditionalwaywehaveallcometoknowandhate.Instead,wetakealearn-while-doingapproach.Developmentconceptsandactualcodinggotogether.

HereiswhatwehavelearnedovertheyearsofteachingiOSprogramming:

Wehavelearnedwhatideaspeoplemustgrasptogetstartedprogramming,andwefocusonthatsubset.

Wehavelearnedthatpeoplelearnbestwhentheseconceptsareintroducedastheyareneeded.

Wehavelearnedthatprogrammingknowledgeandexperiencegrowbestwhentheygrowtogether.

Wehavelearnedthat“goingthroughthemotions”ismuchmoreimportantthanitsounds.Manytimeswewillaskyoutostarttypingincodebeforeyouunderstandit.Werealizethatyoumayfeellikeatrainedmonkeytypinginabunchofcodethatyoudonotfullygrasp.Butthebestwaytolearncodingistofindandfixyourtypos.Farfrombeingadrag,thisbasicdebuggingiswhereyoureallylearntheinsandoutsofthecode.Thatiswhyweencourageyoutotypeinthecodeyourself.Youcouldjustdownloadit,butcopyingandpastingisnotprogramming.Wewantbetterforyouandyourskills.

Whatdoesthismeanforyou,thereader?Tolearnthiswaytakessometrust–andweappreciateyours.Italsotakespatience.Asweleadyouthroughthesechapters,wewilltrytokeepyoucomfortableandtellyouwhatishappening.However,therewillbetimeswhenyouwillhavetotakeourwordforit.(Ifyouthinkthiswillbugyou,keepreading–wehavesomeideasthatmighthelp.)Donotgetdiscouragedifyourunacrossaconceptthatyoudonotunderstandrightaway.Rememberthatweareintentionallynotprovidingalltheknowledgeyouwilleverneedallatonce.Ifaconceptseemsunclear,wewilllikelydiscussitinmoredetaillaterwhenitbecomesnecessary.Andsomethingsthatarenotclearatthebeginningwillsuddenlymakesensewhenyouimplementthemthefirst(orthetwelfth)time.

Peoplelearndifferently.Itispossiblethatyouwilllovehowwehandoutconceptsonanas-neededbasis.Itisalsopossiblethatyouwillfinditfrustrating.Incaseofthelatter,herearesomeoptions:

Takeadeepbreathandwaititout.Wewillgetthere,andsowillyou.

Checktheindex.Wewillletitslideifyoulookaheadandreadthroughamoreadvanceddiscussionthatoccurslaterinthebook.

ChecktheonlineAppledocumentation.Thisisanessentialdevelopertool,and

WOW! eBook www.wowebook.org

Page 24: iOS Programming The Big Nerd Ranch Guide, 5th

youwillwantplentyofpracticeusingit.Consultitearlyandoften.

IfSwiftorobject-orientedprogrammingconceptsaregivingyouahardtime(orifyouthinktheywill),youmightconsiderbackingupandreadingourSwiftProgramming:TheBigNerdRanchGuide.

WOW! eBook www.wowebook.org

Page 25: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 26: iOS Programming The Big Nerd Ranch Guide, 5th

HowtoUseThisBookThisbookisbasedontheclassweteachatBigNerdRanch.Assuch,itwasdesignedtobeconsumedinacertainmanner.

Setyourselfareasonablegoal,like“Iwilldoonechaptereveryday.”Whenyousitdowntoattackachapter,findaquietplacewhereyouwillnotbeinterruptedforatleastanhour.Shutdownyouremail,yourTwitterclient,andyourchatprogram.Thisisnotatimeformultitasking;youwillneedtoconcentrate.

Dotheactualprogramming.Youcanreadthroughachapterfirst,ifyoulike.Butthereallearningcomeswhenyousitdownandcodeasyougo.Youwillnotreallyunderstandtheideauntilyouhavewrittenaprogramthatusesitand,perhapsmoreimportantly,debuggedthatprogram.

Acoupleoftheexercisesrequiresupportingfiles.Forexample,inthefirstchapteryouwillneedaniconforyourQuizapplication,andwehaveoneforyou.Youcandownloadtheresourcesandsolutionstotheexercisesfromhttp://www.bignerdranch.com/solutions/iOSProgramming5ed.zip.

Therearetwotypesoflearning.WhenyoulearnaboutthePeloponnesianWar,youaresimplyaddingdetailstoascaffoldingofideasthatyoualreadyunderstand.Thisiswhatwewillcall“EasyLearning.”Yes,learningaboutthePeloponnesianWarcantakealongtime,butyouareseldomflummoxedbyit.LearningiOSprogramming,ontheotherhand,is“HardLearning,”andyoumayfindyourselfquitebaffledattimes,especiallyinthefirstfewdays.Inwritingthisbook,wehavetriedtocreateanexperiencethatwilleaseyouoverthebumpsinthelearningcurve.Herearetwothingsyoucandotomakethejourneyeasier:

FindsomeonewhoalreadyknowshowtowriteiOSapplicationsandwillansweryourquestions.Inparticular,gettingyourapplicationontoadevicethefirsttimeisusuallyveryfrustratingifyouaredoingitwithoutthehelpofanexperienceddeveloper.

Getenoughsleep.Sleepypeopledonotrememberwhattheyhavelearned.

WOW! eBook www.wowebook.org

Page 27: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 28: iOS Programming The Big Nerd Ranch Guide, 5th

UsinganeBookIfyouarereadingthisbookonaneReader,wewanttopointoutthatreadingthecodemaybetrickyattimes.Longerlinesofcodewillwraptoasecondlinedependingonyourselectedfontsize.ThisbothersusbecausewearereallyconscientiousatBigNerdRanchaboutthewayourcodeappearsonthepage.Clearvisualpatternsincodemakethatcodeeasiertounderstand.

Thelongestlinesofcodeinthisbookare86monospacecharacters,likethisone.funcprocessRecentPhotosRequest(datadata:NSData?,error:NSError?)->PhotosResult{

YoucanplaywithyoureReaders’ssettingstofindthebestforviewinglongcodelines.

Whenyougettothepointwhereyouareactuallytypingincode,wesuggestopeningthebookonyourMaciniBooksorAdobeDigitalEditions.(AdobeDigitalEditionsisafreeeReaderapplicationyoucandownloadfromhttp://www.adobe.com/products/digitaleditions/.)Maketheapplicationwindowlargeenoughsothatyoucanseethecodewithnowrappinglines.Youwillalsobeabletoseethefiguresinfulldetail.

WOW! eBook www.wowebook.org

Page 29: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 30: iOS Programming The Big Nerd Ranch Guide, 5th

HowThisBookIsOrganizedInthisbook,eachchapteraddressesoneormoreideasofiOSdevelopmentthroughdiscussionandhands-onpractice.Formorecodingpractice,mostchaptersincludechallengeexercises.Weencourageyoutotakeonatleastsomeofthese.TheyareexcellentforfirmingupyourgraspoftheconceptsintroducedinthechapterandformakingyouamoreconfidentiOSprogrammer.Finally,mostchaptersconcludewithoneortwo“FortheMoreCurious”sectionsthatexplaincertainconsequencesoftheconceptsthatwereintroducedearlier.

Chapter1introducesyoutoiOSprogrammingasyoubuildanddeployatinyapplication.YouwillgetyourfeetwetwithXcodeandtheiOSsimulatoralongwithallthestepsforcreatingprojectsandfiles.ThechapterincludesadiscussionofModel-View-ControllerandhowitrelatestoiOSdevelopment.

Chapter2providesanoverviewofSwift,includingbasicsyntax,types,optionals,initialization,andhowSwiftisabletointeractwiththeexistingiOSframeworks.Youwillalsogetexperienceworkinginaplayground,Xcode’snewcodeprototypingtool.

InChapter3,youwillfocusontheiOSuserinterfaceasyoulearnaboutviewsandtheviewhierarchyandcreateanapplicationcalledWorldTrotter.

Chapter4introducesdelegation,animportantiOSdesignpattern.YouwillalsoatextfieldtoWorldTrotter.

InChapter5,youwillexpandWorldTrotterandlearnaboutusingviewcontrollersformanaginguserinterfaces.Youwillgetpracticeworkingwithviewsandviewcontrollersaswellasnavigatingbetweenscreensusingatabbar.

InChapter6,youwilllearnhowtomanageviewsandviewcontrollersincode.YouwilladdasegmentedcontroltoWorldTrotterthatwillletyouswitchbetweenvariousmaptypes.

Chapter7introducestheconceptsandtechniquesofinternationalizationandlocalization.YouwilllearnaboutNSLocale,stringstables,andNSBundleasyoulocalizepartsofWorldTrotter.

InChapter8,youwilllearnaboutandadddifferenttypesofanimationstotheQuizprojectthatyoucreatedinChapter1.

Chapter9introducesthelargestapplicationinthebook–Homepwner.(Bytheway,“Homepwner”isnotatypo;youcanfindthedefinitionof“pwn”atwww.urbandictionary.com.)Thisapplicationkeepsarecordofyouritemsincaseoffireorothercatastrophe.Homepwnerwilltakeeightchapterstocomplete.

InChapter9-Chapter11,youwillworkwithtables.Youwilllearnabouttableviews,theirviewcontrollers,andtheirdatasources.Youwilllearnhowtodisplaydatainatable,howtoallowtheusertoeditthetable,andhowtoimprovetheinterface.

Chapter12introducesstackviewsthatwillhelpyoucreatecomplexinterfacesveryeasily.YouwilluseastackviewtoaddanewscreentoHomepwnerthatdisplaysthedetailsforasingleitem.

WOW! eBook www.wowebook.org

Page 31: iOS Programming The Big Nerd Ranch Guide, 5th

Chapter13buildsonthenavigationexperiencegainedinChapter5.YouwilluseUINavigationControllertogiveHomepwneradrill-downinterfaceandanavigationbar.

Chapter14introducesthecamera.YouwilltakepicturesanddisplayandstoreimagesinHomepwner.

InChapter15,youwilladdpersistencetoHomepwnerusingarchivingtosaveandloadtheapplicationdata.

InChapter16,youwilllearnaboutsizeclasses,andyouwillusethesetoupdateHomepwner’sinterfacetoscalewellacrossvariousscreensizes.

InChapter17andChapter18,youwillcreateadrawingapplicationnamedTouchTrackertolearnabouttouchevents.YouwillseehowtoaddmultitouchcapabilityandhowtouseUIGestureRecognizertorespondtoparticulargestures.Youwillalsogetexperiencewiththefirstresponderandresponderchainconceptsandmorepracticewithusingstructuresanddictionaries.

Chapter19introduceswebservicesasyoucreatethePhotoramaapplication.ThisapplicationfetchesandparsesJSONfromaserverusingNSURLSessionsandNSJSONSerialization.

InChapter20,youwilllearnaboutcollectionviewsasyoubuildaninterfaceforPhotoramausingUICollectionViewandUICollectionViewCell.

InChapter21andChapter22,youwilladdpersistencetoPhotoramausingCoreData.YouwillstoreandloadimagesandassociateddatausinganNSManagedObjectContext.

WOW! eBook www.wowebook.org

Page 32: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 33: iOS Programming The Big Nerd Ranch Guide, 5th

StyleChoicesThisbookcontainsalotofcode.Wehaveattemptedtomakethatcodeandthedesignsbehinditexemplary.Wehavedoneourbesttofollowtheidiomsofthecommunity,butattimeswehavewanderedfromwhatyoumightseeinApple’ssamplecodeorcodeyoumightfindinotherbooks.Inparticular,youshouldknowup-frontthatwenearlyalwaysstartaprojectwiththesimplesttemplateproject:thesingleviewapplication.Whenyourappworks,youwillknowitisbecauseofyourefforts–notbecausethatbehaviorwasbuiltintothetemplate.

WOW! eBook www.wowebook.org

Page 34: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 35: iOS Programming The Big Nerd Ranch Guide, 5th

TypographicalConventionsTomakethisbookeasiertoread,certainitemsappearincertainfonts.Classes,types,methods,andfunctionsappearinabold,fixed-widthfont.Classesandtypesstartwithcapitalletters,andmethodsandfunctionsstartwithlowercaseletters.Forexample,“IntheloadView()methodoftheRexViewControllerclass,createaconstantoftypeString.”

Variables,constants,andfilenamesappearinafixed-widthfontbutarenotbold.Soyouwillsee,“InViewController.swift,addavariablenamedfidoandinitializeitto“Rufus”.”

Applicationnames,menuchoices,andbuttonnamesappearinasansseriffont.Forexample,“OpenXcodeandselectNewProject…fromtheFilemenu.SelectSingleViewApplicationandthenclickChoose….”

Allcodeblocksareinafixed-widthfont.Codethatyouneedtotypeinisalwaysbold.Forexample,inthefollowingcode,youwouldtypeinthetwolinesbeginning@IBOutlet.Theotherlinesarealreadyinthecodeandareincludedtoletyouknowwheretoaddthenewlines.importUIKit

classViewController:UIViewController{

@IBOutletvarquestionLabel:UILabel!@IBOutletvaranswerLabel:UILabel!

}

WOW! eBook www.wowebook.org

Page 36: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 37: iOS Programming The Big Nerd Ranch Guide, 5th

NecessaryHardwareandSoftwareTobuildtheapplicationsinthisbook,youmusthaveaMacrunningOSXYosemite(10.10.5)orlater.YouwillalsoneedXcode,Apple’sIntegratedDevelopmentEnvironment,whichisavailableontheAppStore.XcodeincludestheiOSSDK,theiOSsimulator,andotherdevelopmenttools.

YoushouldjointheAppleDeveloperProgram,whichcosts$99/year,because:

Downloadingthelatestdevelopertoolsisfreeformembers.

Youcannotputanappinthestoreuntilyouareamember.

Ifyouaregoingtotakethetimetoworkthroughthisentirebook,membershipintheAppleDeveloperProgramisworththecost.Gotohttp://developer.apple.com/programs/ios/tojoin.

WhataboutiOSdevices?MostoftheapplicationsyouwilldevelopinthefirsthalfofthebookareforiPhone,butyouwillbeabletorunthemonaniPad.OntheiPadscreen,iPhoneapplicationsappearinaniPhone-sizedwindow.NotacompellinguseofiPad,butthatisOKwhenyouarestartingwithiOS.Intheearlychapters,youwillbefocusedonlearningthefundamentalsoftheiOSSDK,andthesearethesameacrossiOSdevices.Laterinthebook,youwillseehowtomakeapplicationsrunnativelyonbothiOSdevicefamilies.

Excitedyet?Good.Let’sgetstarted.

WOW! eBook www.wowebook.org

Page 38: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 39: iOS Programming The Big Nerd Ranch Guide, 5th

1ASimpleiOSApplication

Inthischapter,youaregoingtowriteaniOSapplicationnamedQuiz.Thisapplicationwillshowaquestionandthenrevealtheanswerwhentheusertapsabutton.Tappinganotherbuttonwillshowtheuseranewquestion(Figure1.1).

Figure1.1Yourfirstapplication:Quiz

WhenyouarewritinganiOSapplication,youmustanswertwobasicquestions:

HowdoIgetmyobjectscreatedandconfiguredproperly?(Example:“IwantabuttonherethatsaysNextQuestion.”)

HowdoImakemyapprespondtouserinteraction?(Example:“Whentheusertapsthebutton,Iwantthispieceofcodetobeexecuted.”)

Mostofthisbookisdedicatedtoansweringthesequestions.

Asyougothroughthisfirstchapter,youwillprobablynotunderstandeverythingthatyouaredoing,andyoumayfeelridiculousjustgoingthroughthemotions.Butgoingthrough

WOW! eBook www.wowebook.org

Page 40: iOS Programming The Big Nerd Ranch Guide, 5th

themotionsisenoughfornow.Mimicryisapowerfulformoflearning;itishowyoulearnedtospeak,anditishowyouwillstartiOSprogramming.Asyoubecomemorecapable,youwillexperimentandchallengeyourselftodocreativethingsontheplatform.Fornow,goaheadanddowhatweshowyou.Thedetailswillbeexplainedinlaterchapters.

WOW! eBook www.wowebook.org

Page 41: iOS Programming The Big Nerd Ranch Guide, 5th

CreatinganXcodeProjectOpenXcodeand,fromtheFilemenu,selectNew→Project…(IfXcodeopenstoawelcomescreen,selectCreateanewXcodeproject.)

Anewworkspacewindowwillappearandasheetwillslidedownfromitstoolbar.Onthelefthandside,findtheiOSsectionandselectApplication(Figure1.2).Youwillbeofferedseveralapplicationtemplatestochoosefrom.SelectSingleViewApplication.

Figure1.2Creatingaproject

ThisbookwascreatedforXcode7.1.ThenamesofthesetemplatesmaychangewithnewXcodereleases.IfyoudonotseeaSingleViewApplicationtemplate,usethesimplest-soundingtemplate.OrvisittheBigNerdRanchforumforthisbookathttp://forums.bignerdranch.comforhelpworkingwithnewerversionsofXcode.

ClickNextand,inthenextsheet,enterQuizfortheProductName(Figure1.3).Theorganizationnameandcompanyidentifierarerequiredtocontinue.YoucanuseBigNerdRanchandcom.bignerdranch.Oruseyourcompanynameandcom.yourcompanynamehere.

FromtheLanguagepop-upmenu,chooseSwift,andfromtheDevicespop-upmenu,chooseiPhone.MakesurethattheUseCoreData,IncludeUnitTests,andIncludeUITestscheckboxesareunchecked.

WOW! eBook www.wowebook.org

Page 42: iOS Programming The Big Nerd Ranch Guide, 5th

Figure1.3Configuringanewproject

YouarecreatingQuizasaniPhoneapplication,butyouwillbeabletorunitonaniPadaswell.Forthefirstpartofthisbook,youwillsticktocreatingiPhoneapplicationsandfocusonlearningthefundamentalsoftheiOSsoftwaredevelopmentkit,orSDK.Later,youwilllearnhowtocreateuserinterfacesthatadaptelegantlytothescreensizesofdifferentiOSdevices.

ClickNextand,inthefinalsheet,savetheprojectinthedirectorywhereyouplantostoretheexercisesinthisbook.ClickCreatetocreatetheQuizproject.

Oncetheprojectiscreated,itwillopenintheXcodeworkspacewindow(Figure1.4).

WOW! eBook www.wowebook.org

Page 43: iOS Programming The Big Nerd Ranch Guide, 5th

Figure1.4Xcodeworkspacewindow

Thelefthandsideoftheworkspacewindowisthenavigatorarea.Thisareadisplaysdifferentnavigators–toolsthatshowyoudifferentpartsofyourproject.Youcanopenanavigatorbyselectingoneoftheiconsinthenavigatorselector,whichisthebarjustabovethenavigatorarea.

Thenavigatorcurrentlyopenistheprojectnavigator.Theprojectnavigatorshowsyouthefilesthatmakeupaproject(Figure1.5).Youcanselectoneofthesefilestoopenitintheeditorareatotherightofthenavigatorarea.

Thefilesintheprojectnavigatorcanbegroupedintofolderstohelpyouorganizeyourproject.Afewgroupshavebeencreatedbythetemplateforyou.Youcanrenamethem,ifyouwant,oraddnewones.Thegroupsarepurelyfortheorganizationoffilesanddonotcorrelatetothefilesysteminanyway.

Figure1.5Quizapplication’sfilesintheprojectnavigator

WOW! eBook www.wowebook.org

Page 44: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 45: iOS Programming The Big Nerd Ranch Guide, 5th

Model-View-ControllerBeforeyoubeginyourapplication,let’sdiscussakeyconceptinapplicationarchitecture:Model-View-Controller,orMVC.MVCisadesignpatternusediniOSdevelopment.InMVC,everyinstancebelongstoeitherthemodellayer,theviewlayer,orthecontrollerlayer.(Layerheresimplyreferstooneormoreobjectsthattogetherfulfillarole.)

Themodellayerholdsdataandknowsnothingabouttheuserinterface.InQuiz,themodelwillconsistoftwoorderedlistsofstrings:oneforquestionsandanotherforanswers.

Usually,instancesinthemodellayerrepresentrealthingsintheworldoftheuser.Forexample,whenyouwriteanappforaninsurancecompany,yourmodelwillalmostcertainlycontainacustomtypecalledInsurancePolicy.

Theviewlayercontainsobjectsthatarevisibletotheuser.Examplesofviewobjects,orviews,arebuttons,textfields,andsliders.Viewobjectsmakeupanapplication’suserinterface.InQuiz,thelabelsshowingthequestionandanswerandthebuttonsbeneaththemareviewobjects.

Thecontrollerlayeriswheretheapplicationismanaged.Controllerobjects,orcontrollers,arethemanagersofanapplication.Controllersconfiguretheviewsthattheuserseesandmakesurethattheviewandmodelobjectsstaysynchronized.

Ingeneral,controllerstypicallyhandle“Andthen?”questions.Forexample,whentheuserselectsanitemfromalist,thecontrollerdetermineswhattheuserseesnext.

Figure1.6showstheflowofcontrolinanapplicationinresponsetouserinput,suchastheusertappingabutton.

WOW! eBook www.wowebook.org

Page 46: iOS Programming The Big Nerd Ranch Guide, 5th

Figure1.6MVCpattern

Noticethatmodelsandviewsdonottalktoeachotherdirectly;controllerssitsquarelyinthemiddleofeverything,receivingmessagesanddispatchinginstructions.

WOW! eBook www.wowebook.org

Page 47: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 48: iOS Programming The Big Nerd Ranch Guide, 5th

DesigningQuizYouaregoingtowritetheQuizapplicationusingtheMVCpattern.Hereisabreakdownoftheinstancesyouwillbecreatingandworkingwith:

Themodellayerwillconsistoftwoinstancesof[String].

TheviewlayerwillconsistoftwoinstancesofUILabelandtwoinstancesofUIButton.

ThecontrollerlayerwillconsistofaninstanceofAppDelegateandaninstanceofViewController.

TheseinstancesandtheirrelationshipsarelaidoutinthediagramforQuizshowninFigure1.7.

Figure1.7ObjectdiagramforQuiz

Figure1.7isthebigpictureofhowthefinishedQuizapplicationwillwork.Forexample,whentheNextQuestionbuttonistapped,itwilltriggeramethodinViewController.Amethodisalotlikeafunction–alistofinstructionstobeexecuted.Thismethodwillretrieveanewquestionfromthearrayofquestionsandaskthetoplabeltodisplaythatquestion.

ItisOKifthisdiagramdoesnotmakesenseyet–itwillbytheendofthechapter.ReferWOW! eBook

www.wowebook.org

Page 49: iOS Programming The Big Nerd Ranch Guide, 5th

backtoitasyoubuildtheapptoseehowitistakingshape.

YouaregoingtobuildQuizinsteps,startingwiththevisualinterfacefortheapplication.

WOW! eBook www.wowebook.org

Page 50: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 51: iOS Programming The Big Nerd Ranch Guide, 5th

InterfaceBuilderYouareusingtheSingleViewApplicationtemplatebecauseitisthesimplesttemplatethatXcodeoffers.Still,thistemplatehasasignificantamountofmagicinthatsomecriticalcomponentshavealreadybeensetupforyou.Fornow,youwilljustusethesecomponents,withoutattemptingtogainadeepunderstandingofhowtheywork.Therestofthebookwillbeconcernedwiththosedetails.

ClickonceontheMain.storyboardfile.Xcodewillopenitsgraphic-styleeditorcalledInterfaceBuilder.

InterfaceBuilderdividestheeditorareaintotwosections:thedocumentoutline,onthelefthandside,andthecanvas,ontheright.

ThisisshowninFigure1.8.Ifwhatyouseeonyourscreendoesnotexactlymatchthefigure,youmayhavetoclickontheShowDocumentOutlinebutton.(Ifyouhaveadditionalareasshowing,donotworryaboutthem.)Youmayalsohavetoclickonthedisclosuretrianglesonthedocumentoutlinetorevealcontent.

WOW! eBook www.wowebook.org

Page 52: iOS Programming The Big Nerd Ranch Guide, 5th

Figure1.8InterfaceBuildershowingMain.storyboard

ThelargerectanglethatyouseeintheInterfaceBuildercanvasiscalledasceneandrepresentstheonly“screen”orviewyourapplicationhasatthistime(rememberthatyouusedthesingleviewapplicationtemplatetocreatethisproject).

Inthenextsection,youwilllearnhowtocreateauserinterfaceforyourapplicationusingInterfaceBuilder.InterfaceBuilderletsyoudragobjectsfromalibraryontothecanvastocreateinstances,andalsoletsyouestablishconnectionsbetweenthoseobjectsandyourcode.Theseconnectionscanresultincodebeingcalledbyauserinteraction.

AcrucialfeatureofInterfaceBuilderisthatitisnotagraphicalrepresentationofcodecontainedinotherfiles.InterfaceBuilderisanobjecteditorthatcancreateinstancesofobjectsandmanipulatetheirproperties.Whenyouaredoneeditinganinterface,itdoesnotgeneratecodethatcorrespondstotheworkyouhavedone.A.storyboardfileisanarchiveofobjectinstancestobeloadedintomemorywhennecessary.

WOW! eBook www.wowebook.org

Page 53: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 54: iOS Programming The Big Nerd Ranch Guide, 5th

BuildingtheInterfaceLet’sgetstartedonyourinterface.YouhaveselectedMain.storyboardtorevealitssinglesceneinthecanvas(Figure1.9).

Figure1.9ThesceneinMain.storyboard

Itistimetoaddyourviewobjectstothatblankslate.

Creatingviewobjects

MakesurethattheutilityareawithinXcode’swindowisvisible.Youmayneedtoclickontherightmostbuttonofthe controlinthetop-rightcornerofthewindow.Theutilityareaistotherightoftheeditorareaandhastwosections:theinspectorandthelibrary.Thetopsectionistheinspector,whichcontainssettingsforthefileorobjectthatisselectedintheeditorarea.Thebottomsectionisthelibrary,whichlistsitemsthatyoucanaddtoafileorproject.

Atthetopofeachsectionintheutilityareaisaselectorfordifferentinspectorsandlibraries(Figure1.10).

WOW! eBook www.wowebook.org

Page 55: iOS Programming The Big Nerd Ranch Guide, 5th

Figure1.10Xcodeutilityarea

Yourapplicationinterfacerequiresfourviewobjects:twobuttonstoacceptuserinputandtwotextlabelstodisplayinformation.Toaddthem,firstmakesureyoucanseetheobjectlibrary,asseeninFigure1.10,byselectingthe tabfromthelibraryselector.

Theobjectlibrarycontainstheobjectsthatyoucanaddtoastoryboardfiletocomposeyourinterface.FindtheLabelobjectbyeitherscrollingdownthroughthelistorbyusingthesearchbaratthebottomofthelibrary.Selectthisobjectinthelibraryanddragitontotheviewobjectonthecanvas.Dragthelabelaroundthecanvasandnoticethedashedbluelinesthatappearwhenthelabelisnearthecenterofthecanvas(Figure1.11).Theseguidelineswillhelpyoulayoutyourinterface.

Figure1.11Addingalabeltothecanvas

WOW! eBook www.wowebook.org

Page 56: iOS Programming The Big Nerd Ranch Guide, 5th

Usingtheguidelines,positionthelabelinthehorizontalcenteroftheviewandnearthetop,asshowninFigure1.11.Eventually,thislabelwilldisplayquestionstotheuser.Dragasecondlabelontotheviewandpositionitinthehorizontalcenter,closertothemiddle.Thislabelwilldisplayanswers.

Next,findButtonintheobjectlibraryanddragtwobuttonsontotheview.Positiononebeloweachlabel.

YouhavenowaddedfourviewobjectstotheViewController’suserinterface.Noticethattheyalsoappearinthedocumentoutline.YourcompletedinterfaceshouldlooklikeFigure1.12.

Figure1.12BuildingtheQuizinterface

Configuringviewobjects

Nowthatyouhavecreatedtheviewobjects,youcanconfiguretheirattributes.Someattributesofaview,likesize,position,andtext,canbechangeddirectlyonthecanvas.Forexample,youcanresizeanobjectbyselectingitinthecanvasorthedocumentoutlineandthendraggingitscornersandedgesinthecanvas.

WOW! eBook www.wowebook.org

Page 57: iOS Programming The Big Nerd Ranch Guide, 5th

Beginbyrenamingthelabelsandbuttons.Double-clickoneachlabelandreplacethetextwith???.Thendouble-clicktheupperbuttonandchangeitsnametoNextQuestion.RenamethelowerbuttontoShowAnswer.TheresultsareshowninFigure1.13.

Figure1.13Renamingthelabelsandbuttons

Youmayhavenoticedthatasaresultofchangingthetextinthelabelsandbuttons,andthereforetheirwidths,theyarenolongerneatlycenteredinthescene.Clickoneachofthemanddragtocenterthemagain,asshowninFigure1.14.

Figure1.14Centeringthelabelsandbuttons

Runningonthesimulator

Totestyouruserinterface,youaregoingtorunQuizonXcode’siOSsimulator.

ToprepareQuiztorunonthesimulator,findthecurrentschemepop-upmenuontheXcodetoolbar(Figure1.15).

WOW! eBook www.wowebook.org

Page 58: iOS Programming The Big Nerd Ranch Guide, 5th

Figure1.15iPhone6sschemeselected

IfitsayssomethinggenericlikeiPhone6s,thentheprojectissettorunonthesimulatorandyouaregoodtogo.IfitsayssomethinglikeChristian’siPhone,thenclickandchooseiPhone6sfromthepop-upmenu.TheiPhone6sschemewillbeyoursimulatordefaultthroughoutthisbook.

(Ifyouwouldliketoruntheapplicationonanactualdevice,readouriOSDeviceProvisionGuideathttps://www.bignerdranch.com/we-teach/how-to-prepare/ios-device-provisioning/.)

Clickthetriangularplaybuttoninthetoolbar.Thiswillbuild(compile)andthenruntheapplication.YouwillbedoingthisoftenenoughthatyoumaywanttolearnandusethekeyboardshortcutCommand-R.

Afterthesimulatorlaunchesyouwillseethattheinterfacehasalltheviewsyouadded,buttheyarenotcorrectlyplaced.ThisisbecausethelabelsandbuttonscurrentlyhaveafixedpositiononascreenlargerthaniPhone6s’s,andtheydonotremaincenteredonthemainview.Tocorrectthisproblem,youwilluseatechnologycalledAutoLayout.

AbriefintroductiontoAutoLayout

Asofnow,yourinterfacelooksniceintheInterfaceBuildercanvas.ButiOSdevicescomeinevermorescreensizes,andapplicationsareexpectedtosupportallscreensizesandorientations–andperhapsmorethanonedevicetype.Youneedtoguaranteethatthelayoutofviewobjectswillbecorrectregardlessofthescreensizeororientationofthedevicerunningtheapplication.ThetoolforthistaskisAutoLayout.

AutoLayoutworksbyspecifyingpositionandsizeconstraintsforeachviewobjectinascene.Theseconstraintscanberelativetoneighboringviewsorcontainerviews.Acontainerviewisjustaviewobjectthatcontainsanotherview.Forexample,takealookatthedocumentoutlineforMain.storyboard(Figure1.16).

WOW! eBook www.wowebook.org

Page 59: iOS Programming The Big Nerd Ranch Guide, 5th

Figure1.16Documentlayoutwithacontainerview

YoucanseeinthedocumentoutlinethatthelabelsandbuttonsyouaddedareindentedwithrespecttoaViewobject.Thisviewobjectisthecontainerofthelabelsandbuttons,andtheobjectscanbepositionedandsizedrelativetothisview.

TobeginspecifyingAutoLayoutconstraints,selectthetoplabelbyclickingoniteitheronthecanvasorinthedocumentoutline.Atthebottomofthecanvas,noticetheAutoLayoutmenus,showninFigure1.17.

Figure1.17TheAutoLayoutmenus

Withthetoplabelstillselected,clickonthe icontorevealtheAlignmenushowninFigure1.18.

Figure1.18Centeringthetoplabelinthecontainer

WOW! eBook www.wowebook.org

Page 60: iOS Programming The Big Nerd Ranch Guide, 5th

WithintheAlignmenu,checktheHorizontallyinContainercheckboxtocenterthelabelinthecontainer.ThenclicktheAdd1Constraintbutton.Thisconstraintguaranteesthatonanysizescreen,inanyorientation,thelabelwillbecenteredhorizontally.

Nowyouneedtoaddmoreconstraintstocenterthelowerlabelandthebuttonswithrespecttothetoplabelandtolockthespacingbetweenthem.SelectthefourviewsbyCommand-clickingonthemoneafteranotherandthenclickonthe icontoopenthePinmenushowninFigure1.19.

Figure1.19Pinningthecenteringandspacingbetweenviews

AsseeninFigure1.19,clickontheredverticaldashedsegmentnearthetopofthemenu.Whenyouclickonthesegment,itwillbecomesolidred,indicatingthatthedistanceofeachviewispinnedtoitsnearesttopneighbor.Also,checktheAlignboxandthenselectHorizontalCentersfromthepop-upmenu.ForUpdateFrames,makesurethatyouhaveItemsofNewConstraintsselected.Finally,clickontheAdd7Constraintsbuttonatthebottomofthemenu.

Ifyoumadeanymistakeswhileaddingconstraints,youmayseeredororangeconstraintsandframesonthecanvasinsteadofthecorrectbluelines.Ifthatisthecase,youwillwanttocleartheexistingconstraintsandgothroughthestepsaboveagain.Toclearconstraints,firstselectthebackground(container)view.Thenclickthe icontoopentheResolveAutoLayoutIssuesmenu.SelectClearConstraintsundertheAllViewssection(Figure1.20).Thiswillclearawayanyconstraintsthatyouhaveaddedandgiveyouafreshstartonaddingtheconstraintsbackin.

WOW! eBook www.wowebook.org

Page 61: iOS Programming The Big Nerd Ranch Guide, 5th

Figure1.20Clearingconstraints

AutoLayoutcanbeadifficulttooltomaster,andthatiswhyyouarestartingtouseitinthefirstchapterofthisbook.Bystartingearly,youwillhavemorechancestouseitandgetusedtoitscomplexity.Also,dealingwithproblemsbeforethingsgettoocomplicatedwillhelpyouunderstandhowtodebuglayoutissueswithmoreconfidence.

Toconfirmthatyourinterfacebehavescorrectly,buildandruntheapplicationonthesimulator.

Makingconnections

Aconnectionletsoneobjectknowwhereanotherobjectisinmemorysothatthetwoobjectscancommunicate.TherearetwokindsofconnectionsthatyoucanmakeinInterfaceBuilder:outletsandactions.Anoutletisareferencetoanobject.Anactionisamethodthatgetstriggeredbyabuttonorsomeotherviewthattheusercaninteractwith,likeasliderorapicker.

Let’sstartbycreatingoutletsthatreferencetheinstancesofUILabel.TimetoleaveInterfaceBuilderandwritesomecode.

Declaringoutlets

Intheprojectnavigator,findandselectthefilenamedViewController.swift.TheeditorareawillchangefromInterfaceBuildertoXcode’scodeeditor.

InViewController.swift,startbydeletinganycodethatthetemplateaddedbetweenclassViewController:UIViewController{and}sothatthefilelookslikethis:importUIKit

classViewController:UIViewController{

}

Next,addthefollowingcodethatdeclarestwoproperties.(Throughoutthisbook,newcodeforyoutoaddwillbeshowninbold.Codeforyoutodeletewillbestruckthrough.)

WOW! eBook www.wowebook.org

Page 62: iOS Programming The Big Nerd Ranch Guide, 5th

Donotworryaboutunderstandingthecodeorpropertiesrightnow;justgetitin.importUIKit

classViewController:UIViewController{@IBOutletvarquestionLabel:UILabel!@IBOutletvaranswerLabel:UILabel!}

ThiscodegiveseveryinstanceofViewControlleranoutletnamedquestionLabelandanoutletnamedanswerLabel.TheviewcontrollercanuseeachoutlettoreferenceaparticularUILabelobject(i.e.,oneofthelabelsinyourview).The@IBOutletkeywordtellsXcodethatyouwillconnecttheseoutletstolabelobjectsusingInterfaceBuilder.

Settingoutlets

Intheprojectnavigator,selectMain.storyboardtoreopenInterfaceBuilder.

YouwantthequestionLabeloutlettopointtotheinstanceofUILabelatthetopoftheuserinterface.

Inthedocumentoutline,findtheViewControllerScenesectionandtheViewControllerobjectwithinit.Inyourcase,theViewControllerstandsinforaninstanceofViewController,whichistheobjectresponsibleformanagingtheinterfacedefinedinMain.storyboard.

Control-drag(orright-clickanddrag)fromtheViewControllerinthedocumentoutlinetothetoplabelinthescene.Whenthelabelishighlighted,releasethemouseandkeyboard;ablackpanelwillappear.SelectquestionLabeltosettheoutlet,asshowninFigure1.21.

Figure1.21SettingquestionLabel

(IfyoudonotseequestionLabelintheconnectionspanel,double-checkyourViewController.swiftfilefortypos.)

WOW! eBook www.wowebook.org

Page 63: iOS Programming The Big Nerd Ranch Guide, 5th

Now,whenthestoryboardfileisloaded,theViewController’squestionLabeloutletwillautomaticallyreferencetheinstanceofUILabelatthetopofthescreen,whichwillallowtheViewControllertotellthelabelwhatquestiontodisplay.

SettheanswerLabeloutletthesameway:Control-dragfromtheViewControllertothebottomUILabelandselectanswerLabel(Figure1.22).

Figure1.22SettinganswerLabel

Noticethatyoudragfromtheobjectwiththeoutletthatyouwanttosettotheobjectthatyouwantthatoutlettopointto.

Youroutletsareallset.Thenextconnectionsyouneedtomakeinvolvethetwobuttons.

Definingactionmethods

WhenaUIButtonistapped,itcallsamethodonanotherobject.Thatobjectiscalledthetarget.Themethodthatistriggerediscalledtheaction.Thisactionisthenameofthemethodthatcontainsthecodetobeexecutedinresponsetothebuttonbeingtapped.

Inyourapplication,thetargetforbothbuttonswillbetheinstanceofViewController.Eachbuttonwillhaveitsownaction.Let’sstartbydefiningthetwoactionmethods:showNextQuestion(_:)andshowAnswer(_:).

ReopenViewController.swiftandaddthetwoactionmethodsaftertheoutlets.classViewController:UIViewController{

@IBOutletvarquestionLabel:UILabel!@IBOutletvaranswerLabel:UILabel!

@IBActionfuncshowNextQuestion(sender:AnyObject){

}

@IBActionfuncshowAnswer(sender:AnyObject){

}}

WOW! eBook www.wowebook.org

Page 64: iOS Programming The Big Nerd Ranch Guide, 5th

Youwillfleshoutthesemethodsafteryoumakethetargetandactionconnections.The@IBActionkeywordtellsXcodethatyouwillbemakingtheseconnectionsinInterfaceBuilder.

Settingtargetsandactions

SwitchbacktoMain.storyboard.Let’sstartwiththeNextQuestionbutton.YouwantitstargettobeViewControlleranditsactiontobeshowNextQuestion(_:).

Tosetanobject’starget,youControl-dragfromtheobjecttoitstarget.Whenyoureleasethemouse,thetargetisset,andapop-upmenuappearsthatletsyouselectanaction.

SelecttheNextQuestionbuttoninthecanvasandControl-dragtotheViewController.WhentheViewControllerishighlighted,releasethemousebuttonandchooseshowNextQuestion:underSentEventsinthepop-upmenu,asshowninFigure1.23.

Figure1.23SettingNextQuestiontarget/action

NowfortheShowAnswerbutton.SelectthebuttonandControl-dragfromthebuttontotheViewController.ThenchooseshowAnswer:fromthepop-upmenu.

Summaryofconnections

TherearenowfiveconnectionsbetweentheViewControllerandtheviewobjects.YouhavesetthepropertiesanswerLabelandquestionLabeltoreferencethelabelobjects–twoconnections.TheViewControlleristhetargetforbothbuttons–twomore.Theproject’stemplatemadeoneadditionalconnection:theviewpropertyofViewControllerisconnectedtotheViewobjectthatrepresentsthebackgroundoftheapplication.Thatmakesfive.

WOW! eBook www.wowebook.org

Page 65: iOS Programming The Big Nerd Ranch Guide, 5th

Youcanchecktheseconnectionsintheconnectionsinspector.SelecttheViewControllerinthedocumentoutline.Then,intheutilitiesarea,clickthe tabtorevealtheconnectionsinspector(Figure1.24).

Figure1.24Checkingconnectionsintheinspector

Yourstoryboardfileiscomplete.Theviewobjectshavebeencreatedandconfiguredandallthenecessaryconnectionshavebeenmadetothecontrollerobject.Let’smoveontocreatingandconnectingyourmodelobjects.

WOW! eBook www.wowebook.org

Page 66: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 67: iOS Programming The Big Nerd Ranch Guide, 5th

CreatingtheModelLayerViewobjectsmakeuptheuserinterface,sodeveloperstypicallycreate,configure,andconnectviewobjectsusingInterfaceBuilder.Thepartsofthemodellayer,ontheotherhand,aretypicallysetupincode.

Intheprojectnavigator,selectViewController.swift.Addthefollowingcodethatdeclarestwoarraysofstringsandaninteger.classViewController:UIViewController{

@IBOutletvarquestionLabel:UILabel!@IBOutletvaranswerLabel:UILabel!

letquestions:[String]=["Fromwhatiscognacmade?","Whatis7+7?","WhatisthecapitalofVermont?"]letanswers:[String]=["Grapes","14","Montpelier"]varcurrentQuestionIndex:Int=0

...}

Thearrayswillbeorderedlistscontainingquestionsandanswers.Theintegerwillkeeptrackofwhatquestiontheuserison.

Noticethatthearraysaredeclaredusingtheletkeyword,whereastheintegerisdeclaredusingthevarkeyword.Aconstantisdenotedwiththeletkeyword;itsvaluecannotchange.Thequestionsandanswersarraysareconstants.Thequestionsandanswersinthisquizwillnotchangeand,infact,cannotbechangedfromtheirinitialvalues.

Avariable,ontheotherhand,isdenotedbythevarkeyword;itsvalueisallowedtochange.YoumadethecurrentQuestionIndexpropertyavariablebecauseitsvaluemustbeabletochangeastheusercyclesthroughthequestionsandanswers.

Implementingactionmethods

Nowthatyouhavequestionsandanswers,youcanfinishimplementingtheactionmethods.InViewController.swift,updateshowNextQuestion(_:)andshowAnswer(_:)....@IBActionfuncshowNextQuestion(sender:AnyObject){++currentQuestionIndexifcurrentQuestionIndex==questions.count{currentQuestionIndex=0}

letquestion:String=questions[currentQuestionIndex]questionLabel.text=questionanswerLabel.text="???"}

@IBActionfuncshowAnswer(sender:AnyObject){letanswer:String=answers[currentQuestionIndex]answerLabel.text=answer}

WOW! eBook www.wowebook.org

Page 68: iOS Programming The Big Nerd Ranch Guide, 5th

Loadingthefirstquestion

Justaftertheapplicationislaunched,youwillwanttoloadthefirstquestionfromthearrayanduseittoreplacethe???placeholderinthequestionLabellabel.AgoodwaytodothisisbyoverridingtheviewDidLoad()methodofViewController.(“Override”meansthatyouareprovidingacustomimplementationforamethod.)AddthemethodtoViewController.swift.classViewController:UIViewController{...overridefuncviewDidLoad(){super.viewDidLoad()questionLabel.text=questions[currentQuestionIndex]}}

Allthecodeforyourapplicationisnowcomplete!

WOW! eBook www.wowebook.org

Page 69: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 70: iOS Programming The Big Nerd Ranch Guide, 5th

BuildingtheFinishedApplicationBuildandruntheapplicationontheiPhone6ssimulator,asyoudidearlier.

Ifbuildingturnsupanyerrors,youcanviewthemintheissuenavigatorbyselectingthetabinthenavigatorarea(Figure1.25).

Figure1.25Issuenavigatorwithexampleerrorsandwarnings

Clickonanyerrororwarningintheissuenavigatortobetakentothefileandthelineofcodewheretheissueoccurred.Findandfixanyproblems(i.e.,codetypos!)bycomparingyourcodewiththecodeinthischapter.Thentryrunningtheapplicationagain.Repeatthisprocessuntilyourapplicationcompiles.

Onceyourapplicationhascompiled,itwilllaunchintheiOSsimulator.PlayaroundwiththeQuizapplication.YoushouldbeabletotaptheNextQuestionbuttonandseeanewquestioninthetoplabel;tappingShowAnswershouldshowtherightanswer.Ifyourapplicationisnotworkingasexpected,double-checkyourconnectionsinMain.storyboard.

YouhavebuiltaworkingiOSapp!Takeamomenttobaskintheglory.

OK,enoughbasking.Yourappworks,butitneedssomespitandpolish.

WOW! eBook www.wowebook.org

Page 71: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 72: iOS Programming The Big Nerd Ranch Guide, 5th

ApplicationIconsWhilerunningQuiz,selectHardware→Homefromthesimulator’smenu.YouwillseethatQuiz’siconisaboring,defaulttile.Let’sgiveQuizabettericon.

AnapplicationiconisasimpleimagethatrepresentstheapplicationontheiOSHomescreen.Differentdevicesrequiredifferent-sizedicons,someofwhichareshowninTable1.1.

Table1.1Applicationiconsizesbydevice

Device Applicationiconsizes

iPhone6sPlusandiPhone6Plus 180x180pixels(@3x)

iPhone6s,iPhone6,andiPhone5 120x120pixels(@2x)

iPadandiPadmini 152x152pixels(@2x)

iPadPro 167x167pixels(@2x)

Wehavepreparedaniconimagefile(size120x120)fortheQuizapplication.Youcandownloadthisicon(alongwithresourcesforotherchapters)fromhttp://www.bignerdranch.com/solutions/iOSProgramming5ed.zip.UnzipiOSProgramming5ed.zipandfindtheIcon@2x.pngfileintheResourcesdirectoryoftheunzippedfolder.

Youaregoingtoaddthisicontoyourapplicationbundleasaresource.Ingeneral,therearetwokindsoffilesinanapplication:codeandresources.Code(likeViewController.swift)isusedtocreatetheapplicationitself.Resourcesarethingslikeimagesandsoundsthatareusedbytheapplicationatruntime.

Intheprojectnavigator,findAssets.xcassets.SelectthisfiletoopenitandthenselectAppIconfromtheresourcelistonthelefthandside(Figure1.26).

Figure1.26ShowingtheAssetCatalog

WOW! eBook www.wowebook.org

Page 73: iOS Programming The Big Nerd Ranch Guide, 5th

ThispanelistheAssetCatalog,whereyoucanmanagealloftheimagesthatyourapplicationwillneed.

DragtheIcon@2x.pngfilefromFinderontothemarginsoftheAppIconsection.Thiswillcopythefileintoyourproject’sdirectoryonthefilesystemandaddareferencetothatfileintheAssetCatalog.(YoucanControl-clickonafileintheAssetCatalogandselecttheoptiontoShowinFindertoconfirmthis,asshowninFigure1.27.)

Figure1.27AddingtheappicontotheAssetCatalog

Buildandruntheapplicationagain.Switchtothesimulator’sHomescreeneitherbyclickingHome→Hardware,asyoudidbefore,orbyusingthekeyboardshortcutCommand-Shift-H.Youshouldseethenewicon.

(Ifyoudonotseetheicon,deletetheapplicationandthenbuildandrunagaintoredeployit.Todothis,theeasiestoptionistoresetthesimulatorbyclickingiOSSimulator→ResetContentandSettings….Thiswillremoveallapplicationsandresetthesimulatortoitsdefaultsettings.Youshouldseetheappiconthenexttimeyouruntheapplication.)

WOW! eBook www.wowebook.org

Page 74: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 75: iOS Programming The Big Nerd Ranch Guide, 5th

LaunchScreenAnotheritemyoushouldsetfortheprojectisthelaunchimage,whichappearswhileanapplicationisloading.ThelaunchimagehasaspecificroleiniOS:itconveystotheuserthattheapplicationisindeedlaunchinganddepictstheuserinterfacethattheuserwillinteractwithoncetheapplicationloads.Therefore,agoodlaunchimageisacontent-lessscreenshotoftheapplication.Forexample,theClockapplication’slaunchimageshowsthefourtabsalongthebottom,allintheunselectedstate.Oncetheapplicationloads,thecorrecttabisselectedandthecontentbecomesvisible.(Keepinmindthatthelaunchimageisreplacedaftertheapplicationhaslaunched;itdoesnotbecomethebackgroundimageoftheapplication.)

AneasywaytoaccomplishthisistoallowXcodetogeneratethepossiblelaunchscreenimagesforyouusingalaunchscreenfile.

Opentheprojectsettingsbyclickingonthetop-levelQuizintheprojectnavigator.UnderAppIconsandLaunchImages,chooseMain.storyboardfromtheLaunchScreenFiledropdown(Figure1.28).LaunchimageswillnowbegeneratedfromMain.storyboard.

Figure1.28Settingthelaunchscreenfile

Congratulations!Youhavewrittenyourfirstapplicationandevenaddedsomedetailstomakeitpolished.

WOW! eBook www.wowebook.org

Page 76: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 77: iOS Programming The Big Nerd Ranch Guide, 5th

2TheSwiftLanguage

SwiftisanewlanguagethatAppleintroducedin2014.ItreplacesObjective-CastherecommendeddevelopmentlanguageforiOSandMac.Inthischapter,youaregoingtofocusonthebasicsofSwift.Youwillnotlearneverything,butyouwilllearnenoughtogetstarted.Then,asyoucontinuethroughthebook,youwilllearnmoreSwiftwhileyoulearniOSdevelopment.

SwiftmaintainstheexpressivenessofObjective-Cwhileintroducingasyntaxthatissafer,succinct,andreadable.Itemphasizestypesafetyandintroducesadvancedfeaturessuchasoptionals,generics,andsophisticatedstructuresandenumerations.Mostimportantly,Swiftallowstheuseofthesenewfeatureswhilerelyingonthesametested,elegantiOSframeworksthatdevelopershavebuiltuponforyears.

IfyouknowObjective-C,thenthechallengeisrecastingwhatyouknow.Itmayseemawkwardatfirst,butwehavecometoloveSwiftatBigNerdRanchandbelieveyouwill,too.

IfyoudonotthinkyouwillbecomfortablepickingupSwiftatthesametimeasiOSdevelopment,youmaywanttostartwithSwiftProgramming:TheBigNerdRanchGuideorApple’sSwifttutorials,whichyoucanfindathttp://developer.apple.com/swift.Butifyouhavesomeprogrammingexperienceandarewillingtolearn“onthejob,”youcanstartyourSwifteducationhereandnow.

WOW! eBook www.wowebook.org

Page 78: iOS Programming The Big Nerd Ranch Guide, 5th

TypesinSwiftSwifttypescanbearrangedintothreebasicgroups:structures,classes,andenumerations(Figure2.1).Allthreecanhave

properties:valuesassociatedwithatype

initializers:codethatinitializesaninstanceofatype

instancemethods:functionsspecifictoatypethatcanbecalledonaninstanceofthattype

classorstaticmethods:functionsspecifictoatypethatcanbecalledonthetypeitself

Figure2.1Swiftbuildingblocks

Swift’sstructures(or“structs”)andenumerations(or“enums”)aresignificantlymorepowerfulthaninmostlanguages.Inadditiontosupportingproperties,initializers,andmethods,theycanalsoconformtoprotocolsandcanbeextended.

Swift’simplementationoftypically“primitive”typessuchasnumbersandBooleanvaluesmaysurpriseyou:theyareallstructures.Infact,alloftheseSwifttypesarestructures:

Numbers: Int,Float,Double

Boolean: Bool

Text: String,Character

Collections: Array<T>,Dictionary<K:Hashable,V>,Set<T:Hashable>

Thismeansthatstandardtypeshaveproperties,initializers,andmethodsoftheirown.Theycanalsoconformtoprotocolsandbeextended.

Finally,akeyfeatureofSwiftisoptionals.Anoptionalallowsyoutostoreeitheravalueofaparticulartypeornovalueatall.Laterinthechapter,youwilllearnmoreaboutoptionalsandtheirroleinSwift.

WOW! eBook www.wowebook.org

Page 79: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 80: iOS Programming The Big Nerd Ranch Guide, 5th

UsingStandardTypesInthissection,youaregoingtoexperimentwithstandardtypesinanXcodeplayground.Aplaygroundletsyouwritecodeandseetheresultswithouttheoverheadofmanuallyrunninganapplicationandcheckingtheoutput.

InXcode,selectFile→New→Playground….Youcanacceptthedefaultnameforthisfile;youwillonlybeherebriefly.MakesuretheplatformisiOS(Figure2.2).

Figure2.2Configuringaplayground

ClickNextandsavethisfileinaconvenientplace.

Whenthefileopens,noticethattheplaygroundisdividedintotwosections(Figure2.3).Thelargerwhiteareatotheleftistheeditorwhereyouwritecode.Thegraycolumnontherightisthesidebar.Theplaygroundcompilesandexecutesyourcodeaftereverylineandshowstheresultsinthesidebar.

Figure2.3Aplayground

WOW! eBook www.wowebook.org

Page 81: iOS Programming The Big Nerd Ranch Guide, 5th

Intheexamplecode,thevarkeyworddenotesavariable,sothevalueofstrcanbechangedfromitsinitialvalue.Typeinthecodebelowtochangethevalueofstr,andyouwillseetheresultsappearinthesidebartotheright.varstr="Hello,playground""Hello,playground"str="Hello,Swift""Hello,Swift"

(Noticethatweareshowingsidebarresultstotherightofthecodeforthebenefitofreaderswhoarenotactivelydoingtheexercise.)

Theletkeyworddenotesaconstantvalue,whichcannotbechanged.InyourSwiftcode,youshoulduseletunlessyouexpectthevaluewillneedtochange.Addaconstanttothemix:varstr="Hello,playground""Hello,playground"str="Hello,Swift""Hello,Swift"letconstStr=str"Hello,Swift"

BecauseconstStrisaconstant,attemptingtochangeitsvaluewillcauseanerror.varstr="Hello,playground""Hello,playground"str="Hello,Swift""Hello,Swift"letconstStr=str"Hello,Swift"constStr="Hello,world"

Anerrorappears,indicatedbytheredsymboltotheleftoftheoffendingline.Clickthesymboltogetmoreinformationabouttheerror.Inthiscase,theerrorreads“Cannotassigntovalue:‘constStr’isa‘let’constant.”

Anerrorintheplaygroundcodewillpreventyoufromseeinganyfurtherresultsinthesidebar,soyouusuallywanttoaddressitrightaway.RemovethelinethatattemptstochangethevalueofconstStr.varstr="Hello,playground""Hello,playground"str="Hello,Swift""Hello,Swift"letconstStr=str"Hello,Swift"constStr="Hello,world"

Inferringtypes

Atthispoint,youmayhavenoticedthatneithertheconstStrconstantnorthestrvariablehasaspecifiedtype.Thisdoesnotmeantheyareuntyped!Instead,thecompilerinferstheirtypesfromtheinitialvalues.Thisiscalledtypeinference.

YoucanfindoutwhattypewasinferredusingQuickHelp.Option-clickonconstStrtoseetheQuickHelpinformationforthisconstant,showninFigure2.4.

WOW! eBook www.wowebook.org

Page 82: iOS Programming The Big Nerd Ranch Guide, 5th

Figure2.4constStrisoftypeString

Option-clickingtorevealQuickHelpwillworkforanysymbol.

Specifyingtypes

Ifyourconstantorvariablehasaninitialvalue,youcanrelyontypeinference.Ifaconstantorvariabledoesnothaveaninitialvalueorifyouwanttoensurethatitisacertaintype,youcanspecifythetypeinthedeclaration.

Addmorevariableswithspecifiedtypes:varstr="Hello,playground""Hello,playground"str="Hello,Swift""Hello,Swift"letconstStr=str"Hello,Swift"

varnextYear:IntvarbodyTemp:FloatvarhasPet:Bool

Notethatthesidebardoesnotreportanyresultsbecausethesevariablesdonotyethavevalues.

Let’sgooverthesenewtypesandhowtheyareused.

NumberandBooleantypes

ThemostcommontypeforintegersisInt.Thereareadditionalintegertypesbasedonwordsizeandsignedness,butApplerecommendsusingIntunlessyoureallyhaveareasontousesomethingelse.

Forfloating-pointnumbers,Swiftprovidesthreetypeswithdifferentlevelsofprecision:Floatfor32-bitnumbers,Doublefor64-bitnumbers,andFloat80for80-bitnumbers.

ABooleanvalueisexpressedinSwiftusingthetypeBool.ABool’svalueiseithertrueorfalse.

Collectiontypes

TheSwiftstandardlibraryoffersthreecollections:arrays,dictionaries,andsets.

WOW! eBook www.wowebook.org

Page 83: iOS Programming The Big Nerd Ranch Guide, 5th

Anarrayisanorderedcollectionofelements.ThearraytypeiswrittenasArray<T>,whereTisthetypeofelementthatthearraywillcontain.Arrayscancontainelementsofanytype:astandardtype,astructure,oraclass.

Addavariableforanarrayofintegers:varhasPet:BoolvararrayOfInts:Array<Int>

Arraysarestronglytyped.Onceyoudeclareanarrayascontainingelementsof,say,Int,youcannotaddaStringtothisarray.

Thereisashorthandsyntaxfordeclaringarrays:youcansimplyusesquarebracketsaroundthetypethatthearraywillcontain.UpdatethedeclarationofarrayOfIntstousetheshorthand:varhasPet:BoolvararrayOfInts:Array<Int>vararrayOfInts:[Int]

Adictionaryisanunorderedcollectionofkey-valuepairs.Thevaluescanbeofanytype,includingstructuresandclasses.Thekeyscanbeofanytypeaswell,buttheymustbeunique.Specifically,thekeysmustbehashable,whichallowsthedictionarytoguaranteethatthekeysareuniqueaswellasaccessthevalueforagivenkeymoreefficiently.BasicSwifttypessuchasInt,Float,Character,andStringareallhashable.

LikeSwiftarrays,Swiftdictionariesarestronglytypedandcanonlycontainkeysandvaluesofthedeclaredtype.Forexample,youmighthaveadictionarythatstorescapitalcitiesbycountry.Thekeysforthisdictionarywouldbethecountrynames,andthevalueswouldbethecitynames.Bothkeysandvalueswouldbestrings,andyouwouldnotbeabletoaddakeyorvalueofanyothertype.

Addavariableforsuchadictionary:vararrayOfInts:[Int]vardictionaryOfCapitalsByCountry:Dictionary<String,String>

Thereisashorthandsyntaxfordeclaringdictionaries,too.UpdatedictionaryOfCapitalsByCountrytousetheshorthand:vararrayOfInts:[Int]vardictionaryOfCapitalsByCountry:Dictionary<String,String>vardictionaryOfCapitalsByCountry:[String:String]

Asetissimilartoanarrayinthatitcontainsanumberofelementsofacertaintype.However,setsareunordered,andthemembersmustbeuniqueaswellashashable.Theunorderednessofsetsmakesthemfasterwhenyousimplyneedtodeterminewhethersomethingisamemberofaset.Addavariableforaset:varwinningLotteryNumbers:Set<Int>

Literalsandsubscripting

Standardtypescanbeassignedliteralvalues,orliterals.Forexample,strisassignedthevalueofastringliteral.Astringliteralisformedwithdoublequotes.ContrasttheliteralvalueassignedtostrwiththenonliteralvalueassignedtoconstStr:

WOW! eBook www.wowebook.org

Page 84: iOS Programming The Big Nerd Ranch Guide, 5th

varstr="Hello,playground""Hello,playground"str="Hello,Swift""Hello,Swift"letconstStr=str"Hello,Swift"

Addtwonumberliteralstoyourplayground:letnumber=4242letfmStation=91.191.1

Arraysanddictionariescanbeassignedliteralvaluesaswell.Thesyntaxforcreatingliteralarraysanddictionariesresemblestheshorthandsyntaxforspecifyingthesetypes.letcountingUp=["one","two"]["one","two"]letnameByParkingSpace=[13:"Alice",27:"Bob"][13:"Alice",27:"Bob"]

Swiftalsoprovidessubscriptingasshorthandforaccessingarrays.Toretrieveanelementinanarray,youprovidetheelement’sindexinsquarebracketsafterthearrayname.letcountingUp=["one","two"]["one","two"]letsecondElement=countingUp[1]"two"...

Noticethatindex1retrievesthesecondelement;anarray’sindexalwaysstartsat0.

Whensubscriptinganarray,besurethatyouareusingavalidindex.Attemptingtoaccessanout-of-boundsindexresultsinatrap.Atrapisaruntimeerrorthatstopstheprogrambeforeitgetsintoanunknownstate.

Subscriptingalsoworkswithdictionaries–moreonthatlaterinthischapter.

Initializers

Sofar,youhaveinitializedyourconstantsandvariablesusingliteralvalues.Indoingso,youcreatedinstancesofaspecifictype.Aninstanceisaparticularembodimentofatype.Historically,thistermhasbeenonlyusedwithclasses,butinSwiftitisusedtodescribestructuresandenumerations,too.Forexample,theconstantsecondElementholdsaninstanceofString.

Anotherwayofcreatinginstancesisbyusinganinitializeronthetype.Initializersareresponsibleforinitializingthecontentsofanewinstanceofatype.Whenaninitializerisfinished,theinstanceisreadyforaction.Tocreateanewinstanceusinganinitializer,youusethetypenamefollowedbyapairofparenthesesand,ifrequired,arguments.Thissignature–thecombinationoftypeandarguments–correspondstoaspecificinitializer.

Somestandardtypeshaveinitializersthatreturnemptyliteralswhennoargumentsaresupplied.Addanemptystringandanemptyarraytoyourplayground.letemptyString=String()""letemptyArrayOfInts=[Int]()0elementsletemptySetOfFloats=Set<Float>()0elements

Othertypeshavedefaultvalues:letdefaultNumber=Int()0letdefaultBool=Bool()false

Typescanhavemultipleinitializers.Forexample,StringhasaninitializerthatacceptsanIntandcreatesastringbasedonthatvalue.letnumber=4242

WOW! eBook www.wowebook.org

Page 85: iOS Programming The Big Nerd Ranch Guide, 5th

letmeaningOfLife=String(number)"42"

Tocreateaset,youusetheSetinitializerthatacceptsanarrayliteral:letavailableRooms=Set([205,411,412]){412,205,411}

Floathasseveralinitializers.Theparameter-lessinitializerreturnsaninstanceofFloatwiththedefaultvalue.Thereisalsoaninitializerthatacceptsafloating-pointliteral.letdefaultFloat=Float()0.0letfloatFromLiteral=Float(3.14)3.14

Ifyouusetypeinferenceforafloating-pointliteral,thetypedefaultstoDouble.Createthefollowingconstantwithafloating-pointliteral:leteasyPi=3.143.14

UsetheFloatinitializerthatacceptsaDoubletocreateaFloatfromthisDouble:leteasyPi=3.143.14letfloatFromDouble=Float(easyPi)3.14

Youcanachievethesameresultbyspecifyingthetypeinthedeclaration.leteasyPi=3.143.14letfloatFromDouble=Float(easyPi)3.14letfloatingPi:Float=3.143.14

Properties

Apropertyisavalueassociatedwithaninstanceofatype.Forexample,StringhasthepropertyisEmpty,whichisaBoolthattellsyouwhetherthestringisempty.Array<T>hasthepropertycount,whichisthenumberofelementsinthearrayasanInt.Accessthesepropertiesinyourplayground:letcountingUp=["one","two"]["one","two"]letsecondElement=countingUp[1]"two"countingUp.count2

...

letemptyString=""emptyString.isEmptytrue

Instancemethods

Aninstancemethodisafunctionthatisspecifictoaparticulartypeandcanbecalledonaninstanceofthattype.Tryouttheappend(_:)instancemethodfromArray<T>.YouwillfirstneedtochangeyourcountingUparrayfromaconstanttoavariable.letcountingUp=["one","two"]varcountingUp=["one","two"]["one","two"]letsecondElement=countingUp[1]"two"countingUp.count

countingUp.append("three")["one","two","three"]

Theappend(_:)methodacceptsanelementofthearray’stypeandaddsittotheendofthearray.Wewilldiscussmethods,includingnaming,inChapter3.

WOW! eBook www.wowebook.org

Page 86: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 87: iOS Programming The Big Nerd Ranch Guide, 5th

OptionalsSwifttypescanbeoptional,whichisindicatedbyappending?toatypename.varanOptionalFloat:Float?varanOptionalArrayOfStrings:[String]?varanOptionalArrayOfOptionalStrings:[String?]?

Anoptionalletsyouexpressthepossibilitythatavariablemaynotstoreavalueatall.Thevalueofanoptionalwilleitherbeaninstanceofthespecifiedtypeornil.

Throughoutthisbook,youwillhavemanychancestouseoptionals.Whatfollowsisanexampletogetyoufamiliarwiththesyntaxsothatyoucanfocusontheuseoftheoptionalslater.

Imagineagroupofinstrumentreadings:varreading1:Floatvarreading2:Floatvarreading3:Float

Sometimes,aninstrumentmightmalfunctionandnotreportareading.Youdonotwantthismalfunctionshowingupas,say,0.0.Youwantittobesomethingcompletelydifferentthattellsyoutocheckyourinstrumentortakesomeotheraction.

Youcandothisbydeclaringthereadingsasoptionals.varreading1:Float?nilvarreading2:Float?nilvarreading3:Float?nil

Asanoptionalfloat,eachreadingcancontaineitheraFloatornil.Ifnotgivenaninitialvalue,thenthevaluedefaultstonil.

Youcanassignvaluestoanoptionaljustlikeanyothervariable.Assignfloating-pointliteralstothereadings:reading1=9.89.8reading2=9.29.2reading3=9.79.7

However,youcannotusetheseoptionalfloatslikenon-optionalfloats–eveniftheyhavebeenassignedFloatvalues.Beforeyoucanreadthevalueofanoptionalvariable,youmustaddressthepossibilityofitsvaluebeingnil.Thisiscalledunwrappingtheoptional.

Youaregoingtotryouttwowaysofunwrappinganoptionalvariable:optionalbindingandforcedunwrapping.Youwillimplementforcedunwrappingfirst.Thisisnotbecauseitisthebetteroption–infact,itisthelesssafeone.Butimplementingforcedunwrappingfirstwillletyouseethedangersandunderstandwhyoptionalbindingistypicallybetter.

Toforciblyunwrapanoptional,youappenda!toitsname.First,tryaveragingthereadingsasiftheywerenon-optionalvariables:reading1=9.89.8reading2=9.29.2reading3=9.79.7letavgReading=(reading1+reading2+reading3)/3

WOW! eBook www.wowebook.org

Page 88: iOS Programming The Big Nerd Ranch Guide, 5th

Thisresultsinanerrorbecauseoptionalsrequireunwrapping.Forciblyunwrapthereadingstofixtheerror:letavgReading=(reading1+reading2+reading3)/3letavgReading=(reading1!+reading2!+reading3!)/39.566667

Everythinglooksfine,andyouseethecorrectaverageinthesidebar.Butadangerlurksinyourcode.Whenyouforciblyunwrapanoptional,youtellthecompilerthatyouaresurethattheoptionalwillnotbenilandcanbetreatedasifitwereanormalFloat.Butwhatifyouarewrong?Tofindout,commentouttheassignmentofreading3,whichwillreturnittoitsdefaultvalue,nil.reading1=9.89.8reading2=9.29.2reading3=9.7//reading3=9.7

Younowhaveanerror.Toseetheconsolewheretheerrorisreportedinaplayground,youmustopenthedebugarea.FromXcode’sViewmenu,selectDebugArea→ShowDebugArea.Theerrorreads:fatalerror:unexpectedlyfoundnilwhileunwrappinganOptionalvalue

Ifyouforciblyunwrapanoptionalandthatoptionalturnsouttobenil,itwillcauseatrap,stoppingyourapplication.

Asaferwaytounwrapanoptionalisoptionalbinding.Optionalbindingworkswithinaconditionalif-letstatement:Youassigntheoptionaltoatemporaryconstantofthecorrespondingnon-optionaltype.Ifyouroptionalhasavalue,thentheassignmentisvalidandyouproceedusingthenon-optionalconstant.Iftheoptionalisnil,thenyoucanhandlethatcasewithanelseclause.

Changeyourcodetouseanif-letstatementthattestsforvalidvaluesinallthreereadings.letavgReading=(reading1!+reading2!+reading3!)/3ifletr1=reading1,r2=reading2,r3=reading3{letavgReading=(r1+r2+r3)/3}else{leterrorString="Instrumentreportedareadingthatwasnil."}

reading3iscurrentlynil,soitsassignmenttor3fails,andthesidebarshowstheerrorstring.

Toseetheothercaseinaction,restorethelinethatassignsavaluetoreading3.Nowthatallthreereadingshavevalues,allthreeassignmentsarevalid,andthesidebarupdatestoshowtheaverageofthethreereadings.

Subscriptingdictionaries

Recallthatsubscriptinganarraybeyonditsboundscausesatrap.Dictionariesaredifferent.Theresultofsubscriptingadictionaryisanoptional:letnameByParkingSpace=[13:"Alice",27:"Bob"][13:"Alice",27:"Bob"]letspace13Assignee:String?=nameByParkingSpace[13]"Alice"

WOW! eBook www.wowebook.org

Page 89: iOS Programming The Big Nerd Ranch Guide, 5th

letspace42Assignee:String?=nameByParkingSpace[42]nil

Ifthekeyisnotinthedictionary,theresultwillbenil.Aswithotheroptionals,itiscommontouseif-letwhensubscriptingadictionary:letspace13Assignee:String?=nameByParkingSpace[13]ifletspace13Assignee=nameByParkingSpace[13]{print("Key13isassignedinthedictionary!")}

WOW! eBook www.wowebook.org

Page 90: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 91: iOS Programming The Big Nerd Ranch Guide, 5th

LoopsandStringInterpolationSwifthasallthecontrolflowstatementsthatyoumaybefamiliarwithfromotherlanguages:if-else,while,for,for-in,repeat-while,andswitch.Eveniftheyarefamiliar,however,theremaybesomedifferencesfromwhatyouareaccustomedto.ThekeydifferencebetweenthesestatementsinSwiftandinC-likelanguagesisthatwhileenclosingparenthesesarenotnecessaryonthesestatements’expressions,Swiftdoesrequirebracesonclauses.Additionally,theexpressionsforifandwhile-likestatementsmustevaluatetoaBool.

ConsiderthisverytraditionalC-styleloop,writteninSwift:forvari=0;i<countingUp.count;++i{letstring=countingUp[i]//Use'string'}

YoucoulddothesamethingalittlemorecleanlyusingSwift’sRangetypeandthefor-instatement:letrange=0..<countingUp.countforiinrange{letstring=countingUp[i]//Use'string'}

Themostdirectroutewouldbetoenumeratetheitemsinthearraythemselves:forstringincountingUp{//Use'string'}

Whatifyouwantedtheindexofeachiteminthearray?Swift’senumerate()functionreturnsasequenceofintegersandvaluesfromitsargument:for(i,string)incountingUp.enumerate(){//(0,"one"),(1,"two")}

Whatarethoseparentheses,youask?Theenumerate()functionactuallyreturnsasequenceoftuples.Atupleisanorderedgroupingofvaluessimilartoanarray,excepteachmembermayhaveadistincttype.Inthisexamplethetupleisoftype(Int,String).WewillnotspendmuchtimeontuplesinthisbookastheyarenotusediniOSAPIs(becauseObjective-Cdoesnotsupporttuples).TheycanbeusefulinyourSwiftcode,however.

Anotherapplicationoftuplesisinenumeratingthecontentsofadictionary:letnameByParkingSpace=[13:"Alice",27:"Bob"]

for(space,name)innameByParkingSpace{letpermit="Space\(space):\(name)"}

Didyounoticethatcuriousmarkupinthestringliteral?ThatisSwift’sstringinterpolation.Expressionsenclosedwithin\(and)areevaluatedandinsertedintothestringatruntime.Inthisexampleyouareusinglocalvariables,butanyvalidSwiftexpression,suchasamethodcall,canbeused.

WOW! eBook www.wowebook.org

Page 92: iOS Programming The Big Nerd Ranch Guide, 5th

Toseethevaluesofthepermitvariableintheplayground,Control-clickontheresultandselectValueHistory.Ifyoudonotseeavalue,clickonthecircularShowResultindicator(Figure2.5).Thiscanbeveryusefulforvisualizingwhatishappeninginyourplaygroundcode’sloops.

Figure2.5UsingtheValueHistorytoseetheresultsofstringinterpolation

WOW! eBook www.wowebook.org

Page 93: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 94: iOS Programming The Big Nerd Ranch Guide, 5th

EnumerationsandtheSwitchStatementAnenumerationisatypewithadiscretesetofvalues.Defineanenumdescribingpies:enumPieType{caseApplecaseCherrycasePecan}

letfavoritePie=PieType.Apple

Swifthasapowerfulswitchstatementthat,amongotherthings,isgreatformatchingonenumvalues:letname:StringswitchfavoritePie{case.Apple:name="Apple"case.Cherry:name="Cherry"case.Pecan:name="Pecan"}

Thecasesforaswitchstatementmustbeexhaustive:eachpossiblevalueoftheswitchexpressionmustbeaccountedfor,whetherexplicitlyorviaadefault:case.UnlikeinC,Swiftswitchcasesdonotfallthrough–onlythecodeforthecasethatismatchedisexecuted.(Ifyouneedthefall-throughbehaviorofC,youcanexplicitlyrequestitusingthefallthroughkeyword.)

Switchstatementscanmatchonmanytypes,evenranges:letosxVersion:Int=...switchosxVersion{case0...8:print("Abigcat")case9:print("Mavericks")case10:print("Yosemite")default:print("Greetings,peopleofthefuture!What'snewin10.\(osxVersion)?")}

Formoreontheswitchstatementanditspatternmatchingcapabilities,seetheControlFlowsectionintheTheSwiftProgrammingLanguageguide.(Moreonthatinjustamoment.)

Enumerationsandrawvalues

Swiftenumscanhaverawvaluesassociatedwiththeircases:enumPieType:Int{caseApple=0caseCherrycasePecan}

Withthetypespecified,youcanaskaninstanceofPieTypeforitsrawValueandtheninitializetheenumtypewiththatvalue.Thisreturnsanoptional,sincetherawvaluemaynotcorrespondwithanactualcaseoftheenum,soitisagreatcandidateforoptional

WOW! eBook www.wowebook.org

Page 95: iOS Programming The Big Nerd Ranch Guide, 5th

binding.letpieRawValue=PieType.Pecan.rawValue//pieRawValueisanIntwithavalueof2

ifletpieType=PieType(rawValue:pieRawValue){//Gotavalid'pieType'!}

TherawvalueforanenumisoftenanInt,butitcanbeanyintegerorfloating-pointnumbertypeaswellastheStringandCharactertypes.

Whentherawvalueisanintegertype,thevaluesautomaticallyincrementifnoexplicitvalueisgiven.ForPieType,onlytheApplecaseisgivenanexplicitvalue.CherryandPecanautomaticallygetassignedarawValueof1and2,respectively.

Thereismoretoenumerations.Eachcaseofanenumerationcanhaveassociatedvalues.YouwilllearnmoreaboutassociatedvaluesinChapter19.

WOW! eBook www.wowebook.org

Page 96: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 97: iOS Programming The Big Nerd Ranch Guide, 5th

ExploringApple’sSwiftDocumentationToexploreApple’sdocumentationonSwift,startathttp://developer.apple.com/swift.Herearetwoparticularresourcestolookfor.Wesuggestbookmarkingthemandvisitingthemwhenyouwanttoreviewaparticularconceptordigalittledeeper.

TheSwiftProgrammingLanguage

ThisguidedescribesmanyfeaturesofSwift.Itstartswiththebasicsandincludesexamplecodeandlotsofdetail.ItalsocontainsthelanguagereferenceandformalgrammarofSwift.

TheSwiftStandardLibraryReference

ThestandardlibraryreferencelaysoutthedetailsofSwifttypes,protocols,andglobal,orfree,functions.

YourhomeworkistobrowsethroughtheTypessectionoftheSwiftStandardLibraryReferenceandthesectionsofTheSwiftProgrammingLanguageguideonTheBasics,StringsandCharacters,andCollectionTypes.Solidifywhatyoulearnedinthischapterandbecomefamiliarwiththeinformationtheseresourcesoffer.Ifyouknowwheretofindthedetailswhenyouneedthem,thenyouwillfeellesspressuretomemorizethem–lettingyoufocusoniOSdevelopmentinstead.

WOW! eBook www.wowebook.org

Page 98: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 99: iOS Programming The Big Nerd Ranch Guide, 5th

3ViewsandtheViewHierarchy

Overthenextfivechapters,youaregoingtobuildanapplicationnamedWorldTrotter.Whenitiscomplete,thisappwillconvertvaluesbetweendegreesFahrenheitanddegreesCelsius.Inthischapter,youwilllearnaboutviewsandtheviewhierarchythroughcreatingWorldTrotter’suserinterface.Attheendofthischapter,yourappwilllooklikeFigure3.1.

Figure3.1WorldTrotter

Let’sstartwithalittlebitofthetheorybehindviewsandtheviewhierarchy.

WOW! eBook www.wowebook.org

Page 100: iOS Programming The Big Nerd Ranch Guide, 5th

ViewBasicsRecallfromChapter1thatviewsareobjectsthatarevisibletotheuser,likebuttons,textfields,andsliders.Viewobjectsmakeupanapplication’suserinterface.Aview

isaninstanceofUIVieworoneofitssubclasses

knowshowtodrawitself

canhandleevents,liketouches

existswithinahierarchyofviewswhoserootistheapplication’swindow

Let’slookattheviewhierarchyingreaterdetail.

WOW! eBook www.wowebook.org

Page 101: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 102: iOS Programming The Big Nerd Ranch Guide, 5th

TheViewHierarchyEveryapplicationhasasingleinstanceofUIWindowthatservesasthecontainerforalltheviewsintheapplication.UIWindowisasubclassofUIView,sothewindowisitselfaview.Thewindowiscreatedwhentheapplicationlaunches.Oncethewindowiscreated,otherviewscanbeaddedtoit.

Whenaviewisaddedtothewindow,itissaidtobeasubviewofthewindow.Viewsthataresubviewsofthewindowcanalsohavesubviews,andtheresultisahierarchyofviewobjectswiththewindowatitsroot(Figure3.2).

Figure3.2Anexampleviewhierarchyandtheinterfacethatitcreates

Oncetheviewhierarchyiscreated,itwillbedrawntothescreen.Thisprocesscanbebrokenintotwosteps:

Eachviewinthehierarchy,includingthewindow,drawsitself.Itrendersitselftoitslayer,whichyoucanthinkofasabitmapimage.(ThelayerisaninstanceofCALayer.)

Thelayersofalltheviewsarecompositedtogetheronthescreen.

Figure3.3showsanotherexampleviewhierarchyandthetwodrawingsteps.

WOW! eBook www.wowebook.org

Page 103: iOS Programming The Big Nerd Ranch Guide, 5th

Figure3.3Viewsrenderthemselvesandthenarecompositedtogether

ForWorldTrotter,youaregoingtocreateaninterfacecomposedofdifferentviews.TherewillbefourinstancesofUILabelandoneinstanceofUITextFieldthatwillallowtheusertoenterinatemperatureinFahrenheit.Let’sgetstarted.

WOW! eBook www.wowebook.org

Page 104: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 105: iOS Programming The Big Nerd Ranch Guide, 5th

CreatingaNewProjectInXcode,selectFile→New→Project…(orusethekeyboardshortcutCommand-Shift-N).FromtheiOSsection,selectApplication,choosetheSingleViewApplicationtemplate,andclickNext.

EnterWorldTrotterfortheproductname.MakesurethatSwiftisselectedfromtheLanguagedropdownandthatiPhoneisselectedfromtheDevicesdropdown.AlsomakesuretheUseCoreDataboxisunchecked(Figure3.4).ClickNextandthenCreateonthefollowingscreen.

Figure3.4ConfiguringWorldTrotter

WOW! eBook www.wowebook.org

Page 106: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 107: iOS Programming The Big Nerd Ranch Guide, 5th

ViewsandFramesWhenyouinitializeaviewprogrammatically,youuseitsinit(frame:)designatedinitializer.Thismethodtakesoneargument,aCGRect,thatwillbecometheview’sframe,apropertyonUIView.varframe:CGRect

Aview’sframespecifiestheview’ssizeanditspositionrelativetoitssuperview.Becauseaview’ssizeisalwaysspecifiedbyitsframe,aviewisalwaysarectangle.

ACGRectcontainsthemembersoriginandsize.TheoriginisastructureoftypeCGPointandcontainstwoCGFloatproperties:xandy.ThesizeisastructureoftypeCGSizeandhastwoCGFloatproperties:widthandheight(Figure3.5).

Figure3.5CGRect

Whentheapplicationislaunched,theviewfortheinitialviewcontrollerisaddedtotheroot-levelwindow.ThisviewcontrollerisrepresentedbytheViewControllerclassdefinedinViewController.swift.WewilldiscusswhataviewcontrollerisinChapter5,butfornow,itissufficienttoknowthataviewcontrollerhasaviewandthattheviewassociatedwiththemainviewcontrollerfortheapplicationisaddedasasubviewofthewindow.

BeforeyoucreatetheviewsforWorldTrotter,youaregoingtoaddsomepracticeviewsprogrammaticallytoexploreviewsandtheirpropertiesandseehowtheinterfacesfor

WOW! eBook www.wowebook.org

Page 108: iOS Programming The Big Nerd Ranch Guide, 5th

applicationsarecreated.

OpenViewController.swiftanddeleteanymethodsthatthetemplatecreated.Yourfileshouldlooklikethis:importUIKit

classViewController:UIViewController{

}

(CuriousabouttheimportUIKitline?UIKitisaframework.Aframeworkisacollectionofrelatedclassesandresources.TheUIKitframeworkdefinesmanyoftheuserinterfaceelementsthatyouruserssee,aswellasotheriOS-specificclasses.Youwillbeusingafewdifferentframeworksasyougothroughthisbook.)

Rightaftertheviewcontroller’sviewisloadedintomemory,itsviewDidLoad()methodiscalled.Thismethodgivesyouanopportunitytocustomizetheviewhierarchy,soitisagreatplacetoaddyourpracticeviews.

InViewController.swift,overrideviewDidLoad().CreateaCGRectthatwillbetheframeofaUIView.Next,createaninstanceofUIViewandsetitsbackgroundColorpropertytoblue.Finally,addtheUIViewasasubviewoftheviewcontroller’sviewtomakeitpartoftheviewhierarchy.(Muchofthiswillnotlookfamiliar.Thatisfine.Wewillexplainmoreafteryouenterthecode.)classViewController:UIViewController{

overridefuncviewDidLoad(){super.viewDidLoad()

letfirstFrame=CGRect(x:160,y:240,width:100,height:150)letfirstView=UIView(frame:firstFrame)firstView.backgroundColor=UIColor.blueColor()view.addSubview(firstView)}

}

TocreateaCGRect,youuseitsinitializerandpassinthevaluesfororigin.x,origin.y,size.width,andsize.height.

TosetthebackgroundColor,youusetheUIColorclassmethodblueColor().ThisisaconveniencemethodthatinitializesaninstanceofUIColorthatisconfiguredtobeblue.ThereareanumberofUIColorconveniencemethodsforcommoncolors,suchasgreenColor(),blackColor(),andclearColor().

Buildandruntheapplication(Command-R).YouwillseeabluerectanglethatistheinstanceofUIView.BecausetheoriginoftheUIView’sframeis(160,240),therectangle’stopleftcorneris160pointstotherightand240pointsdownfromthetopleftcornerofitssuperview.Theviewstretches100pointstotherightand150pointsdownfromitsorigin,inaccordancewithitsframe’ssize(Figure3.6).

WOW! eBook www.wowebook.org

Page 109: iOS Programming The Big Nerd Ranch Guide, 5th

Figure3.6WorldTrotterwithoneUIView

Notethatthesevaluesareinpoints,notpixels.Ifthevalueswereinpixels,thentheywouldnotbeconsistentacrossdisplaysofdifferentresolutions(i.e.,Retinavs.non-Retina).Asinglepointisarelativeunitofameasure;itwillbeadifferentnumberofpixelsdependingonhowmanypixelsareinthedisplay.Sizes,positions,lines,andcurvesarealwaysdescribedinpointstoallowfordifferencesindisplayresolution.

Figure3.7representstheviewhierarchythatyouhavecreated.

WOW! eBook www.wowebook.org

Page 110: iOS Programming The Big Nerd Ranch Guide, 5th

Figure3.7Currentviewhierarchy

EveryinstanceofUIViewhasasuperviewproperty.Whenyouaddaviewasasubviewofanotherview,theinverserelationshipisautomaticallyestablished.Inthiscase,theUIView’ssuperviewistheUIWindow.

Let’sexperimentwiththeviewhierarchy.First,inViewController.swift,createanotherinstanceofUIViewwithadifferentframeandbackgroundcolor.overridefuncviewDidLoad(){super.viewDidLoad()

letfirstFrame=CGRect(x:160,y:240,width:100,height:150)letfirstView=UIView(frame:firstFrame)firstView.backgroundColor=UIColor.blueColor()view.addSubview(firstView)

letsecondFrame=CGRect(x:20,y:30,width:50,height:50)letsecondView=UIView(frame:secondFrame)secondView.backgroundColor=UIColor.greenColor()view.addSubview(secondView)}

Buildandrunagain.Inadditiontothebluerectangle,youwillseeagreensquarenearthetoplefthandcornerofthewindow.Figure3.8showstheupdatedviewhierarchy.

WOW! eBook www.wowebook.org

Page 111: iOS Programming The Big Nerd Ranch Guide, 5th

Figure3.8Updatedviewhierarchywithtwosubviewsassiblings

NowyouaregoingtoadjusttheviewhierarchysothatoneinstanceofUIViewisasubviewoftheotherUIViewinsteadoftheviewcontroller’sview.InViewController.swift,addsecondViewasasubviewoffirstView....

letsecondView=UIView(frame:secondFrame)secondView.backgroundColor=UIColor.greenColor()

view.addSubview(secondView)firstView.addSubview(secondView)

Yourviewhierarchyisnowfourlevelsdeep,asshowninFigure3.9.

WOW! eBook www.wowebook.org

Page 112: iOS Programming The Big Nerd Ranch Guide, 5th

Figure3.9OneUIViewasasubviewoftheother

Buildandruntheapplication.NoticethatsecondView’spositiononthescreenhaschanged(Figure3.10).Aview’sframeisrelativetoitssuperview,sothetopleftcornerofsecondViewisnowinset(20,30)pointsfromthetopleftcorneroffirstView.

WOW! eBook www.wowebook.org

Page 113: iOS Programming The Big Nerd Ranch Guide, 5th

Figure3.10WorldTrotterwithnewhierarchy

(IfthegreeninstanceofUIViewlookssmallerthanitdidpreviously,thatisjustanopticalillusion.Itssizehasnotchanged.)

Nowthatyouhaveseenthebasicsofviewsandtheviewhierarchy,youcanstartworkingontheinterfaceforWorldTrotter.Insteadofbuildinguptheinterfaceprogrammatically,youwilluseInterfaceBuildertovisuallylayouttheinterface,asyoudidinChapter1.

InViewController.swift,startbyremovingyourpracticecode.overridefuncviewDidLoad(){super.viewDidLoad()

letfirstFrame=CGRect(x:160,y:240,width:100,height:150)letfirstView=UIView(frame:firstFrame)firstView.backgroundColor=UIColor.blueColor()view.addSubview(firstView)

letsecondFrame=CGRect(x:20,y:30,width:50,height:50)letsecondView=UIView(frame:secondFrame)secondView.backgroundColor=UIColor.greenColor()firstView.addSubview(secondView)}

Nowlet’saddsomeviewstotheinterfaceandsettheirframes.

OpenMain.storyboard.Noticethattheinterfaceonthescreeniscurrentlyasquare.Whileyouexploreviewsandtheirframes,itwillbenicetohavethesizeoftheinterfaceinXcodematchthescreensizeofthedevicethatyouwillbeusing.

WOW! eBook www.wowebook.org

Page 114: iOS Programming The Big Nerd Ranch Guide, 5th

SelecttheViewControllereitherinthedocumentoutlineorbyclickingtheyellowcircleabovetheinterface.Opentheattributesinspector,whichisthefourthtabintheutilitiesarea.YoucanquicklyopenthispaneusingthekeyboardshortcutCommand-Option-4.

Atthetopofthepane,findthesectionlabeledSimulatedMetricsandchangetheSizetobeiPhone4.7-inch.Thiswillresizethesquareinterfacetomatchthedimensionsofthe4.7-inchdevices.

Fromtheobjectlibrary,dragfiveinstancesofUILabelontothecanvas.Spacethemoutverticallyonthetophalfoftheinterfaceandcenterthemhorizontally.SettheirtexttomatchFigure3.11.

Figure3.11Addinglabelstotheinterface

SelectthetoplabelsoyoucanseeitsframeinInterfaceBuilder.Openitssizeinspector–thefifthtabintheutilitiesarea.(Asyoumighthavenoticedbythispoint,thekeyboardshortcutsfortheutilitiestabsareCommand-Optionplusthetabnumber.Sincethesize

WOW! eBook www.wowebook.org

Page 115: iOS Programming The Big Nerd Ranch Guide, 5th

inspectoristhefifthtab,itskeyboardshortcutisCommand-Option-5.)

UndertheViewsection,findFrameRectangle.(Ifyoudonotseeit,youmightneedtoselectitfromtheShowpop-upmenu.)Thesevaluesaretheview’sframe,andtheydictatethepositionoftheviewonscreen(Figure3.12).

Figure3.12Viewframevalues

BuildandruntheapplicationontheiPhone6ssimulator.Thiscorrespondstothe4.7-inchsimulatedmetricsthatyouspecifiedinthestoryboard.TheinterfaceonthesimulatorwilllookidenticaltotheinterfacethatyoulaidoutinInterfaceBuilder.

Customizingthelabels

Let’smaketheinterfacelookalittlebitbetterbycustomizingtheviewproperties.

InMain.storyboard,selectthebackgroundview.Opentheattributesinspectorandgivetheappanewbackgroundcolor:FindandclicktheBackgrounddropdownandclickOther.Selectthesecondtab(theColorSliderstab)andenteraHexColor#ofF5F4F1(Figure3.13).Thiswillgivethebackgroundawarm,graycolor.

WOW! eBook www.wowebook.org

Page 116: iOS Programming The Big Nerd Ranch Guide, 5th

Figure3.13Changingthebackgroundcolor

Youcancustomizeattributescommontoselectedviewssimultaneously.Youwillusethistogivemanyofthelabelsalargerfontsizeaswellasaburntorangetextcolor.

SelectthetoptwoandbottomtwolabelsbyCommand-clickingtheminthedocumentoutlineandopentheattributesinspector.Updatethetextcolor:UndertheLabelsection,findColorandopenthepop-upmenu.SelecttheColorSliderstabagainandenteraHexColor#ofE15829.

Nowlet’supdatethefont.Selectthe212and100labels.UndertheLabelsectionintheattributesinspector,findFontandclickonthetexticonnexttothecurrentfont.Fromthepopoverthatappears,maketheFontSystem-SystemandtheSize70(Figure3.14).Selecttheremainingthreelabels.OpentheirFontpop-upandmaketheFontSystem-SystemandtheSize36.

WOW! eBook www.wowebook.org

Page 117: iOS Programming The Big Nerd Ranch Guide, 5th

Figure3.14Customizingthelabels’font

Nowthatthefontsizeislarger,thetextnolongerfitswithintheboundsofthelabel.Youcouldresizethelabelsmanually,butthereisaneasierway.

Selectthetoplabelonthecanvas.FromXcode’sEditormenu,selectSizetoFitContent(Command-=).Thiswillresizethelabeltoexactlyfititstextcontents.Repeattheprocessfortheotherfourlabels.(Youcanselectallfourlabelstoresizethemallatonce.)Nowmovethelabelssothattheyareagainnicelyalignedverticallyandcenteredhorizontally(Figure3.15).

WOW! eBook www.wowebook.org

Page 118: iOS Programming The Big Nerd Ranch Guide, 5th

Figure3.15Updatingthelabelframes

BuildandruntheapplicationontheiPhone6ssimulator.NowbuildandruntheapplicationontheiPhone6sPlussimulator.Noticethatthelabelsarenolongercentered–instead,theyappearshiftedslightlytotheleft.

Youhavejustseentwoofthemajorproblemswithabsoluteframes.First,whenthecontentschange(likewhenyouchangedthefontsize),theframesdonotautomaticallyupdate.Second,theviewdoesnotlookequallygoodondifferentsizesofscreens.

Ingeneral,youshouldnotuseabsoluteframesforyourviews.Instead,youshoulduseAutoLayouttoflexiblycomputetheframesforyoubasedonconstraintsthatyouspecifyforeachview.Forexample,whatyoureallywantforWorldTrotterisforthelabelstoremainthesamedistancefromthetopofthescreenandtoremainhorizontallycenteredwithintheirsuperview.Theyshouldalsoupdateifthefontortextofthelabelschange.Thisiswhatyouwillaccomplishinthenextsection.

WOW! eBook www.wowebook.org

Page 119: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 120: iOS Programming The Big Nerd Ranch Guide, 5th

TheAutoLayoutSystemBeforeyoucanfixthelabelstohavethemlayoutflexibly,youneedtolearnalittletheoryabouttheAutoLayoutsystem.

AsyousawinChapter1,absolutecoordinatesmakeyourlayoutfragilebecausetheyassumethatyouknowthesizeofthescreenaheadoftime.

UsingAutoLayout,youcandescribethelayoutofyourviewsinarelativewaythatenablestheirframestobedeterminedatruntimesothattheframes’definitionscantakeintoaccountthescreensizeofthedevicethattheapplicationisrunningon.

Alignmentrectangleandlayoutattributes

TheAutoLayoutsystemisbasedonthealignmentrectangle.Thisrectangleisdefinedbyseverallayoutattributes(Figure3.16).

Figure3.16Layoutattributesdefininganalignmentrectangleofaview

Width/Height

Thesevaluesdeterminethealignmentrectangle’ssize.Top/Bottom/Left/Right

Thesevaluesdeterminethespacingbetweenthegivenedgeofthealignmentrectangleandthealignmentrectangleofanotherviewinthehierarchy.

CenterX/CenterY

Thesevaluesdeterminethecenterpointofthealignmentrectangle.Baseline

Thisvalueisthesameasthebottomattributeformost,butnotall,views.Forexample,UITextFielddefinesitsbaselineasthebottomofthetextitdisplaysratherthanthebottomofthealignmentrectangle.Thiskeeps“descenders”(letterslike‘g’and‘p’thatdescendbelowthebaseline)frombeingobscuredbya

WOW! eBook www.wowebook.org

Page 121: iOS Programming The Big Nerd Ranch Guide, 5th

viewrightbelowthetextfield.Leading/Trailing

Thesevaluesarelanguage-specificattributes.Ifthedeviceissettoalanguagethatreadslefttoright(e.g.,English),thentheleadingattributeisthesameastheleftattributeandthetrailingattributeisthesameastherightattribute.Ifthelanguagereadsrighttoleft(e.g.,Arabic),thentheleadingattributeisontherightandthetrailingattributeisontheleft.InterfaceBuilderautomaticallyprefersleadingandtrailingoverleftandright,and,ingeneral,youshouldaswell.

Bydefault,everyviewhasanalignmentrectangle,andeveryviewhierarchyusesAutoLayout.

Thealignmentrectangleisverysimilartotheframe.Infact,thesetworectanglesareoftenthesame.Whereastheframeencompassestheentireview,thealignmentrectangleonlyencompassesthecontentthatyouwishtouseforalignmentpurposes.Figure3.17showsanexamplewheretheframeandthealignmentrectanglearedifferent.

Figure3.17Framevs.alignmentrectangle

Youcannotdefineaview’salignmentrectangledirectly.Youdonothaveenoughinformation(likescreensize)todothat.Instead,youprovideasetofconstraints.Takentogether,theseconstraintsenablethesystemtodeterminethelayoutattributes,andthusthealignmentrectangle,foreachviewintheviewhierarchy.

Constraints

Aconstraintdefinesaspecificrelationshipinaviewhierarchythatcanbeusedtodeterminealayoutattributeforoneormoreviews.Forexample,youmightaddaconstraintlike,“Theverticalspacebetweenthesetwoviewsshouldalwaysbe8points,”or,“Theseviewsmustalwayshavethesamewidth.”Aconstraintcanalsobeusedtogiveaviewafixedsize,like,“Thisview’sheightshouldalwaysbe44points.”

Youdonotneedaconstraintforeverylayoutattribute.Somevaluesmaycomedirectlyfromaconstraint;otherswillbecomputedbythevaluesofrelatedlayoutattributes.Forexample,ifaview’sconstraintssetitsleftedgeanditswidth,thentherightedgeisalreadydetermined(leftedge+width=rightedge,always).Asageneralruleofthumb,youneedatleasttwoconstraintsperdimension(horizontalandvertical).

WOW! eBook www.wowebook.org

Page 122: iOS Programming The Big Nerd Ranch Guide, 5th

If,afteralloftheconstraintshavebeenconsidered,thereisstillanambiguousormissingvalueforalayoutattribute,thentherewillbeerrorsandwarningsfromAutoLayoutandyourinterfacewillnotlookasyouexpectonalldevices.Debuggingtheseproblemsisimportant,andyouwillgetsomepracticelaterinthischapter.

Howdoyoucomeupwithconstraints?Let’sseehowusingthelabelsthatyouhavelaidoutonthecanvas.

First,describewhatyouwanttheviewtolooklikeindependentofscreensize.Forexample,youmightsaythatyouwantthetoplabeltobe:

8pointsfromthetopofthescreen

centeredhorizontallyinitssuperview

aswideandastallasitstext

ToturnthisdescriptionintoconstraintsinInterfaceBuilder,itwillhelptounderstandhowtofindaview’snearestneighbor.Thenearestneighboristheclosestsiblingviewinthespecifieddirection(Figure3.18).

Figure3.18Nearestneighbor

Ifaviewdoesnothaveanysiblingsinthespecifieddirection,thenthenearestneighborisitssuperview,alsoknownasitscontainer.

WOW! eBook www.wowebook.org

Page 123: iOS Programming The Big Nerd Ranch Guide, 5th

Nowyoucanspellouttheconstraintsforthelabel:

1. Thelabel’stopedgeshouldbe8pointsawayfromitsnearestneighbor(whichisitscontainer–theviewoftheViewController).

2. Thelabel’scentershouldbethesameasitssuperview’scenter.

3. Thelabel’swidthshouldbeequaltothewidthofitstextrenderedatitsfontsize.

4. Thelabel’sheightshouldbeequaltotheheightofitstextrenderedatitsfontsize.

Ifyouconsiderthefirstandfourthconstraints,youcanseethatthereisnoneedtoexplicitlyconstrainthelabel’sbottomedge.Itwillbedeterminedfromtheconstraintsonthelabel’stopedgeandthelabel’sheight.Similarly,thesecondandthirdconstraintstogetherdeterminethelabel’srightandleftedges.

Nowthatyouhaveaplanforthetoplabel,youcanaddtheseconstraints.ConstraintscanbeaddedusingInterfaceBuilderorincode.ApplerecommendsthatyouaddconstraintsusingInterfaceBuilderwheneverpossible,andthatiswhatyouwilldohere.However,ifyourviewsarecreatedandconfiguredprogrammatically,thenyoucanaddconstraintsincode.InChapter6,youwillpracticethatapproach.

AddingconstraintsinInterfaceBuilder

Let’sgetstartedconstrainingthattoplabel.

Selectthetoplabelonthecanvas.Inthebottomrighthandcornerofthecanvas,findtheAutoLayoutconstraintmenu(Figure3.19).

WOW! eBook www.wowebook.org

Page 124: iOS Programming The Big Nerd Ranch Guide, 5th

Figure3.19UsingtheAutoLayoutconstraintmenu

Clickthe icon(thethirdfromtheleft)torevealthePinmenu.Thismenushowsyouthecurrentsizeandpositionofthelabel.

AtthetopofthePinmenuarefourvaluesthatdescribethelabel’scurrentspacingfromitsnearestneighboronthecanvas.Forthislabel,youareonlyinterestedinthetopvalue.

Toturnthisvalueintoaconstraint,clickthetopredstrutseparatingthevaluefromthesquareinthemiddle.Thestrutwillbecomeasolidredline.

Inthemiddleofthemenu,findthelabel’sWidthandHeight.ThevaluesnexttoWidthandHeightindicatethecurrentcanvasvalues.Toconstrainthelabel’swidthandheighttothecurrentcanvasvalues,checktheboxesnexttoWidthandHeight.ThebuttonatthebottomofthemenureadsAdd3Constraints.Clickthisbutton.

Atthispoint,youhavenotspecifiedenoughconstraintstofullydeterminethealignmentrectangle.InterfaceBuilderwillhelpyoudeterminewhattheproblemis.

InthetoprightcornerofInterfaceBuilder,noticetheyellowwarningsign(Figure3.20).Clickonthisicontorevealtheissue:“Horizontalpositionisambiguousfor“212”.”

WOW! eBook www.wowebook.org

Page 125: iOS Programming The Big Nerd Ranch Guide, 5th

Figure3.20Horizontalambiguity

Youhaveaddedtwoverticalconstraints(atopedgeconstraintandaheightconstraint),butyouhaveonlyaddedonehorizontalconstraint(awidthconstraint).Havingonlyoneconstraintmakesthehorizontalpositionofthelabelambiguous.Youwillfixthisissuebyaddingacenteralignmentconstraintbetweenthelabelanditssuperview.

Withthetoplabelstillselected,clickthe icon(thesecondfromtheleftintheAutoLayoutconstraintsmenu)torevealtheAlignmenu.Ifyouhavemultipleviewsselected,thismenuwillallowyoutoalignattributesamongtheviews.Sinceyouhaveonlyselectedonelabel,theonlyoptionsyouaregivenaretoaligntheviewwithinitscontainer.

IntheAlignmenu,selectHorizontallyinContainer(donotclickAdd1Constraintyet).Onceyouaddthisconstraint,therewillbeenoughconstraintstofullydeterminethealignmentrectangle.Toensurethattheframeofthelabelmatchestheconstraintsspecified,opentheUpdateFramespop-upmenufromtheAlignmenuandselectItemsofNewConstraints.Thiswillrepositionthelabeltomatchtheconstraintsthathavebeenadded.NowclickonAdd1Constrainttoaddthecenteringconstraintandrepositionthelabel.

Thelabel’sconstraintsareallbluenowthatthealignmentrectangleforthelabelisfullyspecified.Additionally,thewarningatthetoprightcornerofInterfaceBuilderisnowgone.

BuildandruntheapplicationontheiPhone6ssimulatorandtheiPhone6sPlussimulator.Thetoplabelwillremaincenteredinbothsimulators.

Intrinsiccontentsize

Althoughthetoplabel’spositionisflexible,itssizeisnot.Thisisbecauseyouhaveaddedexplicitwidthandheightconstraintstothelabel.Ifthetextorfontweretochange,youwouldbeinthesamepositionyouwereinearlier.Thesizeoftheframeisabsolute,sotheframewouldnothugtothecontent.

Thisiswheretheintrinsiccontentsizeofaviewcomesintoplay.Youcanthinkoftheintrinsiccontentsizeasthesizethataview“wants”tonaturallybe.Forlabels,thissizeisthesizeofthetextrenderedatthegivenfont.Forimages,thisisthesizeoftheimageitself.

Aview’sintrinsiccontentsizeactsasimplicitwidthandheightconstraints.Ifyoudonotspecifyconstraintsthatexplicitlydeterminethewidth,theviewwillbeitsintrinsicwidth.Thesamegoesfortheheight.

Withthisknowledge,letthetoplabelhaveaflexiblesizebyremovingtheexplicitwidthandheightconstraints.

InMain.storyboard,selectthewidthconstraintonthelabel.YoucandothisbyWOW! eBook

www.wowebook.org

Page 126: iOS Programming The Big Nerd Ranch Guide, 5th

clickingontheconstraintonthecanvas.Alternatively,inthedocumentoutline,youcanclickonthedisclosuretrianglenexttothe212label,thendisclosethelistofconstraintsforthelabel(Figure3.21).Onceyouhaveselectedthewidthconstraint,presstheDeletekey.Dothesamefortheheightconstraint.

Figure3.21Selectingthewidthconstraint

Noticethattheconstraintsforthelabelarestillblue.Sincethewidthandheightarebeinginferredfromthelabel’sintrinsiccontentsize,therearestillenoughconstraintstodeterminethelabel’salignmentrectangle.

Misplacedviews

Asyouhaveseen,blueconstraintsindicatethatthealignmentrectangleforaviewisfullyspecified.Orangeconstraintsoftenindicateamisplacedview.ThismeansthattheframefortheviewinInterfaceBuilderisdifferentthantheframethatAutoLayouthascomputed.

WOW! eBook www.wowebook.org

Page 127: iOS Programming The Big Nerd Ranch Guide, 5th

Amisplacedviewisveryeasytofix.Thatisgood,becauseitisalsoaverycommonissuethatyouwillencounterwhenworkingwithAutoLayout.

Giveyourtoplabelamisplacedviewsothatyoucanseehowtoresolvethisissue.Resizethetoplabelonthecanvasusingtheresizecontrolsandlookfortheyellowwarninginthetoprightcornerofthecanvas.Clickonthiswarningicontorevealtheproblem:“Framefor“212”willbedifferentatruntime”(Figure3.22).

Figure3.22Misplacedviewwarning

Asthewarningsays,theframeatruntimewillnotbethesameastheframespecifiedonthecanvas.Ifyoulookclosely,youwillseeanorangedottedlinethatindicateswhattheruntimeframewillbe.

Buildandruntheapplication.NoticethatthelabelisstillcentereddespitethenewframethatyougaveitinInterfaceBuilder.Thismightseemgreat–yougettheresultthatyouwant,afterall.ButthedisconnectbetweenwhatyouhavespecifiedinInterfaceBuilderandtheconstraintscomputedbyAutoLayoutwillcauseproblemsdownthelineasyoucontinuetobuildyourviews.Let’sfixthemisplacedview.

Backinthestoryboard,selectthetoplabelonthecanvas.Clickthe icon(theright-mosticon)torevealtheResolveAutoLayoutIssuesmenu.SelectUpdateFramesfromtheSelectedViewssection.Thiswillupdatetheframeofthelabeltomatchtheframethattheconstraintswillcompute.

YouwillgetveryusedtoupdatingtheframesofviewsasyouworkwithAutoLayout.Onewordofcaution:ifyoutrytoupdatetheframesforaviewthatdoesnothaveenoughconstraints,youwillalmostcertainlygetunexpectedresults.Ifthathappens,undothechangeandinspecttheconstraintstoseewhatismissing.

Atthispoint,thetoplabelisingoodshape.Ithasenoughconstraintstodetermineitsalignmentrectangle,andtheviewislayingoutthewayyouwant.

BecomingproficientwithAutoLayouttakesalotofexperience,sointhenextsectionyouWOW! eBook

www.wowebook.org

Page 128: iOS Programming The Big Nerd Ranch Guide, 5th

aregoingtoremovetheconstraintsfromthetoplabelandthenaddconstraintstoallofthelabels.

Addingmoreconstraints

Let’sfleshouttheconstraintsfortherestoftheviews.Beforeyoudothat,youwillfirstremovetheexistingconstraintsfromthetoplabel.

Selectthetoplabelonthecanvas.OpentheResolveAutoLayoutIssuesmenuandselectClearConstraintsfromtheSelectedViewssection(Figure3.23).

Figure3.23Clearingconstraints

Youaregoingtoaddtheconstraintstoalloftheviewsintwosteps.Firstyouwillcenterthetoplabelhorizontallywithinthesuperview.Thenyouwilladdconstraintsthatpinthetopofeachlabeltoitsnearestneighborwhilealigningthecentersofallofthelabels.

Selectthetoplabel.OpentheAlignmenuandchooseHorizontallyinContainerwithaconstantof0.MakesurethatUpdateFrameshasNoneselected;rememberthatyoudonotwanttoupdatetheframeofaviewthatdoesnothaveenoughconstraints,andthisoneconstraintwillcertainlynotprovideenoughinformationtocomputethealignmentrectangle.GoaheadandAdd1Constraint.

Nowselectallfivelabelsonthecanvas.Itcanbeveryconvenienttoaddconstraintstomultipleviewssimultaneously.OpenthePinmenuandmakethefollowchoices:

1. Selectthetopstrutandmakesureithasaconstantof8.

2. FromtheAlignmenu,chooseHorizontalCenters.

3. FromtheUpdateFramesmenu,chooseItemsofNewConstraints.

YourmenushouldmatchFigure3.24.Onceitdoes,clickAdd9Constraints.ThiswilladdtheconstraintstotheviewsandupdatetheirframestoreflecttheAutoLayoutchanges.

WOW! eBook www.wowebook.org

Page 129: iOS Programming The Big Nerd Ranch Guide, 5th

Figure3.24AddingmoreconstraintswiththePinmenu

BuildandruntheapplicationontheiPhone6ssimulator.Theviewswillbecenteredwithintheinterface.NowbuildandruntheapplicationontheiPhone6sPlussimulator.Unlikeearlierinthechapter,allofthelabelsremaincenteredonthelargerinterface.

AutoLayoutisacrucialtechnologyforeveryiOSdeveloper.Ithelpsyoucreateflexiblelayoutsthatworkacrossarangeofdevicesandinterfacesizes.Italsotakesalotofpracticetomaster.YouwillgetalotofexperienceworkingwithAutoLayoutasyouworkthroughthisbook.

NowthattheinterfaceforWorldTrotterisusingAutoLayouttoadapttovariousscreensizes,thereisnoneedforyoutospecifyaniPhonescreensizewhenworkinginthestoryboard.

InMain.storyboard,selecttheViewControllerandopenitsattributesinspector.FindtheSimulatedMetricssectionandchangetheSizetoInferred.Theinterfaceupdatestobethesquareshapethatitwasinitially.Noticethatthelabelsstillremaincenteredinthissquareinterfaceduetotheconstraintsthatyouadded.

Designinginterfacesusingtheinferredsquareshapehelpstoforceyoutothinkaboutdesigningadaptiveinterfacesthatworkwithavarietyofscreensizesinsteadofdesigningforoneparticularscreensize.

WOW! eBook www.wowebook.org

Page 130: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 131: iOS Programming The Big Nerd Ranch Guide, 5th

BronzeChallenge:MoreAutoLayoutPracticeRemovealloftheconstraintsfromtheViewControllerinterfaceandthenaddthembackin.Trytodothiswithoutconsultingthebook.

WOW! eBook www.wowebook.org

Page 132: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 133: iOS Programming The Big Nerd Ranch Guide, 5th

4TextInputandDelegation

WorldTrotterlooksgood,butsofaritdoesnotdoanything.Inthischapter,youaregoingtoaddaninstanceofUITextFieldtoWorldTrotter.ThetextfieldwillallowtheusertotypeinatemperatureinFahrenheitthatwillthenbeconvertedtoCelsiusanddisplayedontheinterface(Figure4.1).

Figure4.1WorldTrotterwithaUITextField

WOW! eBook www.wowebook.org

Page 134: iOS Programming The Big Nerd Ranch Guide, 5th

TextEditingThefirstthingyouaregoingtodoisaddaUITextFieldtotheinterfaceandsetuptheconstraintsforthattextfield.Thistextfieldwillreplacethetoplabelintheinterfacethatcurrentlyhasthetext“212.”

OpenMain.storyboard.SelectthetoplabelandpresstheDeletekeytoremovethissubview.Theconstraintsforalloftheotherlabelswillturnredsincetheywerealldirectlyorindirectlyanchoredtothattoplabel(Figure4.2).ThatisOK;youwillfixthemshortly.

Figure4.2Ambiguousframesforthelabels

OpentheobjectlibraryanddragaTextFieldtothetopofthecanvaswherethelabelyoudeletedwaspreviouslyplaced.

Nowsetuptheconstraintsforthistextfield.Withthetextfieldselected,opentheAlignmenuandaligntheviewHorizontallyinContainerwithaconstantof0.MakesurethatUpdateFramesissettoNoneandthenAdd1Constraint.

NowopenthePinmenu.Givethetextfieldatopedgeconstraintof8points,abottomedgeconstraintof8points,andawidthof250(Figure4.3).Addthesethreeconstraints.

WOW! eBook www.wowebook.org

Page 135: iOS Programming The Big Nerd Ranch Guide, 5th

Figure4.3TextfieldPinmenu

Finally,selectthetextfieldandthelabelrightbelowit.OpentheAlignmenu,selectHorizontalCenterswithaconstantof0,UpdateFramesforAllFramesinContainer,andthenAdd1Constraint(Figure4.4).

Figure4.4Aligningthetextfield

Next,customizesomeofthetextfieldproperties.OpentheattributesinspectorforthetextWOW! eBook

www.wowebook.org

Page 136: iOS Programming The Big Nerd Ranch Guide, 5th

fieldandmakethefollowingchanges:

Setthetextcolor(fromtheColormenu)toburntorange.

SetthefonttoSystemwithasizeof70.

SettheAlignmenttocentered.

Settheplaceholdertexttobevalue.Thisisthetextthatwillbedisplayedwhentheuserhasnotenteredanytext.

SettheBorderStyletobenone,whichisthefirstelementofthesegmentedcontrolwiththedottedlines.

TheattributesinspectorforyourtextfieldshouldlooklikeFigure4.5.

Figure4.5Textfieldattributesinspector

Sincethetextfield’sfontchanged,theviewsonthecanvasnowaremisplaced.Selectthegraybackgroundview,opentheResolveAutoLayoutIssuesmenu,andselectUpdateFramesfromtheAllViewsinViewControllersection.Thetextfieldandlabelswillberepositionedtomatchtheirconstraints(Figure4.6).

WOW! eBook www.wowebook.org

Page 137: iOS Programming The Big Nerd Ranch Guide, 5th

Figure4.6Updatedframes

Buildandruntheapplication.Taponthetextfieldandentersometext.Ifyoudonotseethekeyboardappear,clickthesimulator’sHardwaremenuandselectKeyboard→ToggleSoftwareKeyboardorusethekeyboardshortcutCommand-K.Bydefault,thesimulatortreatsyourcomputer’skeyboardasaBluetoothkeyboardconnectedtothesimulator.Thisisnotusuallywhatyouwant.Instead,youwantthesimulatortomimicaniOSdevicerunningwithoutanyaccessoriesattachedbyusingtheonscreenkeyboard.

Keyboardattributes

Whenatextfieldistapped,thekeyboardautomaticallyslidesupontothescreen.(Youwillseewhythishappenslaterinthischapter.)Thekeyboard’sappearanceisdeterminedbyasetoftheUITextField’spropertiescalledtheUITextInputTraits.Oneofthesepropertiesisthetypeofkeyboardthatisdisplayed.Forthisapplication,youwanttousethedecimalpad.

Intheattributesinspectorforthetextfield,findtheattributenamedKeyboardTypeandchooseDecimalPad.Inthesamesection,youcanseesomeoftheothertextinputtraitsthatyoucancustomizeforthekeyboard.ChangebothCorrectionandSpellCheckingtoNo(Figure4.7).

WOW! eBook www.wowebook.org

Page 138: iOS Programming The Big Nerd Ranch Guide, 5th

Figure4.7Keyboardtextinputtraits

Buildandruntheapplication.Tappingonthetextfieldwillnowrevealthedecimalpad.

Respondingtotextfieldchanges

ThenextstepoftheprojectwillbetoupdatetheCelsiuslabelwhentextistypedintothetextfield.Youaregoingtoneedtowritesomecodetodothis.Specifically,thiscodewillgointotheviewcontrollersubclassassociatedwiththisinterface.

Currently,thatcorrespondswiththeViewControllerclassdefinedinViewController.swift.However,ViewControllerisnotaverydescriptivenameforaviewcontrollerthatmanagestheconversionbetweenFahrenheitandCelsius.Havingdescriptivetypenamesallowsyoutomoreeasilymaintainyourprojectsastheygrowlarger.Youaregoingtodeletethisfileandreplaceitwithamoredescriptiveclass.

Intheprojectnavigator,findViewController.swiftanddeleteit.ThencreateanewfilebyselectingFile→New→File…(orpressCommand-N).IntheiOSsection,selectSource,chooseSwiftFile,andclickNext.

Onthenextpane,namethisfileConversionViewController.SavethefileintheWorldTrottergroupwithintheWorldTrotterprojectandmakesurethattheWorldTrottertargetischecked,asshowninFigure4.8.ClickCreate,andXcodewillopenConversionViewController.swiftintheeditor.

WOW! eBook www.wowebook.org

Page 139: iOS Programming The Big Nerd Ranch Guide, 5th

Figure4.8SavingaSwiftfile

InConversionViewController.swift,importUIKitanddefineanewviewcontrollernamedConversionViewController.importFoundationimportUIKit

classConversionViewController:UIViewController{

}

NowyouneedtoassociatetheinterfaceyoucreatedinMain.storyboardwiththisnewviewcontrollerthatyoudefined.

OpenMain.storyboardandselecttheViewController,eitherinthedocumentoutlineorbyclickingtheyellowcircleabovetheinterface.

Opentheidentityinspector,whichisthethirdtabintheutilitiesview(Command-Option-3).Atthetop,findtheCustomClasssectionandchangetheClasstoConversionViewController(Figure4.9).YouwilllearnwhatallofthisisdoinginChapter5.

Figure4.9Changingthecustomclass

YousawinChapter1thatabuttoncansendeventstoacontrollerwhenthebuttonis

WOW! eBook www.wowebook.org

Page 140: iOS Programming The Big Nerd Ranch Guide, 5th

tapped.Textfieldsareanothercontrol(bothUIButtonandUITextFieldaresubclassesofUIControl)andcansendaneventwhenthetextchanges.

Togetthisallworking,youwillneedtocreateanoutlettotheCelsiustextlabelandcreateanactionforthetextfieldtocallwhenthetextchanges.

OpenConversionViewController.swiftanddefinethisoutletandaction.Fornow,thelabelwillbeupdatedwithwhatevertexttheusertypesintothetextfield.classConversionViewController:UIViewController{

@IBOutletvarcelsiusLabel:UILabel!

@IBActionfuncfahrenheitFieldEditingChanged(textField:UITextField){celsiusLabel.text=textField.text}}

OpenMain.storyboardtomaketheseconnections.TheoutletwillbeconnectedjustasyoudidinChapter1.Control-dragfromtheConversionViewControllertotheCelsiuslabel(theonethatcurrentlysays“100”)andconnectittothecelsiusLabel.

Connectingtheactionwillbealittledifferentsinceyouwanttheactiontobetriggeredwhentheeditingchanges.

Selectthetextfieldonthecanvasandopenitsconnectionsinspectorfromtheutilitypane(theright-mosttab,orCommand-Option-6).Theconnectionsinspectorisagreatplacetomakeconnectionsandseewhatconnectionshavealreadybeenmade.

YouaregoingtohavechangestothetextfieldtriggertheactionyoudefinedinConversionViewController.Intheconnectionsinspector,locatetheSentEventsandtheEditingChangedevent.ClickanddragfromthecircletotherightofEditingChangedtotheConversionViewControllerandclickthefahrenheitFieldEditingChanged:actioninthepop-upmenu(Figure4.10).

Figure4.10Connectingtheeditingchangedevent

WOW! eBook www.wowebook.org

Page 141: iOS Programming The Big Nerd Ranch Guide, 5th

Buildandruntheapplication.Tapthetextfieldandtypesomenumbers.TheCelsiuslabelwillmimicthetextthatistypedin.Nowdeletethetextinthetextfieldandnoticehowthelabelseemstogoaway.Alabelwithnotexthasanintrinsiccontentwidthandheightof0,sothelabelsbelowitmoveup.Let’sfixthisissue.

InConversionViewController.swift,updatefahrenheitFieldEditingChanged(_:)todisplay“???”ifthetextfieldisempty.@IBActionfuncfahrenheitFieldEditingChanged(textField:UITextField){celsiusLabel.text=textField.text

iflettext=textField.textwhere!text.isEmpty{celsiusLabel.text=text}else{celsiusLabel.text="???"}}

Ifthetextfieldhastextandthattextisnotempty,itwillbesetonthecelsiusLabel.Ifeitherofthoseconditionsarenottrue,thenthecelsiusLabelwillbegiventhestring“???”.

Buildandruntheapplication.Addsometext,deleteit,andthenconfirmthatthecelsiusLabelispopulatedwith“???”oncethetextfieldisempty.

Dismissingthekeyboard

Currently,thereisnowaytodismissthekeyboard.Let’saddthatfunctionality.OnecommonwayofdoingthisisbydetectingwhentheusertapstheReturnkeyandusingthatactiontodismissthekeyboard;youwillusethisapproachinChapter13.SincethedecimalpaddoesnothaveaReturnkey,youwillallowtheusertotaponthebackgroundviewtotriggerthedismissal.

Whenthetextfieldistapped,themethodbecomeFirstResponder()iscalledonthetextfield.Thisisthemethodthat,amongotherthings,causesthekeyboardtoappear.Todismissthekeyboard,youcallthemethodresignFirstResponder()onthetextfield.YouwilllearnmoreaboutthesemethodsinChapter13.

Toaccomplishthis,youwillneedanoutlettothetextfieldandamethodthatistriggeredwhenthebackgroundviewistapped.ThismethodwillcallresignFirstResponder()onthetextfieldoutlet.Let’stakecareofthecodefirst.

OpenConversionViewController.swiftanddeclareanoutletnearthetoptoreferencethetextfield.@IBOutletvarcelsiusLabel:UILabel!@IBOutletvartextField:UITextField!

Nowimplementanactionmethodthatwilldismissthekeyboardwhencalled.

(Inthecodeabove,weincludedexistingcodesothatyoucouldpositionthenewcodecorrectly.Inthecodebelow,wedonotprovidethatcontextbecausethepositionofthenewcodeisnotimportantsolongasitiswithinthecurlybracesforthetypebeing

WOW! eBook www.wowebook.org

Page 142: iOS Programming The Big Nerd Ranch Guide, 5th

implemented–inthiscase,theConversionViewControllerclass.Whenacodeblockincludesallnewcode,wesuggestthatyouputitattheendofthetype’simplementation,justinsidethefinalclosingbrace.InChapter14,youwillseehowtoeasilynavigatewithinanimplementationfilewhenyourfilesgetlongerandmorecomplex.)@IBActionfuncdismissKeyboard(sender:AnyObject){textField.resignFirstResponder()}

Twothingsarestillneeded:thetextFieldoutletneedstobeconnectedinthestoryboardfile,andyouneedawayoftriggeringthedismissKeyboard(_:)methodyouadded.

Totakecareofthefirstitem,openMain.storyboardandselecttheConversionViewController.Control-dragfromtheConversionViewControllertothetextfieldonthecanvasandconnectittothetextFieldoutlet.

Nowyouneedawayoftriggeringthemethodyouimplemented.Youwilluseagesturerecognizertoaccomplishthis.

AgesturerecognizerisasubclassofUIGestureRecognizerthatdetectsaspecifictouchsequenceandcallsanactiononitstargetwhenthatsequenceisdetected.Therearegesturerecognizersthatdetecttaps,swipes,longpresses,andmore.Inthischapter,youwilluseaUITapGestureRecognizertodetectwhentheusertapsthebackgroundview.YouwilllearnmoreaboutgesturerecognizersinChapter18.

InMain.storyboard,findTapGestureRecognizerintheobjectlibrary.DragthisobjectontothebackgroundviewfortheConversionViewController.Youwillseeareferencetothisgesturerecognizerinthescenedock,therowoficonsabovethecanvas.

Control-dragfromthegesturerecognizerinthescenedocktotheConversionViewControllerandconnectittothedismissKeyboard:method(Figure4.11).

Figure4.11Connectingthegesturerecognizeraction

WOW! eBook www.wowebook.org

Page 143: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 144: iOS Programming The Big Nerd Ranch Guide, 5th

ImplementingtheTemperatureConversionWiththebasicsoftheinterfacewiredup,let’simplementtheconversionfromFahrenheittoCelsius.YouaregoingtostorethecurrentFahrenheitvalueandcomputetheCelsiusvaluewheneverthetextfieldchanges.

InConversionViewController.swift,addapropertyfortheFahrenheitvalue.Thiswillbeanoptionaldouble(aDouble?).@IBOutletvarcelsiusLabel:UILabel!

varfahrenheitValue:Double?

Thereasonthispropertyisoptionalisbecausetheusermightnothavetypedinanumber,similartotheemptystringissueyoufixedearlier.

NowaddacomputedpropertyfortheCelsiusvalue.ThisvaluewillbecomputedbasedontheFahrenheitvalue.varfahrenheitValue:Double?

varcelsiusValue:Double?{ifletvalue=fahrenheitValue{return(value-32)*(5/9)}else{returnnil}}

FirstyouchecktoseeifthereisaFahrenheitvalue.Ifthereis,youconvertthisvaluetotheequivalentvalueinCelsius.IfthereisnoFahrenheitvalue,thenyoucannotcomputeaCelsiusvalueandsoyoureturnnil.

AnytimetheFahrenheitvaluechanges,theCelsiuslabelneedstobeupdated.

AddamethodtoConversionViewControllerthatupdatesthecelsiusLabel.funcupdateCelsiusLabel(){ifletvalue=celsiusValue{celsiusLabel.text="\(value)"}else{celsiusLabel.text="???"}}

YouwantthismethodtobecalledwhenevertheFahrenheitvaluechanges.Todothis,youwilluseapropertyobserver,whichisachunkofcodethatgetscalledwheneveraproperty’svaluechanges.

Apropertyobserverisdeclaredusingcurlybracesimmediatelyafterthepropertydeclaration.Insidethebraces,youdeclareyourobserverusingeitherwillSetordidSetdependingonwhetheryouwanttobenotifiedimmediatelybeforeorimmediatelyafterthepropertyvaluechanges,respectively.

AddapropertyobservertofahrenheitValuethatgetscalledafterthepropertyvaluechanges.varfahrenheitValue:Double?{didSet{

WOW! eBook www.wowebook.org

Page 145: iOS Programming The Big Nerd Ranch Guide, 5th

updateCelsiusLabel()}}

(Onesmallnote:propertyobserversarenottriggeredwhenthepropertyvalueischangedfromwithinaninitializer.)

Withthatlogicinplace,youcannowupdatetheFahrenheitvaluewhenthetextfieldchanges(which,inturn,willtriggeranupdateoftheCelsiuslabel).

InfahrenheitFieldEditingChanged(_:),deleteyourearliernonconvertingimplementationandinsteadupdatetheFahrenheitvalue.@IBActionfuncfahrenheitFieldEditingChanged(textField:UITextField){iflettext=textField.textwhere!text.isEmpty{celsiusLabel.text=text}else{celsiusLabel.text="???"}

iflettext=textField.text,value=Double(text){fahrenheitValue=value}else{fahrenheitValue=nil}}

Firstyoucheckwhetherthetextfieldhassometext.Ifso,youchecktoseewhetherthattextcanberepresentedbyaDouble.Forexample,“3.14”canberepresentedbyaDouble,butboth“three”and“1.2.3”cannot.Ifbothofthosecheckspass,thentheFahrenheitvalueissettothatDoublevalue.Ifeitherofthosechecksfails,thentheFahrenheitvalueissettonil.

Buildandruntheapplication.TheconversionbetweenFahrenheitandCelsiusworksgreat–solongasyouenteravalidnumber.

Intheremainderofthischapter,youwillupdateWorldTrottertoaddresstwoissues:youwillformattheCelsiusvaluetoshowaprecisionuptoonefractionaldigit,andyouwillnotallowtheusertotypeinmorethanonedecimalseparator.

Thereareacoupleofotherissueswithyourapp,butyouwillfocusonthesetwofornow.Someoftheotherissueswillbepresentedaschallengesattheendofthischapter.Let’sstartwithupdatingtheprecisionoftheCelsiusvalue.

Numberformatters

Youuseanumberformattertocustomizethedisplayofanumber.Thereareotherformattersforformattingdates,energy,mass,length,andmore.

CreateaconstantnumberformatterinConversionViewController.swift.letnumberFormatter:NSNumberFormatter={letnf=NSNumberFormatter()nf.numberStyle=.DecimalStylenf.minimumFractionDigits=0nf.maximumFractionDigits=1returnnf}()

WOW! eBook www.wowebook.org

Page 146: iOS Programming The Big Nerd Ranch Guide, 5th

Hereyouareusingaclosuretoinstantiatethenumberformatter.YouarecreatinganNSNumberFormatterwiththe.Decimalstyleandconfiguringittodisplaynomorethanonefractionaldigit.YouwilllearnmoreaboutthisnewsyntaxfordeclaringpropertiesinChapter15.

NowmodifyupdateCelsiusLabel()tousethisformatter.funcupdateCelsiusLabel(){ifletvalue=celsiusValue{celsiusLabel.text="\(value)"celsiusLabel.text=numberFormatter.stringFromNumber(value)}else{celsiusLabel.text="???"}}

Buildandruntheapplication.PlayaroundwithFahrenheitvaluestoseetheformatteratwork.YoushouldneverseemorethanonefractionaldigitontheCelsiuslabel.

Inthenextsection,youwillupdatetheapplicationtoacceptamaximumofonedecimalseparatorinthetextfield.Todothis,youwilluseacommoniOSdesignpatterncalleddelegation.

WOW! eBook www.wowebook.org

Page 147: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 148: iOS Programming The Big Nerd Ranch Guide, 5th

DelegationDelegationisanobject-orientedapproachtocallbacks.Acallbackisafunctionthatissuppliedinadvanceofaneventandiscalledeverytimetheeventoccurs.Someobjectsneedtomakeacallbackformorethanoneevent.Forinstance,thetextfieldwantsto“callback”whentheuserenterstextaswellaswhentheuserpressestheReturnkey.

However,thereisnobuilt-inwayfortwo(ormore)callbackfunctionstocoordinateandshareinformation.Thisistheproblemaddressedbydelegation–yousupplyasingledelegatetoreceivealloftheevent-relatedcallbacksforaparticularobject.Thisdelegateobjectcanthenstore,manipulate,acton,andrelaytheinformationfromthecallbacksasitseesfit.

Whentheusertypesintoatextfield,thattextfieldwillaskitsdelegateifitwantstoacceptthechangesthattheuserhasmade.ForWorldTrotter,youwanttodenythatchangeiftheuserattemptstoenteraseconddecimalseparator.ThedelegateforthetextfieldwillbetheinstanceofConversionViewController.

Conformingtoaprotocol

ThefirststepisenablinginstancesoftheConversionViewControllerclasstoperformtheroleofUITextFielddelegatebydeclaringthatConversionViewControllerconformstotheUITextFieldDelegateprotocol.Foreverydelegaterole,thereisacorrespondingprotocolthatdeclaresthemethodsthatanobjectcancallonitsdelegate.

TheUITextFieldDelegateprotocollookslikethis:protocolUITextFieldDelegate:NSObjectProtocol{optionalfunctextFieldShouldBeginEditing(textField:UITextField)->BooloptionalfunctextFieldDidBeginEditing(textField:UITextField)optionalfunctextFieldShouldEndEditing(textField:UITextField)->BooloptionalfunctextFieldDidEndEditing(textField:UITextField)optionalfunctextField(textField:UITextField,shouldChangeCharactersInRangerange:NSRange,replacementStringstring:String)->BooloptionalfunctextFieldShouldClear(textField:UITextField)->BooloptionalfunctextFieldShouldReturn(textField:UITextField)->Bool}

Thisprotocol,likeallprotocols,isdeclaredwithprotocolfollowedbyitsname,UITextFieldDelegate.TheNSObjectProtocolafterthecolonreferstotheNSObjectprotocolandtellsyouthatUITextFieldDelegateinheritsallofthemethodsintheNSObjectprotocol.ThemethodsspecifictoUITextFieldDelegatearedeclarednext.

Youcannotcreateinstancesofaprotocol;itissimplyalistofmethodsandproperties.Instead,implementationislefttoeachtypethatconformstotheprotocol.

Inaclass’sdeclaration,theprotocolsthattheclassconformstoareinacomma-delimitedlistfollowingthesuperclass(ifthereisone).InConversionViewController.swift,declarethatConversionViewControllerconformstotheUITextFieldDelegate

WOW! eBook www.wowebook.org

Page 149: iOS Programming The Big Nerd Ranch Guide, 5th

protocol.classConversionViewController:UIViewController,UITextFieldDelegate{

Protocolsusedfordelegationarecalleddelegateprotocols,andthenamingconventionforadelegateprotocolisthenameofthedelegatingclassplusthewordDelegate.Notallprotocolsaredelegateprotocols,however,andyouwillseeanexampleofadifferentkindofprotocolinChapter15.TheprotocolswehavementionedsofararepartoftheiOSSDK,butyoucanalsowriteyourownprotocols.

Usingadelegate

NowthatyouhavedeclaredConversionViewControllerasconformingtotheUITextFieldDelegateprotocol,youcansetthedelegatepropertyofthetextfield.

OpenMain.storyboardandControl-dragfromthetextfieldtotheConversionViewController.ChoosedelegatefromthepopovertoconnectthedelegatepropertyofthetextfieldtotheConversionViewController.

Next,youaregoingtoimplementtheUITextFieldDelegatemethodthatyouareinterestedin–textField(_:shouldChangeCharactersInRange:replacementString:)

Becausethetextfieldcallsthismethodonitsdelegate,youmustimplementitinConversionViewController.swift.

InConversionViewController.swift,implementtextField(_:shouldChangeCharactersInRange:replacementString:)

toprintthetextfield’scurrenttextaswellasthereplacementstring.Fornow,justreturntruefromthismethod.functextField(textField:UITextField,shouldChangeCharactersInRangerange:NSRange,replacementStringstring:String)->Bool{

print("Currenttext:\(textField.text)")print("Replacementtext:\(string)")

returntrue}

NoticethatXcodewasabletoautocompletethismethodbecauseConversionViewControllerconformstoUITextFieldDelegate.ItisagoodideatodeclareaprotocolbeforeimplementingmethodsfromtheprotocolsothatXcodecanofferthissupport.

Buildandruntheapplication.EnterseveraldigitsinthetextfieldandwatchXcode’sconsole(Figure4.12).Itprintsoutthecurrenttextofthetextfieldaswellasthereplacementstring.

WOW! eBook www.wowebook.org

Page 150: iOS Programming The Big Nerd Ranch Guide, 5th

Figure4.12Printingtotheconsole

Considerthis“currenttext”and“replacementtext”informationinlightofyourgoalofpreventingmultipledecimalseparators.Logically,iftheexistingstringhasadecimalseparatorandthereplacementstringhasadecimalseparator,thechangeshouldberejected.

UpdatetextField(_:shouldChangeCharactersInRange:replacementString:)

tousethislogic.functextField(textField:UITextField,shouldChangeCharactersInRangerange:NSRange,replacementStringstring:String)->Bool{

print("Currenttext:\(textField.text)")print("Replacementtext:\(string)")

returntrue

letexistingTextHasDecimalSeparator=textField.text?.rangeOfString(".")letreplacementTextHasDecimalSeparator=string.rangeOfString(".")

ifexistingTextHasDecimalSeparator!=nil&&replacementTextHasDecimalSeparator!=nil{returnfalse}else{returntrue}}

Buildandruntheapplication.Attempttoentermultipledecimalseparators;theapplicationwillrejecttheseconddecimalseparatorthatyouenter.

Moreonprotocols

IntheUITextFieldDelegateprotocol,therearetwokindsofmethods:methodsthathandleinformationupdatesandmethodsthathandlerequestsforinput.Forexample,the

WOW! eBook www.wowebook.org

Page 151: iOS Programming The Big Nerd Ranch Guide, 5th

textfield’sdelegateimplementsthetextFieldDidBeginEditing(_:)methodifitwantstoknowwhentheusertapsonthetextfield.

Ontheotherhand,textField(_:shouldChangeCharactersInRange:replacementString:)

isarequestforinput.Atextfieldcallsthismethodonitsdelegatetoaskwhetherthereplacementstringshouldbeacceptedorrejected.ThemethodreturnsaBool,whichisthedelegate’sanswer.

Methodsdeclaredinaprotocolcanberequiredoroptional.Bydefault,protocolmethodsarerequired,meaningthataclassconformingtotheprotocolmusthaveanimplementationofthosemethods.Ifaprotocolhasoptionalmethods,theseareprecededbythedirectiveoptional.LookingbackattheUITextFieldDelegateprotocol,youcanseethatallofitsmethodsareoptional.Thisistypicallytrueofdelegateprotocols.

WOW! eBook www.wowebook.org

Page 152: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 153: iOS Programming The Big Nerd Ranch Guide, 5th

BronzeChallenge:DisallowAlphabeticCharactersCurrently,theusercanenteralphabeticcharacterseitherbyusingaBluetoothkeyboardorbypastingcopiedtextintothetextfield.Fixthisissue.Hint:youwillwanttousetheNSCharacterSetclass.

WOW! eBook www.wowebook.org

Page 154: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 155: iOS Programming The Big Nerd Ranch Guide, 5th

5ViewControllers

AviewcontrollerisaninstanceofasubclassofUIViewController.Aviewcontrollermanagesaviewhierarchy.Itisresponsibleforcreatingviewobjectsthatmakeupthehierarchyandforhandlingeventsassociatedwiththeviewobjectsinitshierarchy.

Sofar,WorldTrotterhasasingleviewcontroller,ConversionViewController.Inthischapter,youwillupdateittousemultipleviewcontrollers.Theuserwillbeabletoswitchbetweentwoviewhierarchies–oneforviewingtheConversionViewControllerandanotherfordisplayingamap(Figure5.1).

Figure5.1ThetwofacesofWorldTrotter

WOW! eBook www.wowebook.org

Page 156: iOS Programming The Big Nerd Ranch Guide, 5th

TheViewofaViewControllerAssubclassesofUIViewController,allviewcontrollersinheritanimportantproperty:varview:UIView!

ThispropertypointstoaUIViewinstancethatistherootoftheviewcontroller’sviewhierarchy.Whentheviewofaviewcontrollerisaddedasasubviewofthewindow,theviewcontroller’sentireviewhierarchyisadded,asshowninFigure5.2.

Figure5.2ObjectdiagramforWorldTrotter

Aviewcontroller’sviewisnotcreateduntilitneedstoappearonthescreen.Thisoptimizationiscalledlazyloading,anditcanconservememoryandimproveperformance.

Therearetwowaysthataviewcontrollercancreateitsviewhierarchy:

programmatically,byoverridingtheUIViewControllermethodloadView()

inInterfaceBuilder,byusinganinterfacefilesuchasastoryboard

YouhavealreadyseenbothofthesemethodsinChapter3.First,youcreatedasampleviewhierarchyprogrammatically,thenyouswitchedtoInterfaceBuildertocreatetheinterfaceforConversionViewControllerusingastoryboardfile.YouwillcontinuetouseInterfaceBuilderinthischapterasyoufurtherexploreviewcontrollers.InChapter6,youwillgetmoreexperiencecreatingprogrammaticviewsusingloadView().

WOW! eBook www.wowebook.org

Page 157: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 158: iOS Programming The Big Nerd Ranch Guide, 5th

SettingtheInitialViewControllerAlthoughastoryboardcanhavemanyviewcontrollers,eachstoryboardfilehasexactlyoneinitialviewcontroller.Theinitialviewcontrolleractsasanentrypointintothestoryboard.Youaregoingtoaddandconfigureanotherviewcontrollertothecanvasandsetittobetheinitialviewcontrollerforthestoryboard.

OpenMain.storyboard.Fromtheobjectlibrary,dragaViewControllerontothecanvas(Figure5.3).Ifyouarerunningoutofavailablespace,youcanControl-clickonthebackgroundtochooseadifferentzoomlevel.

Figure5.3Addingaviewcontrollertothecanvas

YouwantthisviewcontrollertodisplayanMKMapView–aclassdesignedtodisplayamap–insteadoftheexistingwhiteUIView.

SelecttheviewoftheViewController–nottheViewControlleritself!–andpressDeletetoremovethisviewfromthecanvas.ThendragaMapKitViewfromtheobjectlibraryontotheviewcontrollertosetitastheviewforthisviewcontroller(Figure5.4).

WOW! eBook www.wowebook.org

Page 159: iOS Programming The Big Nerd Ranch Guide, 5th

Figure5.4Addingamapviewtothecanvas

NowselecttheViewControllerandopenitsattributesinspector.UndertheViewControllersection,checktheboxnexttoIsInitialViewController(Figure5.5).DidyounoticethatthegrayarrowonthecanvasthatwaspointingattheConversionViewControllerisnowpointingtotheViewController?Thearrow,asyouhaveprobablysurmised,indicatestheinitialviewcontroller.Anotherwaytoassigntheinitialviewcontrolleristodragthatarrowfromoneviewcontrollertoanotheronthecanvas.

Figure5.5Settingtheinitialviewcontroller

Thereisaquirkthatwouldcauseproblemsifyouweretobuildandruntheapprightnow.(Tryit,ifyoulike.)MKMapViewisinaframeworkthatisnotcurrentlybeingloadedintotheapplication.Aframeworkisasharedlibraryofcodethatincludesassociatedresources

WOW! eBook www.wowebook.org

Page 160: iOS Programming The Big Nerd Ranch Guide, 5th

suchasinterfacefilesandimages.YoubrieflylearnedaboutframeworksinChapter3,andyouhavebeenusingacoupleofframeworksalready:UIKitandFoundationarebothframeworks.

Sofar,youhavebeenincludingframeworksinyourappbyusingtheimportkeyword,likeso:importUIKit

NowyouneedtoimporttheMapKitframeworkfortheMKMapViewtoload.However,ifyouimporttheMapKitframeworkusingtheimportkeywordwithoutincludinganycodethatusesthatframework,thecompilerwilloptimizeitout–eventhoughyouareusingamapviewinyourstoryboard.

Instead,youneedtomanuallylinktheMapKitframeworktotheapp.

Withtheprojectnavigatoropen,clickontheWorldTrotterprojectatthetopofthelisttoopentheprojectsettings.FindandopentheGeneraltabinthesettings.ScrolldowntothebottomandfindthesectionlabeledLinkedFrameworksandLibraries.Clickonthe+atthebottomandsearchforMapKit.framework.SelectthisframeworkandclickAdd(Figure5.6).

Figure5.6AddingtheMapKitframework

Nowyoucanbuildandruntheapplication.Sinceyouhavechangedtheinitialviewcontroller,themapshowsupinsteadoftheviewoftheConversionViewController.

Asmentionedabove,therecanonlyeverbeoneinitialviewcontrollerassociatedwithagivenstoryboard.YousawthisearlierwhenyousettheViewControllertobetheinitialviewcontroller.Atthatpoint,theConversionViewControllerwasnolongertheinitialviewcontrollerforthisstoryboard.Let’stakealookathowthisrequirementworkswiththerootlevelUIWindowtoaddtheinitialviewcontroller’sviewtothewindowhierarchy.

UIWindowhasarootViewControllerproperty.Whenaviewcontrollerissetasthewindow’srootViewController,thatviewcontroller’sviewgetsaddedtothewindow’sviewhierarchy.Whenthispropertyisset,anyexistingsubviewsonthewindowareremovedandviewcontroller’sviewgetsaddedtothewindowwiththeappropriateAutoLayoutconstraints.

Eachapplicationhasonemaininterface,areferencetoastoryboard.Whentheapplicationlaunches,theinitialviewcontrollerforthemaininterfacegetssetastherootViewControllerofthewindow.

WOW! eBook www.wowebook.org

Page 161: iOS Programming The Big Nerd Ranch Guide, 5th

Themaininterfaceforanapplicationissetintheprojectsettings.StillintheGeneraltaboftheprojectsettings,findtheDeploymentInfosection.HereyouwillseetheMainInterfacesetting(Figure5.7).ThisissettoMain,whichcorrespondstoMain.storyboard.

Figure5.7Anapplication’smaininterface

WOW! eBook www.wowebook.org

Page 162: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 163: iOS Programming The Big Nerd Ranch Guide, 5th

UITabBarControllerViewcontrollersbecomemoreinterestingwhentheuserhasawaytoswitchbetweenthem.Throughoutthebook,youwilllearnanumberofwaystopresentviewcontrollers.Inthischapter,youwillcreateaUITabBarControllerthatwillallowtheusertoswapbetweentheConversionViewControllerandtheUIViewControllerdisplayingthemap.

UITabBarControllerkeepsanarrayofviewcontrollers.Italsomaintainsatabbaratthebottomofthescreenwithatabforeachviewcontrollerinitsarray.Tappingonatabresultsinthepresentationoftheviewoftheviewcontrollerassociatedwiththattab.

OpenMain.storyboardandselecttheViewController.FromtheEditormenu,chooseEmbedIn→TabBarController.ThiswilladdtheViewControllertotheviewcontrollersarrayoftheTabBarController.YoucanseethisrepresentedbytheRelationshiparrowpointingfromtheTabBarControllertotheViewController(Figure5.8).Additionally,InterfaceBuilderknowstomaketheTarBarControllertheinitialviewcontrollerforthestoryboard.

Figure5.8Tabbarcontrollerwithoneviewcontroller

Atabbarcontrollerisnotveryusefulwithjustoneviewcontroller.AddtheConversionViewControllertotheTabBarController’sviewcontrollersarray.

Control-dragfromtheTabBarControllertotheConversionViewController.FromtheRelationshipSeguesection,chooseviewcontrollers(Figure5.9).

WOW! eBook www.wowebook.org

Page 164: iOS Programming The Big Nerd Ranch Guide, 5th

Figure5.9Addingaviewcontrollertothetabbarcontroller

Buildandruntheapplication.Taponthetwotabsatthebottomtoswitchbetweenthetwoviewcontrollers.Atthemoment,thetabsjustsay“Item,”whichisnotveryhelpful.Inthenextsection,youwillupdatethetabbaritemstomakethetabsmoredescriptiveandobvious.

UITabBarControllerisitselfasubclassofUIViewController.AUITabBarController’sviewisaUIViewwithtwosubviews:thetabbarandtheviewoftheselectedviewcontroller(Figure5.10).

WOW! eBook www.wowebook.org

Page 165: iOS Programming The Big Nerd Ranch Guide, 5th

Figure5.10UITabBarControllerdiagram

Tabbaritems

Eachtabonthetabbarcandisplayatitleandanimage,andeachviewcontrollermaintainsatabBarItempropertyforthispurpose.WhenaviewcontrolleriscontainedbyaUITabBarController,itstabbaritemappearsinthetabbar.Figure5.11showsanexampleofthisrelationshipiniPhone’sPhoneapplication.

WOW! eBook www.wowebook.org

Page 166: iOS Programming The Big Nerd Ranch Guide, 5th

Figure5.11UITabBarItemexample

First,youneedtoaddafewfilestoyourprojectthatwillbetheimagesforthetabbaritems.Intheprojectnavigator,opentheAssetCatalogbyopeningAssets.xcassets.Then,findConvertIcon.png,[email protected],[email protected],MapIcon.png,[email protected],andMapIcon@3x.pngintheResourcesdirectoryofthefilethatyoudownloadedearlier(http://www.bignerdranch.com/solutions/iOSProgramming5ed.zip).DragthesefilesintotheimagessetlistontheleftsideoftheAssetCatalog(Figure5.12).

WOW! eBook www.wowebook.org

Page 167: iOS Programming The Big Nerd Ranch Guide, 5th

Figure5.12AddingimagestotheAssetCatalog

Thetabbaritempropertiescanbeseteitherprogrammaticallyorinastoryboard.Sinceyourdataisstatic,thestoryboardwillbethebestplacetosetthetabbaritemproperties.

InMain.storyboard,locatetheViewController.Noticethatatabbarwiththetabbariteminitwasaddedtotheinterfacesincetheviewcontrollerwillbepresentedwithinatabbarcontroller.Thisisveryusefulwhenlayingoutyourinterface.

Selectthistabbaritemandopenitsattributesinspector.UndertheBarItemsection,changetheTitleto“Map”andchooseMapIconfromtheImagemenu.Youcanalsochangethetextofthetabbaritembydouble-clickingonthetextonthecanvas.Thetabbarwillbeupdatedtoreflectthesevalues(Figure5.13).

Figure5.13ViewController’stabbaritem

NowfindtheConversionViewControllerandselectitstabbaritem.SettheTitletobe“Convert”andtheImagetobeConvertIcon.

Let’salsochangethefirsttabtobetheConvertViewController.Theorderofthetabsisdeterminedbytheorderoftheviewcontrollerswithinthetabbarcontroller’sviewControllersarray.YoucanchangetheorderinastoryboardbydraggingthetabsatthebottomoftheTabBarController.

WOW! eBook www.wowebook.org

Page 168: iOS Programming The Big Nerd Ranch Guide, 5th

FindtheTabBarControlleronthecanvas.DragtheConverttabtobeinthefirstposition.

Buildandruntheapplication.Notonlyarethetabbaritemsatthebottommoredescriptive,buttheConvertViewControllerisnowthefirstviewcontrollerthatisdisplayed(Figure5.14).

Figure5.14Tabbaritemswithlabelsandicons

WOW! eBook www.wowebook.org

Page 169: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 170: iOS Programming The Big Nerd Ranch Guide, 5th

LoadedandAppearingViewsNowthatyouhavetwoviewcontrollers,thelazyloadingofviewsmentionedearlierbecomesmoreimportant.

Whentheapplicationlaunches,thetabbarcontrollerdefaultstoloadingtheviewofthefirstviewcontrollerinitsarray,whichistheConvertViewController.ThismeansthattheMapViewController’sviewisnotneededandwillonlybeneededwhen(orif)theusertapsthetabtoseeit.

Youcantestthisbehaviorforyourself.Whenaviewcontrollerfinishesloadingitsview,viewDidLoad()iscalled,andyoucanoverridethismethodtomakeitprintamessagetotheconsole,allowingyoutoseethatitwascalled.

Youaregoingtoaddcodetobothviewcontrollers.However,thereisnocodecurrentlyassociatedwiththeviewcontrollerdisplayingthemapbecauseeverythinghasbeenconfiguredusingthestoryboard.Nowthatyouwanttoaddcodetothatviewcontroller,youaregoingtocreateaviewcontrollersubclassandassociateitwiththatinterface.

CreateanewSwiftfile(Command-N)andnameitMapViewController.OpenMapViewController.swiftanddefineaUIViewControllersubclassnamedMapViewController.importFoundationimportUIKit

classMapViewController:UIViewController{

}

NowopenMain.storyboardandselectthemap’sviewcontroller.OpenitsidentityinspectorandchangetheClasstoMapViewController.

NowthatyouhaveassociatedtheMapViewControllerclasswiththeviewcontrolleronthecanvas,youcanaddcodetobothConversionViewControllerandMapViewControllertoprinttotheconsolewhentheirviewDidLoad()methodiscalled.

InConversionViewController.swift,overrideviewDidLoad()toprintastatementtotheconsole.overridefuncviewDidLoad(){//AlwayscallthesuperimplementationofviewDidLoadsuper.viewDidLoad()

print("ConversionViewControllerloadeditsview.")}

InMapViewController.swift,overridethesamemethod.overridefuncviewDidLoad(){//AlwayscallthesuperimplementationofviewDidLoadsuper.viewDidLoad()

print("MapViewControllerloadeditsview.")}

Buildandruntheapplication.Theconsolereportsthat

WOW! eBook www.wowebook.org

Page 171: iOS Programming The Big Nerd Ranch Guide, 5th

ConversionViewControllerloadeditsviewrightaway.TapMapViewController’stab,andtheconsolewillreportthatitsviewisnowloaded.Atthispoint,bothviewshavebeenloaded,soswitchingbetweenthetabsnowwillnolongertriggerviewDidLoad().(Tryitandsee.)

Accessingsubviews

Often,youwillwanttodosomeextrainitializationorconfigurationofsubviewsdefinedinInterfaceBuilderbeforetheyappeartotheuser.Sowherecanyouaccessasubview?Therearetwomainoptions,dependingonwhatyouneedtodo.ThefirstoptionistheviewDidLoad()methodthatyouoverrodetospotlazyloading.Thismethodiscalledaftertheviewcontroller’sinterfacefileisloaded,atwhichpointalloftheviewcontroller’soutletswillreferencetheappropriateobjects.ThesecondoptionisanotherUIViewControllermethod,viewWillAppear(_:).Thismethodiscalledjustbeforeaviewcontroller’sviewisaddedtothewindow.

Whichshouldyouchoose?OverrideviewDidLoad()iftheconfigurationonlyneedstobedoneonceduringtherunoftheapp.OverrideviewWillAppear(_:)ifyouneedtheconfigurationtobedoneeachtimetheviewcontroller’sviewappearsonscreen.

WOW! eBook www.wowebook.org

Page 172: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 173: iOS Programming The Big Nerd Ranch Guide, 5th

InteractingwithViewControllersandTheirViewsLet’slookatsomemethodsthatarecalledduringthelifecycleofaviewcontrolleranditsview.Someofthesemethodsyouhavealreadyseen,andsomearenew.

init(coder:)istheinitializerforUIViewControllerinstancescreatedfromastoryboard.

Whenaviewcontrollerinstanceiscreatedfromastoryboard,itsinit(coder:)getscalledonce.YouwilllearnmoreaboutthismethodinChapter15.

init(nibName:bundle:)isthedesignatedinitializerforUIViewController.

Whenaviewcontrollerinstanceiscreatedwithouttheuseofastoryboard,itsinit(nibName:bundle:)getscalledonce.Notethatinsomeapps,youmayendupcreatingseveralinstancesofthesameviewcontrollerclass.Thismethodwillgetcalledonceoneachviewcontrollerasitiscreated.

loadView()isoverriddentocreateaviewcontroller’sviewprogrammatically.

viewDidLoad()isoverriddentoconfigureviewscreatedbyloadinganinterfacefile.Thismethodgetscalledaftertheviewofaviewcontrolleriscreated.

viewWillAppear(_:)isoverriddentoconfigureviewscreatedbyloadinganinterfacefile.

ThismethodandviewDidAppear(_:)getcalledeverytimeyourviewcontrollerismovedonscreen.viewWillDisappear(_:)andviewDidDisappear(_:)getcalledeverytimeyourviewcontrollerismovedoffscreen.

WOW! eBook www.wowebook.org

Page 174: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 175: iOS Programming The Big Nerd Ranch Guide, 5th

SilverChallenge:DarkModeWhenevertheConversionViewControllerisviewed,updateitsbackgroundcolorbasedonthetimeofday.Intheevening,thebackgroundshouldbeadarkcolor.Otherwise,thebackgroundshouldbealightcolor.YouwillneedtooverrideviewWillAppear(_:)toaccomplishthis.(Ifthatisnotenoughexcitementinyourlife,youcanchangethebackgroundcoloreachtimetheviewcontrollerisviewed.)

WOW! eBook www.wowebook.org

Page 176: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 177: iOS Programming The Big Nerd Ranch Guide, 5th

FortheMoreCurious:RetinaDisplayWiththereleaseofiPhone4,AppleintroducedtheRetinadisplayforiPhoneandiPodtouch.TheRetinadisplayhasmuchhigherresolutioncomparedtoearlierdevices.Let’slookatwhatyoushoulddotomakegraphicslooktheirbestonbothdisplays.

Forvectorgraphics,youdonotneedtodoanything;yourcodewillrenderascrisplyasthedeviceallows.However,ifyoudrawusingCoreGraphicsfunctions,thesegraphicswillappeardifferentlyondifferentdevices.InCoreGraphics(alsocalledQuartz),lines,curves,text,etc.aredescribedintermsofpoints.Onanon-Retinadisplay,apointis1x1pixel.OnmostRetinadisplays,apointis2x2pixels(Figure5.15).TheexceptionsareiPhone6PlusandiPhone6sPlus,whichhaveahigherresolutionRetinadisplaywhereapointis3x3pixels.

Figure5.15Renderingtodifferentresolutions

Giventhesedifferences,bitmapimages(likeJPEGorPNGfiles)willbeunattractiveiftheimageisnottailoredtothedevice’sscreentype.Sayyourapplicationincludesasmallimageof25x25pixels.Ifthisimageisdisplayedona2xRetinadisplay,thentheimagemustbestretchedtocoveranareaof50x50pixels.Atthispoint,thesystemdoesatypeofaveragingcalledanti-aliasingtokeeptheimagefromlookingjagged.Theresultisanimagethatisnotjagged–butitisfuzzy(Figure5.16).

WOW! eBook www.wowebook.org

Page 178: iOS Programming The Big Nerd Ranch Guide, 5th

Figure5.16Fuzzinessfromstretchinganimage

Youcouldusealargerfileinstead,buttheaveragingwouldthencauseproblemsintheotherdirectionwhentheimageisshrunkforanon-Retinadisplay.Theonlysolutionistobundletwoimagefileswithyourapplication:oneatapixelresolutionequaltothenumberofpointsonthescreenfornon-RetinadisplaysandonetwicethatsizeinpixelsforRetinadisplays.

Fortunately,youdonothavetowriteanyextracodetohandlewhichimagegetsloadedonwhichdevice.Allyouhavetodoissuffixthehigher-resolutionimagefor2xRetinadisplayswith@2xandthehigher-resolutionimagefor3xRetinadisplayswith@3x.Makesurethe@2x(or@3x)goesbeforethefileextension:[email protected],[email protected],whenyouuseUIImage’sinit(named:)initializertoloadtheimage,thismethodlooksinthebundleandgetstheappropriatefilefortheparticulardevice.

WOW! eBook www.wowebook.org

Page 179: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 180: iOS Programming The Big Nerd Ranch Guide, 5th

6ProgrammaticViews

Inthischapter,youwillupdateWorldTrottertocreatetheviewforMapViewControllerprogrammatically(Figure6.1).Indoingso,youwilllearnmoreaboutviewcontrollersandhowtosetupconstraintsandcontrols(suchasUIButtons)programmatically.

Figure6.1WorldTrotterwithprogrammaticviews

Currently,theviewforMapViewControllerisdefinedinthestoryboard.Thefirststep,then,istoremovethisviewfromthestoryboardsoyoucaninsteadcreateitprogrammatically.

InMain.storyboard,selectthemapviewassociatedwithMapViewControllerandpressDelete(Figure6.2).

WOW! eBook www.wowebook.org

Page 181: iOS Programming The Big Nerd Ranch Guide, 5th

Figure6.2Deletingtheview

WOW! eBook www.wowebook.org

Page 182: iOS Programming The Big Nerd Ranch Guide, 5th

CreatingaViewProgrammaticallyYoulearnedinChapter5thattocreateaviewcontroller’sviewprogrammatically,youoverridetheUIViewControllermethodloadView().

OpenMapViewController.swiftandoverrideloadView()tocreateaninstanceofMKMapViewandsetitastheviewoftheviewcontroller.Youwillneedareferencetothemapviewlateron,socreateapropertyforitaswell.importUIKitimportMapKit

classMapViewController:UIViewController{

varmapView:MKMapView!

overridefuncloadView(){//CreateamapviewmapView=MKMapView()

//Setitas*the*viewofthisviewcontrollerview=mapView}

overridefuncviewDidLoad(){super.viewDidLoad()

print("MapViewControllerloadeditsview.")}

}

Whenaviewcontrolleriscreated,itsviewpropertyisnil.Ifaviewcontrollerisaskedforitsviewanditsviewisnil,thentheloadView()methodiscalled.

Buildandruntheapplication.Althoughtheapplicationlooksthesame,themapviewisbeingcreatedprogrammaticallyinsteadofthroughInterfaceBuilder.

WOW! eBook www.wowebook.org

Page 183: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 184: iOS Programming The Big Nerd Ranch Guide, 5th

ProgrammaticConstraintsInChapter3,youlearnedaboutAutoLayoutconstraintsandhowtoaddthemusingInterfaceBuilder.Inthissection,youwilllearnhowtoaddconstraintstoaninterfaceprogrammatically.

ApplerecommendsthatyoucreateandconstrainyourviewsinInterfaceBuilderwheneverpossible.However,ifyourviewsarecreatedincode,thenyouwillneedtoconstrainthemprogrammatically.TheinterfaceforMapViewControlleriscreatedprogrammatically,soitisagreatcandidateforprogrammaticconstraints.

Tolearnaboutprogrammaticconstraints,youaregoingtoaddaUISegmentedControltoMapViewController’sinterface.Asegmentedcontrolallowstheusertochoosebetweenadiscretesetofoptions,andyouwilluseonetoallowtheusertoswitchbetweenmaptypes:standard,hybrid,andsatellite.

InMapViewController.swift,updateloadView()toaddasegmentedcontroltotheinterface.overridefuncloadView(){//CreateamapviewmapView=MKMapView()

//Setitas*the*viewofthisviewcontrollerview=mapView

letsegmentedControl=UISegmentedControl(items:["Standard","Hybrid","Satellite"])segmentedControl.backgroundColor=UIColor.whiteColor().colorWithAlphaComponent(0.5)segmentedControl.selectedSegmentIndex=0

segmentedControl.translatesAutoresizingMaskIntoConstraints=falseview.addSubview(segmentedControl)}

Thelineofcoderegardingtranslatingconstraintshastodowithanoldersystemforscalinginterfaces–autoresizingmasks.BeforeAutoLayoutwasintroduced,iOSapplicationsusedautoresizingmaskstoallowviewstoscalefordifferent-sizedscreensatruntime.

Everyviewhasanautoresizingmask.Bydefault,iOScreatesconstraintsthatmatchtheautoresizingmaskandaddsthemtotheview.Thesetranslatedconstraintswilloftenconflictwithexplicitconstraintsinthelayoutandcauseanunsatisfiableconstraintsproblem.ThefixistoturnoffthisdefaulttranslationbysettingthepropertytranslatesAutoresizingMaskIntoConstraintstofalse.(ThereismoreaboutAutoLayoutandautoresizingmasksattheendofthischapter.)

Anchors

WhenyouworkwithAutoLayoutprogrammatically,youwilluseanchorstocreateyourconstraints.Anchorsarepropertiesontheviewthatcorrespondtoattributesthatyoumightwanttoconstraintoananchoronanotherview.Forexample,youmightconstraintheleadinganchorofoneviewtotheleadinganchorofanotherview.Thiswouldhavethe

WOW! eBook www.wowebook.org

Page 185: iOS Programming The Big Nerd Ranch Guide, 5th

effectofthetwoviews’leadingedgesbeingaligned.

Let’screatesomeconstraintstodothefollowing.

Thetopanchorofthesegmentedcontrolshouldbeequaltothetopanchorofitssuperview.

Theleadinganchorofthesegmentedcontrolshouldbeequaltotheleadinganchorofitssuperview.

Thetrailinganchorofthesegmentedcontrolshouldbeequaltothetrailinganchorofitssuperview.

InMapViewController.swift,createtheseconstraintsinloadView().letsegmentedControl=UISegmentedControl(items:["Standard","Hybrid","Satellite"])segmentedControl.backgroundColor=UIColor.whiteColor().colorWithAlphaComponent(0.5)segmentedControl.selectedSegmentIndex=0

segmentedControl.translatesAutoresizingMaskIntoConstraints=falseview.addSubview(segmentedControl)

lettopConstraint=segmentedControl.topAnchor.constraintEqualToAnchor(view.topAnchor)letleadingConstraint=segmentedControl.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor)lettrailingConstraint=segmentedControl.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor)

(Notethatduetopagesizerestrictionsweareshowingeachconstant’sdeclarationsplitacrosstwolines.Youshouldentereachdeclarationonasingleline.)

Xcodewillalertyoutoaproblemwitheachlineyouhaveentered.Youwillfixtheminamoment.

AnchorshaveamethodconstraintEqualToAnchor(_:)thatwillcreateaconstraintbetweenthetwoanchors.ThereareafewotherconstraintcreationmethodsonNSLayoutAnchor,includingonethatacceptsaconstantasanargument:funcconstraintEqualToAnchor(anchor:NSLayoutAnchor!,constantc:CGFloat)->NSLayoutConstraint!

Activatingconstraints

YounowhavethreeNSLayoutConstraintinstances.However,theseconstraintswillhavenoeffectonthelayoutuntilyouexplicitlyactivatethembysettingtheiractivepropertiestotrue.ThiswillresolveXcode’scomplaint.

InMapViewController.swift,activatetheconstraintsattheendofloadView().lettopConstraint=segmentedControl.topAnchor.constraintEqualToAnchor(view.topAnchor)letleadingConstraint=segmentedControl.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor)lettrailingConstraint=segmentedControl.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor)

topConstraint.active=trueleadingConstraint.active=truetrailingConstraint.active=true

WOW! eBook www.wowebook.org

Page 186: iOS Programming The Big Nerd Ranch Guide, 5th

Constraintsneedtobeaddedtothemostrecentcommonancestorfortheviewsassociatedwiththeconstraint.Figure6.3showsaviewhierarchyalongwiththecommonancestorfortwoviews.

Figure6.3Commonancestor

Ifaconstraintonlyhasoneview(suchaswhenaddingawidthorheightconstrainttoaview),thenthatviewisconsideredthecommonancestor.

Bysettingtheactivepropertyonaconstrainttotrue,theconstraintwillworkitswayupthehierarchyfortheitemstofindthecommonancestortoaddtheconstraintto.ItwillthencallthemethodaddConstraint(_:)ontheappropriateview.SettingtheactivepropertyispreferabletocallingaddConstraint(_:)orremoveConstraint(_:)yourself.

BuildandruntheapplicationandswitchtotheMapViewController.Thesegmentedcontrolisnowpinnedtothetop,leading,andtrailingedgesofitssuperview(Figure6.4).

WOW! eBook www.wowebook.org

Page 187: iOS Programming The Big Nerd Ranch Guide, 5th

Figure6.4Segmentedcontroladdedtothescreen

Althoughtheconstraintsaredoingtherightthing,theinterfacedoesnotlookgood.Thesegmentedcontrolisunderlappingthestatusbar,anditwouldlookbetterifthesegmentedcontrolwasinsetfromtheleadingandtrailingedgesofthescreen.Let’stacklethestatusbarissuefirst.

Layoutguides

Viewcontrollersexposetwolayoutguidestoassistwithlayoutcontent:thetopLayoutGuideandthebottomLayoutGuide.Thelayoutguidesindicatetheextenttowhichtheviewcontroller’sviewcontentswillbevisible.UsingtopLayoutGuidewillallowyourcontenttonotunderlapthestatusbarornavigationbaratthetopofthescreen.(YouwilllearnaboutnavigationbarsinChapter13.)UsingthebottomLayoutGuidewillallowyourcontenttonotunderlapthetabbaratthebottomofthescreen.

Thelayoutguidesexposethreeanchorsthatyoucanusetoaddconstraints:topAnchor,bottomAnchor,andheightAnchor.Sinceyouwantthesegmentedcontroltobeunderthestatusbar,youwillconstrainthebottomanchorofthetoplayoutguidetothetopanchorofthesegmentedcontrol.

InMapViewController.swift,updatethesegmentedcontrol’sconstraintsinloadView().Makethesegmentedcontrolbe8pointsbelowthetoplayoutguide.lettopConstraint=segmentedControl.topAnchor.constraintEqualToAnchor(view.topAnchor)lettopConstraint=segmentedControl.topAnchor.constraintEqualToAnchor(topLayoutGuide.bottomAnchor,constant:8)letleadingConstraint=segmentedControl.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor)lettrailingConstraint=segmentedControl.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor)

topConstraint.active=trueleadingConstraint.active=truetrailingConstraint.active=true

Buildandruntheapplication.Thesegmentedcontrolnowappearsbelowthestatusbar.Byusingthelayoutguidesinsteadofahardcodedconstant,theviewswilladaptbasedonthecontexttheyappearin.

WOW! eBook www.wowebook.org

Page 188: iOS Programming The Big Nerd Ranch Guide, 5th

Nowlet’supdatethesegmentedcontrolsothatitisinsetfromtheleadingandtrailingedgesofitssuperview.

Margins

Althoughyoucouldinsetthesegmentedcontrolusingaconstantontheconstraint,itismuchbettertousethemarginsoftheviewcontroller’sview.

EveryviewhasalayoutMarginspropertythatdenotesthedefaultspacingtousewhenlayingoutcontent.ThispropertyisaninstanceofUIEdgeInsets,whichyoucanthinkofasatypeofframe.Whenaddingconstraints,youwillusethelayoutMarginsGuide,whichexposesanchorsthataretiedtotheedgesofthelayoutMargins.

Theprimaryadvantageofusingthemarginsisthatthemarginscanchangedependingonthedevicetype(iPadoriPhone)aswellasthesizeofthedevice(iPhone6soriPhone6sPlus).Usingthemarginswillgiveyoucontentthatlooksgoodonanydevice.

Updatethesegmentedcontrol’sleadingandtrailingconstraintsinloadView()tousethemargins.lettopConstraint=segmentedControl.topAnchor.constraintEqualToAnchor(topLayoutGuide.bottomAnchor,constant:8)

letleadingConstraint=segmentedControl.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor)lettrailingConstraint=segmentedControl.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor)

letmargins=view.layoutMarginsGuideletleadingConstraint=segmentedControl.leadingAnchor.constraintEqualToAnchor(margins.leadingAnchor)lettrailingConstraint=segmentedControl.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor)

topConstraint.active=trueleadingConstraint.active=truetrailingConstraint.active=true

Buildandruntheapplicationagain.Thesegmentedcontrolisnowinsetfromtheview’smargins(Figure6.5).

Figure6.5Segmentedcontrolwithupdatedconstraints

WOW! eBook www.wowebook.org

Page 189: iOS Programming The Big Nerd Ranch Guide, 5th

Explicitconstraints

Itishelpfultounderstandhowthesemethodsthatyouhaveusedcreateconstraints.NSLayoutConstrainthasthefollowinginitializer:convenienceinit(itemview1:AnyObject,attributeattr1:NSLayoutAttribute,relatedByrelation:NSLayoutRelation,toItemview2:AnyObject?,attributeattr2:NSLayoutAttribute,multiplier:CGFloat,constantc:CGFloat)

Thisinitializercreatesasingleconstraintusingtwolayoutattributesoftwoviewobjects.Themultiplieristhekeytocreatingaconstraintbasedonaratio.Theconstantisafixednumberofpoints,likeyouhaveusedinyourspacingconstraints.

ThelayoutattributesaredefinedasconstantsintheNSLayoutConstraintclass:

NSLayoutAttribute.Left

NSLayoutAttribute.Leading

NSLayoutAttribute.Trailing

NSLayoutAttribute.Top

NSLayoutAttribute.Bottom

NSLayoutAttribute.Width

NSLayoutAttribute.Height

NSLayoutAttribute.CenterX

NSLayoutAttribute.CenterY

NSLayoutAttribute.FirstBaseline

NSLayoutAttribute.LastBaseline

Thereareadditionalattributesthathandlethemarginsassociatedwithaview,suchasNSLayoutAttribute.LeadingMargin.

Let’sconsiderahypotheticalconstraint.Sayyouwantedtheimageviewtobe1.5timesaswideasitistall.Youcouldcreateitwiththefollowingcode.(Donottypethishypotheticalconstraintinyourcode!Itwillconflictwithothersyoualreadyhave.)letaspectConstraint=NSLayoutConstraint(item:imageView,attribute:.Width,relatedBy:.Equal,toItem:imageView,attribute:.Height,multiplier:1.5,constant:0.0)

Tounderstandhowthisinitializerworks,thinkofthisconstraintastheequationshowninFigure6.6.

WOW! eBook www.wowebook.org

Page 190: iOS Programming The Big Nerd Ranch Guide, 5th

Figure6.6NSLayoutConstraintequation

Yourelatealayoutattributeofoneviewtothelayoutattributeofanotherviewusingamultiplierandaconstanttodefineasingleconstraint.

WOW! eBook www.wowebook.org

Page 191: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 192: iOS Programming The Big Nerd Ranch Guide, 5th

ProgrammaticControlsNowlet’supdatethesegmentedcontroltochangethemaptypewhentheusertapsonasegment.

AUISegmentedControlisasubclassofUIControl.YouhaveworkedwithanotherUIControlsubclassinChapter1,theUIButtonclass.Controlsareresponsibleforcallingmethodsontheirtargetinresponsetosomeevent.

ControleventsareoftypeUIControlEvents.Hereareafewofthecommoncontroleventsthatyouwilluse:

UIControlEvents.TouchDown

Atouchdownonthecontrol.UIControlEvents.TouchUpInside

Atouchdownfollowedbyatouchupwhilestillwithintheboundsofthecontrol.

UIControlEvents.ValueChanged

Atouchthatcausesthevalueofthecontroltochange.

UIControlEvents.EditingChanged

AtouchthatcausesaneditingchangeforaUITextField.

Youused.TouchUpInsidefortheUIButtoninChapter1(itisthedefaulteventwhenyouControl-dragtoconnectactionsinInterfaceBuilder),andyousawthe.EditingChangedeventinChapter4.Forthesegmentedcontrol,youwillusethe.ValueChangedevent.

InMapViewController.swift,updateloadView()toaddatarget-actionpairtothesegmentedcontrolandassociateitwiththe.ValueChangedevent.overridefuncloadView(){

mapView=MKMapView()view=mapView

letsegmentedControl=UISegmentedControl(items:["Standard","Satellite","Hybrid"])segmentedControl.backgroundColor=UIColor.whiteColor().colorWithAlphaComponent(0.5)segmentedControl.selectedSegmentIndex=0

segmentedControl.addTarget(self,action:"mapTypeChanged:",forControlEvents:.ValueChanged)

...

Next,implementtheactionmethodthattheeventwilltrigger.Thismethodwillcheckwhichsegmentwasselectedandupdatethemapaccordingly.funcmapTypeChanged(segControl:UISegmentedControl){switchsegControl.selectedSegmentIndex{case0:mapView.mapType=.Standardcase1:

WOW! eBook www.wowebook.org

Page 193: iOS Programming The Big Nerd Ranch Guide, 5th

mapView.mapType=.Hybridcase2:mapView.mapType=.Satellitedefault:break}}

Buildandruntheapplication.Changetheselectedsegmentandthemapwillupdateaccordingly.

WOW! eBook www.wowebook.org

Page 194: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 195: iOS Programming The Big Nerd Ranch Guide, 5th

BronzeChallenge:AnotherTabCreateanewviewcontrollerandaddittothetabbarcontroller.ThisviewcontrollershoulddisplayaWKWebView,whichisaclassusedtodisplaywebcontent.Thewebviewshoulddisplayhttps://www.bignerdranch.comforyoutobookyournextvacation.

WOW! eBook www.wowebook.org

Page 196: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 197: iOS Programming The Big Nerd Ranch Guide, 5th

SilverChallenge:User’sLocationAddabuttontotheMapViewControllerthatdisplaysandzoomsinontheuser’scurrentlocation.Youwillneedtousedelegationtoaccomplishthis.RefertothedocumentationforMKMapViewDelegate.

WOW! eBook www.wowebook.org

Page 198: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 199: iOS Programming The Big Nerd Ranch Guide, 5th

GoldChallenge:DroppingPinsMapviewscandisplaypins,whichareinstancesofMKPinAnnotationView.Addthreepinstothemapview:onewhereyouwereborn,onewhereyouarenow,andoneataninterestinglocationyouhavevisitedinthepast.Addabuttontotheinterfacethatallowsthemaptodisplaythelocationofapin.Subsequenttapsshouldsimplycyclethroughthelistofpins.

WOW! eBook www.wowebook.org

Page 200: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 201: iOS Programming The Big Nerd Ranch Guide, 5th

FortheMoreCurious:NSAutoresizingMaskLayoutConstraintAswementionedearlier,beforeAutoLayoutiOSapplicationsusedanothersystemformanaginglayout:autoresizingmasks.Eachviewhadanautoresizingmaskthatconstrainedtherelationshipbetweenaviewanditssuperview,butthismaskcouldnotaffectrelationshipsbetweensiblingviews.

Bydefault,viewscreateandaddconstraintsbasedontheirautoresizingmasks.However,thesetranslatedconstraintsoftenconflictwithyourexplicitconstraintsinyourlayout,whichresultsanunsatisfiableconstraintsproblem.

Toseethishappen,commentoutthelineinloadView()thatturnsoffthetranslationofautoresizingmasks.//segmentedControl.translatesAutoresizingMaskIntoConstraints=falseview.addSubview(segmentedControl)

Nowthesegmentedcontrolhasaresizingmaskthatwillbetranslatedintoaconstraint.Buildandruntheapplicationandnavigatetothemapinterface.Youwillnotlikewhatyousee.Theconsolewillreporttheproblemanditssolution.Unabletosimultaneouslysatisfyconstraints.Probablyatleastoneoftheconstraintsinthefollowinglistisoneyoudon'twant.Trythis:(1)lookateachconstraintandtrytofigureoutwhichyoudon'texpect;(2)findthecodethataddedtheunwantedconstraintorconstraintsandfixit.(Note:Ifyou'reseeingNSAutoresizingMaskLayoutConstraintsthatyoudon'tunderstand,refertothedocumentationfortheUIViewpropertytranslatesAutoresizingMaskIntoConstraints)("<NSAutoresizingMaskLayoutConstraint:0x7fb6b8e0ad00h=--&v=--&H:[UISegmentedControl:0x7fb6b9897390(212)]>","<NSLayoutConstraint:0x7fb6b9975350UISegmentedControl:0x7fb6b9897390.leading==UILayoutGuide:0x7fb6b9972640'UIViewLayoutMarginsGuide'.leading>","<NSLayoutConstraint:0x7fb6b9975460UISegmentedControl:0x7fb6b9897390.trailing==UILayoutGuide:0x7fb6b9972640'UIViewLayoutMarginsGuide'.trailing>","<NSLayoutConstraint:0x7fb6b8e0b370'UIView-Encapsulated-Layout-Width'H:[MKMapView:0x7fb6b8d237c0(0)]>","<NSLayoutConstraint:0x7fb6b9972020'UIView-leftMargin-guide-constraint'H:|-(0)-[UILayoutGuide:0x7fb6b9972640'UIViewLayoutMarginsGuide'](LTR)(Names:'|':MKMapView:0x7fb6b8d237c0)>","<NSLayoutConstraint:0x7fb6b9974f50'UIView-rightMargin-guide-constraint'H:[UILayoutGuide:0x7fb6b9972640'UIViewLayoutMarginsGuide']-(0)-|(LTR)(Names:'|':MKMapView:0x7fb6b8d237c0)>")

Willattempttorecoverbybreakingconstraint<NSLayoutConstraint:0x7fb6b9975460UISegmentedControl:0x7fb6b9897390.trailing==UILayoutGuide:0x7fb6b9972640'UIViewLayoutMarginsGuide'.trailing>

MakeasymbolicbreakpointatUIViewAlertForUnsatisfiableConstraintstocatchthisinthedebugger.ThemethodsintheUIConstraintBasedLayoutDebuggingcategoryonUIViewlistedin<UIKit/UIView.h>mayalsobehelpful.

Let’sgooverthisoutput.AutoLayoutisreportingthatitis“Unabletosimultaneouslysatisfyconstraints.”Thishappenswhenaviewhierarchyhasconstraintsthatconflict.

Then,theconsolespitsoutsomehandytipsandalistofallconstraintsthatareinvolved.Eachconstraint’sdescriptionisshownintheconsole.Let’slookattheformatofoneoftheseconstraintsmoreclosely.<NSLayoutConstraint:0x7fb6b9975350UISegmentedControl:0x7fb6b9897390.leading

WOW! eBook www.wowebook.org

Page 202: iOS Programming The Big Nerd Ranch Guide, 5th

==UILayoutGuide:0x7fb6b9972640'UIViewLayoutMarginsGuide'.leading>

Thisdescriptionindicatesthattheconstraintlocatedatmemoryaddress0x7fb6b9975350issettingtheleadingedgeoftheUISegmentedControl(at0x7fb6b9897390)equaltotheleadingedgeofthemarginoftheUILayoutGuide(at0x7fb6b9972640).

FiveoftheseconstraintsareinstancesofNSLayoutConstraint.One,however,isaninstanceofNSAutoresizingMaskLayoutConstraint.Thisconstraintistheproductofthetranslationoftheimageview’sautoresizingmask.

Finally,ittellsyouhowitisgoingtosolvetheproblembylistingtheconflictingconstraintthatitwillignore.Unfortunately,itchoosespoorlyandignoresoneofyourexplicitinstancesofNSLayoutConstraintinsteadoftheNSAutoresizingMaskLayoutConstraint.Thisiswhyyourinterfacelookslikeitdoes.

Thenotebeforetheconstraintsarelistedisveryhelpful:theNSAutoresizingMaskLayoutConstraintneedstoberemoved.Betteryet,youcanpreventthisconstraintfrombeingaddedinthefirstplacebyexplicitlydisablingtranslationinloadView()://segmentedControl.translatesAutoresizingMaskIntoConstraints=falseview.addSubview(segmentedControl)

WOW! eBook www.wowebook.org

Page 203: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 204: iOS Programming The Big Nerd Ranch Guide, 5th

7Localization

TheappealofiOSisglobal–iOSusersliveinmanydifferentcountriesandspeakmanydifferentlanguages.Youcanensurethatyourapplicationisreadyforaglobalaudiencethroughtheprocessesofinternationalizationandlocalization.

Internationalizationismakingsureyournativeculturalinformation(likelanguage,currency,dateformat,numberformat,etc.)isnothardcodedintoyourapplication.Localizationistheprocessofprovidingtheappropriatedatainyourapplicationbasedontheuser’sLanguageandRegionFormatsettings.YoucanfindthesesettingsintheiOSSettingsapplication(Figure7.1).SelecttheGeneralrowandthentheLanguage&Regionrow.

Figure7.1Languageandregionsettings

Applemakestheseprocessesrelativelysimple.AnapplicationthattakesadvantageofthelocalizationAPIsdoesnotevenneedtoberecompiledtobedistributedinotherlanguagesorregions.(Bytheway,since“internationalization”and“localization”arelongwords,youwillsometimesseethemabbreviatedasi18nandL10n,respectively.)

WOW! eBook www.wowebook.org

Page 205: iOS Programming The Big Nerd Ranch Guide, 5th

Inthischapter,youwillfirstinternationalizetheWorldTrotterapplicationandthenlocalizeitintoSpanish.

WOW! eBook www.wowebook.org

Page 206: iOS Programming The Big Nerd Ranch Guide, 5th

InternationalizationInthisfirstsection,youwillusetheNSNumberFormatterandNSNumberclassestointernationalizetheConversionViewController.

Formatters

InChapter4,youusedaninstanceofNSNumberFormattertosetthetextoftheCelsiuslabelinConversionViewController.NSNumberFormatterhasalocaleproperty,whichissettothedevice’scurrentlocale.WheneveryouuseanNSNumberFormattertocreateanumber,itchecksitslocalepropertyandsetstheformataccordingly.SothetextoftheCelsiuslabelhasbeeninternationalizedfromthestart.

NSLocaleknowshowdifferentregionsdisplaysymbols,dates,anddecimalsandwhethertheyusethemetricsystem.AninstanceofNSLocalerepresentsoneregion’ssettingsforthesevariables.IntheSettingsapplication,theusercanchoosearegion,likeUnitedStatesorUnitedKingdom.(WhydoesAppleuse“region”insteadof“country”?Somecountrieshavemorethanoneregionwithdifferentsettings.ScrollthroughtheoptionsinApplicationRegiontoseeforyourself.)

WhenyoucallthemethodcurrentLocale()onNSLocale,theinstanceofNSLocalethatrepresentstheuser’sregionsettingisreturned.OnceyouhavethatinstanceofNSLocale,youcanaskitquestionslike,“Whatisthecurrencysymbolforthisregion?”or,“Doesthisregionusethemetricsystem?”

Toaskoneofthesequestions,youcallthemethodobjectForKey(_:)ontheNSLocaleinstancewithoneoftheNSLocaleconstantsasanargument.letcurrentLocale=NSLocale.currentLocale()letisMetric=currentLocale.objectForKey(NSLocaleUsesMetricSystem)?.boolValueletcurrencySymbol=currentLocale.objectForKey(NSLocaleCurrencyCode)

EventhoughtheCelsiuslabelisalreadyinternationalized,thereisstillaproblemwithit.ChangethesystemregiontoSpaintosee.Selecttheactiveschemepop-upandselectEditScheme(Figure7.2).

Figure7.2Editscheme

MakesurethatRunisselectedonthelefthandsideandthenselecttheOptionstabatthetop.

WOW! eBook www.wowebook.org

Page 207: iOS Programming The Big Nerd Ranch Guide, 5th

FortheApplicationRegionpop-up,selectEuropeandthenSpain(Figure7.3).Finally,Closetheactiveschemewindow.

Figure7.3Selectingadifferentregion

Buildandruntheapplication.OntheConversionViewController,tapthetextfieldandmakesurethesoftwarekeyboardisvisible.Youmayalreadynoticeonedifference:inSpain,thedecimalseparatorisacommainsteadofaperiod(andthethousandsseparatorisaperiodinsteadofacomma).Thenumber123,456.789intheUnitedStateswouldbewritten123.456,789inSpain.

Attempttotypeinmultipledecimalseparators(thecomma)andnoticethattheapplicationhappilyallowsit.Whoops!Yourcodefordisallowingmultipledecimalseparatorschecksforaperiodinsteadofusingalocale-specificdecimalseparator.Let’sfixthat.

OpenConversionViewController.swiftandupdatetextfield(_:shouldChangeCharactersInRange:replacementString:)

tousethelocale-specificdecimalseparator.functextField(textField:UITextField,shouldChangeCharactersInRangerange:NSRange,replacementStringstring:String)->Bool{

letexistingTextHasDecimalSeparator=textField.text?.rangeOfString(".")letreplacementTextHasDecimalSeparator=string.rangeOfString(".")

letcurrentLocale=NSLocale.currentLocale()letdecimalSeparator=currentLocale.objectForKey(NSLocaleDecimalSeparator)as!String

letexistingTextHasDecimalSeparator=textField.text?.rangeOfString(decimalSeparator)letreplacementTextHasDecimalSeparator=string.rangeOfString(decimalSeparator)

WOW! eBook www.wowebook.org

Page 208: iOS Programming The Big Nerd Ranch Guide, 5th

ifexistingTextHasDecimalSeparator!=nil&&replacementTextHasDecimalSeparator!=nil{returnfalse}else{returntrue}}

Buildandruntheapplication.Theapplicationnolongerallowsyoutotypeinmultipledecimalseparators,anditdoesthisinawaythatisindependentoftheuser’sregionchoice.

Butthereisstillaproblem.Ifyoutypeinanumberwithadecimalseparatorthatisnotaperiod,theconversiontoCelsiusisnothappening–theCelsiuslabeldisplays“???”.Whatisgoingonhere?InfahrenheitFieldEditingChanged(_:),youareusinganinitializerfortheDoubletypethattakesinastringasitsargument.Thisinitializerdoesnotknowhowtohandleastringthatusessomethingotherthanaperiodforitsdecimalseparator.

Let’sfixthiscodeusingtheNSNumberFormatterclass.InConversionViewController.swift,updatefahrenheitFieldEditingChanged(_:)toconvertthetextfield’sstringintoanumberinalocale-independentway.@IBActionfuncfahrenheitFieldEditingChanged(textField:UITextField){

iflettext=textField.text,value=Double(text){fahrenheitValue=value}else{fahrenheitValue=nil}

iflettext=textField.text,number=numberFormatter.numberFromString(text){fahrenheitValue=number.doubleValue}else{fahrenheitValue=nil}}

Hereyouareusingthenumberformatter’sinstancemethodnumberFromString(_:)toconvertthestringintoanumber.Sincethenumberformatterisawareofthelocale,itisabletoconvertthestringintoanumber.Ifthestringcontainsavalidnumber,themethodreturnsaninstanceofNSNumber.NSNumberisaclassthatcanrepresentavarietyofnumbertypes,includingInt,Float,Double,andmore.YoucanaskaninstanceofNSNumberforitsvaluerepresentedasoneofthosevalues.YouaredoingthatheretogetthedoubleValueofthenumber.

Buildandruntheapplication.Nowthatyouareconvertingthestringinalocale-independentway,thetextfield’svalueisproperlyconvertedtoitsCelsiusvalue.

Baseinternationalization

Wheninternationalizing,youasktheinstanceofNSLocalequestions.ButtheNSLocaleonlyhasafewregion-specificvariables.Thisiswherelocalization–creatingapplication-specificsubstitutionsfordifferentregionandlanguagesettings–comesinto

WOW! eBook www.wowebook.org

Page 209: iOS Programming The Big Nerd Ranch Guide, 5th

play.Localizationusuallyinvolveseithergeneratingmultiplecopiesofresources(likeimages,sounds,andinterfacefiles)fordifferentregionsandlanguagesorcreatingandaccessingstringstables(whichyouwillseelaterinthechapter)totranslatetextintodifferentlanguages.

Beforeyougothroughtheprocessoflocalizingresources,youmustunderstandhowaniOSapplicationhandleslocalizedresources.

WhenyoubuildatargetinXcode,anapplicationbundleiscreated.AlloftheresourcesthatyouaddedtothetargetinXcodearecopiedintothisbundle,alongwiththeexecutableitself.ThisbundleisrepresentedatruntimebyaninstanceofNSBundleknownasthemainbundle.ManyclassesworkwiththeNSBundletoloadresources.

Localizingaresourceputsanothercopyoftheresourceintheapplicationbundle.Theseresourcesareorganizedintolanguage-specificdirectories,knownaslprojdirectories.Eachoneofthesedirectoriesisthenameofthelocalizationsuffixedwithlproj.Forexample,theAmericanEnglishlocalizationisen_US,whereenistheEnglishlanguagecodeandUSistheUnitedStatesofAmericaregioncode,sothedirectoryforAmericanEnglishresourcesisen_US.lproj.(Theregioncanbeomittedifyoudonotneedtomakeregionaldistinctionsinyourresourcefiles.)Theselanguageandregioncodesarestandardonallplatforms,notjustiOS.

Whenabundleisaskedforthepathofaresourcefile,itfirstlooksattherootlevelofthebundleforafileofthatname.Ifitdoesnotfindone,itlooksatthelocaleandlanguagesettingsofthedevice,findstheappropriatelprojdirectory,andlooksforthefilethere.Thus,justbylocalizingresourcefiles,yourapplicationwillautomaticallyloadthecorrectfile.

Oneoptionforlocalizingresourcefilesistocreateseparatestoryboardfilesandmanuallyediteachstringineachfile.However,thisapproachdoesnotscalewellifyouareplanningmultiplelocalizations.Whathappenswhenyouaddanewlabelorbuttontoyourlocalizedstoryboard?Youhavetoaddthisviewtothestoryboardforeverylanguage.Notfun.

Tosimplifytheprocessoflocalizinginterfacefiles,Xcodehasafeaturecalledbaseinternationalization.BaseinternationalizationcreatestheBase.lprojdirectory,whichcontainsthemaininterfacefiles.LocalizingindividualinterfacefilescanthenbedonebycreatingjusttheLocalizable.stringsfiles.Itisstillpossibletocreatethefullinterfacefiles,incaselocalizationcannotbedonebychangingstringsalone.However,withthehelpofAutoLayout,stringsreplacementissufficientformostlocalizationneeds.Inthenextsection,youwilluseAutoLayouttoprepareyourlayoutforlocalization.

Preparingforlocalization

OpenMain.storyboard,andthenshowtheassistanteditoreitherbyclickingView→AssistantEditor→ShowAssistantEditororwiththekeyboardshortcutOption-Command-Return.Fromthejumpbardropdown,selectPreview(Figure7.4).Thepreviewassistantallowsyoutoeasilyseehowyourinterfacewilllookacrossscreensizesandorientationsaswellasbetweendifferentlocalizedlanguages.

WOW! eBook www.wowebook.org

Page 210: iOS Programming The Big Nerd Ranch Guide, 5th

Figure7.4Openingthepreviewassistant

Inthestoryboard,selecttheConversionViewControllertoseeitspreview(Figure7.5).

WOW! eBook www.wowebook.org

Page 211: iOS Programming The Big Nerd Ranch Guide, 5th

Figure7.5Previewassistant

Noticethecontrolsinthelowercornersofthepreviewassistant.The+buttonontheleftsideallowsyoutoaddadditionalscreensizestothepreviewcanvas.Thisallowsyoutoeasilyseehowchangestoyourinterfacepropagateacrossscreensizesandorientationssimultaneously.Thebuttonontherightsideallowsyoutoselectalanguagetopreviewthisinterfacein.

(Ifyourpreviewisforaconfigurationotherthan“iPhone4.7-inch,”usethe+buttontoaddthisconfiguration.ThenclickonwhateverpreviewopenedbydefaultandpresstheDeletekeytoremoveit.)

Youhavenotlocalizedtheapplicationintoanotherlanguageyet,butXcodesuppliesapseudolanguageforyoutouse.Pseudolanguageshelpyouinternationalizeyourapplicationsbeforereceivingtranslationsforallofyourstringsandassets.Thebuilt-in

WOW! eBook www.wowebook.org

Page 212: iOS Programming The Big Nerd Ranch Guide, 5th

pseudolanguage,Double-LengthPseudolanguage,mimicslanguagesthataremoreverbosebyrepeatingwhatevertextstringisinthetextelement.So,forexample,“isreally”becomes“isreallyisreally.”

SelecttheLanguagepop-upthatsaysEnglishandchooseDouble-LengthPseudolanguage.Thelabelsallhavetheirtextdoubled(Figure7.6).

Figure7.6Doubledtextstrings

Thedouble-lengthpseudolanguagerevealsaproblemimmediately:thelabelsgooffboththeleftandrightedgesofthescreenandyouareunabletoreadtheentirestrings.Thefixistoconstrainallofthelabelssothattheirleadingandtrailingedgesstaywithinthemarginsoftheirsuperview.Thenyouwillneedtochangethelinecountforthelabelsto0,whichtellsthelabelsthattheirtextshouldwraptomultiplelinesifneeded.Youaregoingtostartbyfixingonelabel,thenrepeatthestepsfortherestofthelabels.

WOW! eBook www.wowebook.org

Page 213: iOS Programming The Big Nerd Ranch Guide, 5th

Inthecanvas,selectthe“degreesFahrenheit”label.Youaregoingtoaddconstraintstothislabelinanewway.Control-dragfromthelabeltotheleftsideofthesuperview.Whenyoudo,acontext-sensitivepop-upwillappeargivingyoutheconstraintsthatmakesenseforthisdirection(Figure7.7).SelectLeadingSpacetoContainerMarginfromthelist.

Figure7.7CreatingconstraintsbyControl-dragging

Thedirectionthatyoudraginfluenceswhichpossibleconstraintsaredisplayed.Ahorizontaldragwillshowhorizontalconstraints,andaverticaldragwillshowverticalconstraints.Adiagonaldragwillshowbothhorizontalandverticalconstraints,whichisusefulforsettingupmanyconstraintssimultaneously.

NowControl-dragfromthe“degreesFahrenheit”labeltotherightsideofthesuperviewandselectTrailingSpacetoContainerMargin.

Ontheirown,theseconstraintsarenotverygood.Theymaintaintheexistingfixeddistancebetweentheleadingandtrailingedgesofthelabel,asyoucanseeinthepreviewassistant(Figure7.8).

WOW! eBook www.wowebook.org

Page 214: iOS Programming The Big Nerd Ranch Guide, 5th

Figure7.8Previewassistantwithnewconstraints

Whatyoureallywantisforthedistancebetweenthelabelandthemarginstobegreaterthanorequalto0.Youcandothiswithinequalityconstraints.

SelecttheleadingconstraintbyclickingontheI-bartotheleftofthelabel.OpenitsattributesinspectorandchangetheRelationtoGreaterThanorEqualandtheConstantto0(Figure7.9).

WOW! eBook www.wowebook.org

Page 215: iOS Programming The Big Nerd Ranch Guide, 5th

Figure7.9Inequalityconstraint

Dothesameforthetrailingconstraint.Takealookatthepreviewassistant;theinterfaceislookingbetter,butthelabelisnowbeingtruncated.

Selectthelabelandopenitsattributesinspector.ChangetheLinescountto0.Nowtakealookatthepreviewassistant;thelabelisnolongerbeingtruncatedandinsteadthetextflowstoasecondline.Sincetheotherlabelsareallrelatedtothelabelabovethem,theyhaveautomaticallybeenmoveddown.

Repeatthestepsabovefortheotherlabels.Youwillneedto:

Addaleadingandtrailingconstrainttoeachlabel.

Settheconstraints’relationtoGreaterThanorEqualandtheconstantto0.(Ashortcutforeditingaconstraintistodouble-clickonit.)

Changethelabel’slinecountto0.

Onceyouaredone,thepreviewassistantwiththedouble-lengthpseudolanguagewilllooklikeFigure7.10.

WOW! eBook www.wowebook.org

Page 216: iOS Programming The Big Nerd Ranch Guide, 5th

Figure7.10Previewassistantwithfinalconstraints

WOW! eBook www.wowebook.org

Page 217: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 218: iOS Programming The Big Nerd Ranch Guide, 5th

LocalizationWorldTrotterisnowinternationalized–itsinterfaceisabletoadapttovariouslanguagesandregions.Nowitistimetolocalizetheapp–thatis,toupdatethestringsandresourcesintheapplicationforanewlanguage.Inthissection,youaregoingtolocalizetheinterfaceofWorldTrotter:theMain.storyboardfile.YouwillcreateEnglishandSpanishlocalizations,whichwillcreatetwolprojdirectoriesinadditiontothebaseone.

Startbylocalizingastoryboardfile.SelectMain.storyboardintheprojectnavigator.

Openthefileinspectorbyclickingthe tabintheinspectorselectororbyusingthekeyboardshortcutOption-Command-1.FindthesectioninthisinspectornamedLocalization.ChecktheEnglishboxandmakesurethatthedropdownsaysLocalizableStrings(Figure7.11).Thiswillcreateastringstablethatyouwilluselatertolocalizetheapplication.

Figure7.11LocalizingintoEnglish

Next,intheprojectnavigator,selecttheWorldTrotterprojectatthetop.ThenselectWorldTrotterundertheProjectsectioninthesidelist,andmakesuretheInfotabisopen.(Ifyoucannotseethesidelist,youcanopenitusingtheShowprojectsandtargetslistbuttonintheupperlefthandcorner(Figure7.12).)

Figure7.12Showingtheprojectsettings

Clickthe+buttonunderLocalizationsandselectSpanish(es).Inthedialog,youcanunchecktheLaunchScreen.storyboardfile;keeptheMain.storyboardfilechecked.

WOW! eBook www.wowebook.org

Page 219: iOS Programming The Big Nerd Ranch Guide, 5th

MakesurethatthereferencelanguageisBaseandthefiletypeisLocalizableStrings.ClickFinish.Thiscreatesanes.lprojfolderandgeneratestheMain.stringsfileinitthatcontainsallthestringsfromthebaseinterfacefile.TheLocalizationsconfigurationshouldlooklikeFigure7.13.

Figure7.13Localizations

Lookintheprojectnavigator.ClickthedisclosurebuttonnexttoMain.storyboard(Figure7.14).XcodemovedtheMain.storyboardfiletotheBase.lprojdirectoryandcreatedtheMain.stringsfileinthees.lprojdirectory.

Figure7.14Localizedstoryboardintheprojectnavigator

Intheprojectnavigator,clicktheSpanishversionofMain.strings.Whenthisfileopens,thetextisnotinSpanish.Youhavetotranslatelocalizedfilesyourself;Xcodeisnotthatsmart.

WOW! eBook www.wowebook.org

Page 220: iOS Programming The Big Nerd Ranch Guide, 5th

Editthisfileaccordingtothefollowingtext.Thenumbersandordermaybedifferentinyourfile,butyoucanusethetextandtitlefieldsinthecommentstomatchupthetranslations./*Class="UITabBarItem";title="Map";ObjectID="6xh-o5-yRt";*/"6xh-o5-yRt.title"="Map""Mapa";

/*Class="UILabel";text="degreescelsius";ObjectID="7la-u7-mx6";*/"7la-u7-mx6.text"="degreesCelsius""gradosCelsius";

/*Class="UILabel";text="degreesfahrenheit";ObjectID="Dic-rs-P0S";*/"Dic-rs-P0S.text"="degreesFahrenheit""gradosFahrenheit";

/*Class="UILabel";text="100";ObjectID="Eso-Wf-EyH";*/"Eso-Wf-EyH.text"="100";

/*Class="UITextField";placeholder="value";ObjectID="On4-jV-YlY";*/"On4-jV-YlY.placeholder"="value""valor";

/*Class="UILabel";text="isreally";ObjectID="wtF-xR-gbZ";*/"wtF-xR-gbZ.text"="isreally""esrealmente";

/*Class="UITabBarItem";title="Convert";ObjectID="zLY-50-CeX";*/"zLY-50-CeX.title"="Convert""Convertir";

Nowthatyouhavefinishedlocalizingthisstoryboardfile,let’stestitout.First,thereisalittleXcodeglitchtobeawareof:sometimesXcodeignoresaresourcefile’schangeswhenyoubuildanapplication.Toensurethatyourapplicationisbeingbuiltfromscratch,firstdeleteitfromyourdeviceorsimulator.(Pressandholditsiconinthelauncher.Whenitstartstowiggle,tapthedeletebadge.)RelaunchXcode.(Yes,exitandstartitagain.)Then,chooseCleanfromtheProductmenu.Finally,tobeabsolutelysure,pressandholdtheOptionkeywhileopeningtheProductmenuandchooseCleanBuildFolder….Thiswillforcetheapplicationtobeentirelyrecompiled,rebundled,andreinstalled.

Opentheactiveschemepop-upandselectEditScheme.MakesureRunisselectedonthelefthandsideandopentheOptionstab.OpentheApplicationLanguagepop-upandselectSpanish.Finally,confirmthatSpainisstillselectedfromtheApplicationRegionpop-up.Closethewindow.

Buildandruntheapplication.MakesureyouareviewingtheConversionViewController,andyouwillseetheinterfaceinSpanish.Becauseyousettheconstraintsonthelabelstoaccommodatedifferentlengthsoftext,theyresizethemselvesappropriately(Figure7.15).

WOW! eBook www.wowebook.org

Page 221: iOS Programming The Big Nerd Ranch Guide, 5th

Figure7.15SpanishConversionViewController

NSLocalizedStringandstringstables

Inmanyplacesinyourapplications,youcreateStringinstancesdynamicallyordisplaystringliteralstotheuser.Todisplaytranslatedversionsofthesestrings,youmustcreateastringstable.Astringstableisafilecontainingalistofkey-valuepairsforallofthestringsthatyourapplicationusesandtheirassociatedtranslations.Itisaresourcefilethatyouaddtoyourapplication,butyoudonotneedtodoalotofworktogetdatafromit.

Youmightuseastringinyourcodelikethis:letgreeting="Hello!"

Tointernationalizethestringinyourcode,youreplaceliteralstringswiththefunctionNSLocalizedString(_:comment:).letgreeting=NSLocalizedString("Hello!",comment:"Thegreetingfortheuser")

Thisfunctiontakestwoarguments:akeyandacommentthatdescribesthestring’suse.Thekeyisthelookupvalueinastringstable.Atruntime,NSLocalizedString(_:comment:)willlookthroughthestringstablesbundledwithyourapplicationforatablethatmatchestheuser’slanguagesettings.Then,inthattable,thefunctiongetsthetranslatedstringthatmatchesthekey.

NowyouaregoingtointernationalizethestringsthattheMapViewControllerdisplaysinitssegmentedcontrol.InMapViewController.swift,locatetheloadView()methodandupdatetheinitializerforthesegmentedcontroltouse

WOW! eBook www.wowebook.org

Page 222: iOS Programming The Big Nerd Ranch Guide, 5th

localizedstrings.overridefuncloadView(){

mapView=MKMapView()view=mapView

letsegmentedControl=UISegmentedControl(items:["Standard","Satellite","Hybrid"])

letstandardString=NSLocalizedString("Standard",comment:"Standardmapview")letsatelliteString=NSLocalizedString("Satellite",comment:"Satellitemapview")lethybridString=NSLocalizedString("Hybrid",comment:"Hybridmapview")letsegmentedControl=UISegmentedControl(items:[standardString,satelliteString,hybridString])

OnceyouhavefilesthathavebeeninternationalizedwiththeNSLocalizedString(_:comment:)function,youcangeneratestringstableswithacommand-lineapplication.

OpentheTerminalapp.ThisisaUnixterminal;itisusedtoruncommand-linetools.YouwanttonavigatetothelocationofMapViewController.swift.IfyouhaveneverusedtheTerminalappbefore,hereisahandytrick.InTerminal,typethefollowing:cd

followedbyaspace.(DonotpressEnteryet.)

Next,openFinderandlocateMapViewController.swiftandthefolderthatcontainsit.DragtheiconofthatfolderontotheTerminalwindow.Terminalwillfilloutthepathforyou.Itwilllooksomethinglikethis:cd/Users/cbkeur/iOSDevelopment/WorldTrotter/WorldTrotter/

PressReturn.ThecurrentworkingdirectoryofTerminalisnowthisdirectory.

UsetheterminalcommandlstoprintoutthecontentsoftheworkingdirectoryandconfirmthatMapViewController.swiftisinthatlist.

Togeneratethestringstable,enterthefollowingintoTerminalandpressEnter:genstringsMapViewController.swift

Theresultingfile,Localizable.strings,containsthestringsfromMapViewController.DragthisnewfilefromFinderintotheprojectnavigator(orusetheFile→AddFilesto“WorldTrotter”…menuitem).Whentheapplicationiscompiled,thisresourcewillbecopiedintothemainbundle.

OpenLocalizable.strings.Thefileshouldlooksomethinglikethis:/*Hybridmapview*/"Hybrid"="Hybrid";

/*Satellitemapview*/"Satellite"="Satellite";

/*Standardmapview*/"Standard"="Standard";

NoticethatthecommentaboveyourstringisthesecondargumentyousuppliedtotheNSLocalizedStringfunction.Eventhoughthefunctiondoesnotrequirethecommentargument,includingitwillmakeyourlocalizinglifeeasier.

WOW! eBook www.wowebook.org

Page 223: iOS Programming The Big Nerd Ranch Guide, 5th

NowthatyouhavecreatedLocalizable.strings,youneedtolocalizeitinXcode.OpenitsfileinspectorandclicktheLocalize…buttonintheutilityarea.MakesureBaseisselectedfromthepop-upandclickLocalize.AddtheSpanishandEnglishlocalizationbycheckingtheboxnexttoeachlanguage.

Intheprojectnavigator,clickonthedisclosuretrianglethatnowappearsnexttoLocalizable.strings.OpentheSpanishversion.ThestringonthelefthandsideisthekeythatispassedtotheNSLocalizedString(_:comment:)function,andthestringontherighthandsideiswhatisreturned.ChangethetextontherighthandsidetotheSpanishtranslationshownbelow.(Totypeanaccentedcharacter,suchas“é,”pressandholdtheappropriatecharacteronyourkeyboardandthenpresstheappropriatenumberfromthepop-up.)/*Hybridmapview*/"Hybrid"="Hybrid""Híbrido";

/*Satellitemapview*/"Satellite"="Satellite""Satélite";

/*Standardmapview*/"Standard"="Standard""Estándar";

Buildandruntheapplicationagain.Nowallthesestrings,includingthetitlesinthesegmentedcontrol,willappearinSpanish(Figure7.16).Iftheydonot,youmightneedtodeletetheapplication,cleanyourproject,andrebuild.(Orcheckyourschemelanguagesetting.)

Figure7.16SpanishMapViewController

Internationalizationandlocalizationareveryimportantforyourapptoreachthelargestaudience.Additionally,asyousawearlyinthischapter,yourappmightnotworkforsomeusersifyouhavenotproperlyinternationalizedit.Youwillinternationalize(butnotlocalize)yourprojectsintherestofthisbook.

Overthepastfivechapters,youhavebuiltaratherimpressiveapplicationthatallowsthe

WOW! eBook www.wowebook.org

Page 224: iOS Programming The Big Nerd Ranch Guide, 5th

usertoconvertbetweenCelsiusandFahrenheitaswellasdisplayamapinafewdifferentways.NotonlydoesthisapplicationscalewellonalliPhonescreensizes,butitisalsolocalizedintoanotherlanguage.Congratulations!

WOW! eBook www.wowebook.org

Page 225: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 226: iOS Programming The Big Nerd Ranch Guide, 5th

BronzeChallenge:AnotherLocalizationPracticemakesperfect.LocalizeWorldTrotterforanotherlanguage.Useatranslationwebsiteifyouneedhelpwiththelanguage.

WOW! eBook www.wowebook.org

Page 227: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 228: iOS Programming The Big Nerd Ranch Guide, 5th

FortheMoreCurious:NSBundle’sRoleinInternationalizationTherealworkoftakingadvantageoflocalizationsisdoneforyoubytheclassNSBundle.Abundlerepresentsalocationonthefilesystemthatgroupsthecompiledcodeandresourcestogether.The“mainbundle”isanothernamefortheapplicationbundle,whichcontainsalloftheresourcesandtheexecutablefortheapplication.YouwilllearnmoreabouttheapplicationbundleinChapter15.

Whenanapplicationisbuilt,allofthelprojdirectoriesarecopiedintothemainbundle.Figure7.17showsthemainbundleforWorldTrotterwithsomeadditionalimagesaddedtotheproject.

Figure7.17Applicationbundle

NSBundleknowshowtosearchthroughlocalizationdirectoriesforeverytypeofresourceusingtheinstancemethodURLForResource(_:withExtension:).Whenyouwantapathtoaresourcebundledwithyourapplication,youcallthismethodonthemainbundle.HereisanexampleusingtheresourcefileBoo.png:letpath=NSBundle.mainBundle().URLForResource:("Boo"withExtension:"png")

Whenattemptingtolocatetheresource,thebundlefirstcheckstoseeiftheresourceexistsatthetopleveloftheapplicationbundle.Ifso,itreturnsthefullURLtothatfile.Ifnot,

WOW! eBook www.wowebook.org

Page 229: iOS Programming The Big Nerd Ranch Guide, 5th

thebundlegetsthedevice’slanguageandregionsettingsandlooksintheappropriatelprojdirectoriestoconstructtheURL.Ifitstilldoesnotfindit,itlookswithintheBase.lprojdirectory.Finally,ifnofileisfound,itreturnsnil.

IntheapplicationbundleshowninFigure7.17,iftheuser’slanguageissettoSpanish,NSBundlewillfindBoo.pngatthetoplevel,Tom.pngines.lproj,andHat.pnginBase.lproj.

Whenyouaddanewlocalizationtoyourproject,Xcodedoesnotautomaticallyremovetheresourcesfromthetop-leveldirectory.Thisiswhyyoumustdeleteandcleananapplicationwhenyoulocalizeafile–otherwise,thepreviousunlocalizedfilewillstillbeintherootleveloftheapplicationbundle.Eventhoughtherearelprojfoldersintheapplicationbundle,thebundlefindsthetop-levelfilefirstandreturnsitsURL.

WOW! eBook www.wowebook.org

Page 230: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 231: iOS Programming The Big Nerd Ranch Guide, 5th

FortheMoreCurious:ImportingandExportingasXLIFFTheindustry-standardformatforlocalizationdataistheXLIFFdatatype,whichstandsforXMLLocalisationInterchangeFileFormat.Whenworkingwithatranslator,youwilloftensendthemanXLIFFfilecontainingthedataintheapplicationtolocalize,andtheywillgiveyoubackalocalizedXLIFFfileforyoutoimport.

XcodenativelysupportsimportingandexportinglocalizationdataintheXLIFFformat.Theexportingprocesswilltakecareoffindingandexportingthelocalizedstringswithintheproject,whichyoupreviouslydidmanuallyusingthegenstringstool.

ToexportthelocalizablestringsinXLIFFformat,selecttheproject(WorldTrotter)intheprojectnavigator.ThenselecttheEditormenu,andthenExportForLocalization….Onthenextscreen,youcanchoosewhethertoexportexistingtranslations(whichisprobablyagoodideasothetranslatordoesnotdoredundantwork)andwhichlanguagesyouwouldlikeexported(Figure7.18).

Figure7.18ExportinglocalizationdataasXLIFF

Toimportlocalizations,selecttheproject(WorldTrotter)intheprojectnavigator.ThenselectEditor→ImportLocalizations….Afterchoosingafile,youwillbeabletoconfirmtheupdatesbeforeyouimport.

WOW! eBook www.wowebook.org

Page 232: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 233: iOS Programming The Big Nerd Ranch Guide, 5th

8ControllingAnimations

Theword“animation”isderivedfromaLatinwordthatmeans“theactofbringingtolife.”Inyourapplications,animationscansmoothlybringinterfaceelementsonscreenorintofocus,theycandrawtheuser’sattentiontoanactionableitem,andtheycangiveclearindicationsofhowyourappisrespondingtotheuser’sactions.Inthischapter,youwillreturntoyourQuizappanduseavarietyofanimationtechniquestobringittolife.

BeforeupdatingQuiz,though,let’stakealookatwhatcanbeanimatedbylookingatthedocumentation.Toopenthedocumentation,openXcode’sHelpmenuandselectDocumentationandAPIReference.Thiswillopenthedocumentationinanewwindow.

Withthedocumentationopen,usethesearchbaratthetoptosearchfortheUIViewClassReference.Double-clickinthesearchresultstoopenitandthenscrolldowntothesectiontitledAnimations.Thedocumentationgivessomeanimationrecommendations(whichwewillfollowinthisbook)andliststhepropertiesonUIViewthatcanbeanimated(Figure8.1).

Figure8.1UIViewanimationdocumentation

WOW! eBook www.wowebook.org

Page 234: iOS Programming The Big Nerd Ranch Guide, 5th

BasicAnimationsThedocumentationisalwaysagoodstartingpointforlearningaboutanyiOStechnology.Withthatlittlebitofinformationunderyourbelt,let’sgoaheadandaddsomeanimationstoQuiz.Thefirsttypeofanimationyouaregoingtouseisthebasicanimation.Abasicanimationanimatesbetweenastartvalueandanendvalue(Figure8.2).

Figure8.2Basicanimation

Thefirstanimationyouaregoingtoaddwillanimatethealphavalue(thedegreeoftransparency)ofthequestionlabelassociatedwithViewController.

OpenQuiz.xcodeprojandViewController.swift.Whentheuseradvancestothenextquestion,youwilluseananimationtofadeinthelabel.ThereareclassmethodsonUIViewthatwillallowyoutoaccomplishthis.ThesimplestUIViewanimationmethodis:classfuncanimateWithDuration(duration:NSTimeInterval,animations:()->Void)

Thisclassmethodtakestwoarguments:adurationoftypeNSTimeInterval(whichisanaliasforaDouble)andananimationsvariablethatisaclosure.

Closures

Aclosureisadiscretebundleoffunctionalitythatcanbepassedaroundyourcode.Closuresarealotlikefunctionsandmethods.Infact,functionsandmethodsarejustspecialcasesofclosures.

Closureshavealightweightsyntaxthatallowsthemtobeeasilypassedinasargumentstofunctionsandmethods.Aclosurecanevenbethereturntypeofafunctionormethod.Inthissection,youwilluseaclosuretospecifytheanimationsthatyouwanttooccur.

Thesignatureofaclosureisacomma-separatedlistofargumentswithinparenthesesfollowedbyareturnarrowandthereturntype:(arguments)->returntype

Noticethatthissyntaxissimilartothesyntaxforfunctions:funcfunctionName(arguments)->returntype

Nowtakealookagainattheclosuresignaturethattheanimationsargumentexpects:classfuncanimateWithDuration(duration:NSTimeInterval,animations:()->Void)

Thisclosuretakesinnoargumentsanddoesnotreturnanything.(Youwillalsoseethis

WOW! eBook www.wowebook.org

Page 235: iOS Programming The Big Nerd Ranch Guide, 5th

returntypeexpressedas(),whichmeansthesamethingasVoid.)

Theclosuresignatureisprettystraightforwardandfamiliar,buthowdoyoudeclareaclosureincode?Closuresyntaxtakesthefollowingform:{(arguments)->returntypein//code}

Youwriteaclosureexpressioninsidebraces({}).Theclosure’sargumentsarelistedinsideparenthesesimmediatelyaftertheopeningbrace.Aclosure’sreturntypecomesaftertheparametersandusestheregularsyntax.Thekeywordinisusedtoseparatetheclosure’sargumentsandreturntypefromthestatementsinsideofitsbody.

InViewController.swift,addanewmethodtohandletheanimationsanddeclareaclosureconstantthattakesinnoargumentsanddoesnotreturnanything.funcanimateLabelTransitions(){

letanimationClosure={()->Voidin

}}

Nowyouhaveaconstantthatreferencesachunkoffunctionality.Currently,however,thisclosuredoesnotactuallydoanything.AddfunctionalitytotheclosurethatsetsthealphaofthequestionLabelto1.Then,passthisclosureasanargumenttoanimateWithDuration(_:animations:).funcanimateLabelTransitions(){

letanimationClosure={()->Voidinself.questionLabel.alpha=1}

//AnimatethealphaUIView.animateWithDuration(0.5,animations:animationClosure)}

Currently,thequestionLabelalreadyhasanalphaof1whenitcomesonscreen,soyouwillnotseeanythinganimateifyoubuildandrun.Toaddressthis,overrideviewWillAppear(_:)toresetthequestionLabel’salphato0eachtimetheViewController’sviewcomesonscreen.overridefuncviewWillAppear(animated:Bool){super.viewWillAppear(animated)

//Setthelabel'sinitialalphaquestionLabel.alpha=0}

Thecodeaboveworksgreat,butthereareafewmodificationsyoucanmakesoitislessverbose.Updatethecodetomakeitmoreconcise.funcanimateLabelTransitions(){

letanimationClosure={()->Voidinself.questionLabel.alpha=1}

//AnimatethealphaUIView.animateWithDuration(0.5,animations:animationClosure)

UIView.animateWithDuration(0.5,animations:{self.questionLabel.alpha=1

WOW! eBook www.wowebook.org

Page 236: iOS Programming The Big Nerd Ranch Guide, 5th

})}

Youhavemadetwochanges:First,youarepassingintheclosureanonymously(i.e.,passingitdirectlyintothemethodinsteadofassigningittoavariableorconstant).Second,youhaveremovedthetypeinformationsincetheclosurecaninferthisfromthecontext.

NowcalltheanimateLabelTransitions()methodwhenevertheusertapstheNextQuestionbutton.@IBActionfuncshowNextQuestion(sender:AnyObject){++currentQuestionIndexifcurrentQuestionIndex==questions.count{currentQuestionIndex=0}

letquestion:String=questions[currentQuestionIndex]questionLabel.text=question

answerLabel.text="???"

animateLabelTransitions()}

Buildandruntheapplication.WhenyoutapontheNextQuestionbutton,thelabelwillfadeintoview.Animationsprovidealessjarringuserexperiencethanhavingviewsjustpopintoexistence.

ThemethodanimateWithDuration(_:animations:)returnsimmediately.Thatis,itstartstheanimation,butitdoesnotwaitfortheanimationtocomplete.

WOW! eBook www.wowebook.org

Page 237: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 238: iOS Programming The Big Nerd Ranch Guide, 5th

AnotherLabelTheanimationworksgreatthefirsttimetheNextQuestionbuttonispressed,butthereisnovisibleanimationonsubsequentbuttonpressessincethelabelalreadyhasanalphavalueof1.Inthissection,youaregoingtoaddanotherlabeltotheinterface.WhentheNextQuestionbuttonispressed,theexistinglabelwillfadeoutwhilethenewlabel(withthetextofthenextquestion)willfadein.

AtthetopofViewController.swift,replaceyourdeclarationofasinglelabelwithtwolabels.@IBOutletvarquestionLabel:UILabel!@IBOutletvarcurrentQuestionLabel:UILabel!@IBOutletvarnextQuestionLabel:UILabel!

@IBOutletvaranswerLabel:UILabel!

XcodeflagsfourplaceswhereyouneedtoreplacequestionLabelwithoneofyournewlabels.UpdateviewDidLoad()tousecurrentQuestionLabel.UpdateviewWillAppear(_:)andshowNextQuestion(_:)tousenextQuestionLabel.funcviewDidLoad(){super.viewDidLoad()

letquestion=questions[currentQuestionIndex]questionLabel.text=questioncurrentQuestionLabel.text=question}

overridefuncviewWillAppear(animated:Bool){super.viewWillAppear(animated)

//Setthelabel'sinitialalphaquestionLabel.alpha=0nextQuestionLabel.alpha=0}

@IBActionfuncshowNextQuestion(sender:AnyObject){++currentQuestionIndexifcurrentQuestionIndex==questions.count{currentQuestionIndex=0}

letquestion:String=questions[currentQuestionIndex]questionLabel.text=questionnextQuestionLabel.text=question

answerLabel.text="???"

animateLabelTransitions()}

NowupdateanimateLabelTransitions()toanimatethealphaofthetwolabels.YouwillfadeoutthecurrentQuestionLabelandfadeinthenextQuestionLabelsimultaneously.funcanimateLabelTransitions(){

//AnimatethealphaUIView.animateWithDuration(0.5,animations:{self.questionLabel.alpha=1self.currentQuestionLabel.alpha=0self.nextQuestionLabel.alpha=1})

WOW! eBook www.wowebook.org

Page 239: iOS Programming The Big Nerd Ranch Guide, 5th

}

OpenMain.storyboard.Nowthatthecodehasbeenupdatedforthesetwolabels,youneedtomaketheconnections.Control-clickontheViewControllertoseealistofconnections.NoticethattheexistingquestionLabelisstillpresentwithayellowwarningsignnexttoit(Figure8.3).Clickonthextoremovethisconnection.

Figure8.3Missingconnection

ConnectthecurrentQuestionLabeloutlettotheexistingquestionlabelbydraggingfromthecirclenexttocurrentQuestionLabeltothelabelonthecanvas.

NowdraganewLabelontotheinterfaceandpositionitnexttotheexistingquestionlabel.ConnectthenextQuestionLabeltothisnewlabel.

Youwantthislabeltobeinthesamepositionastheexistingquestionlabel.Asyouhavelikelyguessed,thebestwaytoachievethisisthroughconstraints.Control-dragfromthenextQuestionLabeltothecurrentQuestionLabelandselectTop.ThenControl-dragupwardfromthenextQuestionLabeltoitssuperviewandselectCenterHorizontallyinContainer.

Atthispoint,nextQuestionLabelismisplaced.Selectthelabel,opentheResolveAutoLayoutIssuesmenu,andselectUpdateFrames.Thelabelswillnowbeontopofoneanother.

Buildandruntheapplication.TaptheNextQuestionbuttonandyouwillseeagracefulfadeforbothofthelabels.

Ifyoutapitagain,however,nofadeoccursbecausethenextQuestionLabelalreadyhasanalphaof1.Tofixthis,youwillswapthereferencestothetwolabels.Whentheanimationcompletes,thecurrentQuestionLabelneedstobesettotheonscreenlabel,andthenextQuestionLabelneedstobesettotheoffscreenlabel.Youwilluseacompletionhandlerontheanimationtoaccomplishthis.

WOW! eBook www.wowebook.org

Page 240: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 241: iOS Programming The Big Nerd Ranch Guide, 5th

AnimationCompletionItcanoftenbeusefultoknowwhenananimationcompletes.Forinstance,youmightwanttochainanimationstogetherorupdateanotherobjectwhentheanimationcompletes.Toknowwhentheanimationfinishes,passaclosureforthecompletionargument.Youwillusethisopportunitytoswapthetwolabelreferences.

InViewController.swift,updateanimateLabelTransitions()tousetheUIViewanimationmethodthathasthemostparameters,includingonethattakesinacompletionclosure.funcanimateLabelTransitions(){

//AnimatethealphaUIView.animateWithDuration(0.5,animations:{self.currentQuestionLabel.alpha=0self.nextQuestionLabel.alpha=1})UIView.animateWithDuration(0.5,delay:0,options:[],animations:{self.currentQuestionLabel.alpha=0self.nextQuestionLabel.alpha=1},completion:{_inswap(&self.currentQuestionLabel,&self.nextQuestionLabel)})}

Thedelayindicateshowlongthesystemshouldwaitbeforetriggeringtheanimation.Wewilltalkabouttheoptionslaterinthischapter.Fornow,youarepassinginanemptyarrayofnooptions.

Inthecompletionclosure,youneedtotellthesystemthatwhatusedtobethecurrentQuestionLabelisnowthenextQuestionLabelandthatwhatusedtobethenextQuestionLabelisnowthecurrentQuestionLabel.Toaccomplishthis,youusetheswap(_:_:)function,whichacceptstworeferencesandswapsthem.

Buildandruntheapplication.Nowyouareabletotransitionbetweenallofthequestions.

WOW! eBook www.wowebook.org

Page 242: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 243: iOS Programming The Big Nerd Ranch Guide, 5th

AnimatingConstraintsInthissection,youaregoingtoextendyouranimationtohavethenextQuestionLabelpropertyflyinfromtheleftsideofthescreenandthecurrentQuestionLabelflyouttotherightsideofthescreenwhenevertheuserpressestheNextQuestionbutton.Indoingso,youwilllearnhowtoanimateconstraints.

First,youneedareferencetotheconstraintsthatneedtobemodified.Sofar,allofyour@IBOutletshavebeentoviewobjects.Butoutletsarenotlimitedtoviews–infact,anyobjectinyourinterfacefilecanhaveanoutlet,includingconstraints.

AtthetopofViewController.swift,declaretwooutletsforthetwolabels’centeringconstraints.@IBOutletvarcurrentQuestionLabel:UILabel!@IBOutletvarcurrentQuestionLabelCenterXConstraint:NSLayoutConstraint!

@IBOutletvarnextQuestionLabel:UILabel!@IBOutletvarnextQuestionLabelCenterXConstraint:NSLayoutConstraint!

@IBOutletvaranswerLabel:UILabel!

NowopenMain.storyboard.Youwanttoconnectthesetwooutletstotheirrespectiveconstraints.Theeasiestwaytoaccomplishthisisusingthedocumentoutline.ClickthedisclosuretrianglenexttoConstraintsinthedocumentoutlineandlocateCurrentQuestionLabel.centerX.Control-dragfromtheViewControllertothatconstraint(Figure8.4)andselectthecorrectoutlet.DothesameforNextQuestionLabel.centerX.

WOW! eBook www.wowebook.org

Page 244: iOS Programming The Big Nerd Ranch Guide, 5th

Figure8.4Connectingaconstraintoutlet

Currently,theNextQuestionbuttonandtheanswersubviewshavetheircenterXconstrainedtothecenterXofthecurrentQuestionLabel.Whenyouimplementtheanimationforthislabeltoslideoffscreen,theothersubviewswillgowithit.Thisisnotwhatyouwant.

SelecttheconstraintthatcenterstheXvalueoftheNextQuestionbuttontothecurrentQuestionLabelanddeleteit.ThenControl-dragupwardfromtheNextQuestionbuttontoitssuperviewandselectCenterHorizontallyinContainer.

Next,youwantthetwoquestionlabelstobeonescreenwidthapart.ThecenterofnextQuestionLabelwillbehalfofthescreenwidthtotheleftoftheview.ThecenterofthecurrentQuestionLabelwillbeatitscurrentposition,centeredinthescreen.

Whentheanimationistriggered,bothlabelswillmoveafullscreenwidthtotheright,placingthenextQuestionLabelatthecenterofthescreenandthecurrentQuestionLabelhalfascreenwidthtotherightofthescreen(Figure8.5).

WOW! eBook www.wowebook.org

Page 245: iOS Programming The Big Nerd Ranch Guide, 5th

Figure8.5Slidingthelabels

Toaccomplishthis,whentheviewofViewControllerisloadedyouneedtomovethenextQuestionLabeltoitsoffscreenposition.

InViewController.swift,addanewmethodandcallitfromviewDidLoad().funcviewDidLoad(){super.viewDidLoad()

letquestion=questions[currentQuestionIndex]currentQuestionLabel.text=question

updateOffScreenLabel()}

funcupdateOffScreenLabel(){letscreenWidth=view.frame.widthnextQuestionLabelCenterXConstraint.constant=-screenWidth}

Nowyouwanttoanimatethelabelstogofromlefttoright.Animatingconstraintsisabitdifferentthananimatingotherproperties.Ifyoumodifytheconstantofaconstraintwithinananimationblock,noanimationwilloccur.Why?Afteraconstraintismodified,thesystemneedstorecalculatetheframesforalloftherelatedviewsinthehierarchytoaccommodatethischange.Itwouldbeexpensiveforanyconstraintchangetotriggerthisautomatically.(Imagineifyouupdatedquiteafewconstraints–youwouldnotwantittorecalculatetheframesaftereachchange.)Soyoumustaskthesystemtorecalculatetheframeswhenyouaredone.Todothis,youcallthemethodlayoutIfNeeded()onaview.Thiswillforcetheviewtolayoutitssubviewsbasedonthelatestconstraints.

InViewController.swift,updateanimateLabelTransitions()tochangetheconstraintconstantsandthenforcethelayoutoftheviews.funcanimateLabelTransitions(){

//Animatethealpha//andthecenterXconstraintsletscreenWidth=view.frame.widthself.nextQuestionLabelCenterXConstraint.constant=0self.currentQuestionLabelCenterXConstraint.constant+=screenWidth

UIView.animateWithDuration(0.5,delay:0,options:[],

WOW! eBook www.wowebook.org

Page 246: iOS Programming The Big Nerd Ranch Guide, 5th

animations:{self.currentQuestionLabel.alpha=0self.nextQuestionLabel.alpha=1

self.view.layoutIfNeeded()},completion:{_inswap(&self.currentQuestionLabel,&self.nextQuestionLabel)})}

Finally,inthecompletionhandler,youneedtoswapthetwoconstraintoutletsandresetthenextQuestionLabeltobeontheleftsideofthescreen.funcanimateLabelTransitions(){

//Animatethealpha//andthecenterXconstraintsletscreenWidth=view.frame.widthself.nextQuestionLabelCenterXConstraint.constant=0self.currentQuestionLabelCenterXConstraint.constant+=screenWidth

UIView.animateWithDuration(0.5,delay:0,options:[],animations:{self.currentQuestionLabel.alpha=0self.nextQuestionLabel.alpha=1

self.view.layoutIfNeeded()},completion:{_inswap(&self.currentQuestionLabel,&self.nextQuestionLabel)swap(&self.currentQuestionLabelCenterXConstraint,&self.nextQuestionLabelCenterXConstraint)

self.updateOffScreenLabel()})}

Buildandruntheapplication.Theanimationworksalmostperfectly.Thelabelsslideonandoffthescreen,andthealphavalueanimatesappropriatelyaswell.

Thereisonesmallproblemtofix,butitcanbeabitdifficulttosee.Toseeitmoreeasily,turnonSlowAnimationsfromtheDebugmenuinthesimulator(Command-T).Thewidthofallofthelabelsgetsanimated(toseethisontheanswerLabel,youneedtoclicktheShowAnswerbutton).Thisisbecausetheintrinsiccontentsizechangeswhenthetextchanges.Thefixistoforcetheviewtolayoutitssubviewsbeforetheanimationbegins.Thiswillupdatetheframesofallthreelabelstoaccommodatethenexttextbeforethealphaandslidinganimationsstart.

UpdateanimateLabelTransitions()toforcetheviewtolayoutitssubviewsbeforetheanimationbegins.funcanimateLabelTransitions(){

//Forceanyoutstandinglayoutchangestooccurview.layoutIfNeeded()

//Animatethealpha//andthecenterXconstraintsletscreenWidth=view.frame.widthself.nextQuestionLabelCenterXConstraint.constant=0self.currentQuestionLabelCenterXConstraint.constant+=screenWidth

UIView.animateWithDuration(0.5,delay:0,

WOW! eBook www.wowebook.org

Page 247: iOS Programming The Big Nerd Ranch Guide, 5th

options:[],animations:{self.currentQuestionLabel.alpha=0self.nextQuestionLabel.alpha=1

self.view.layoutIfNeeded()},completion:{_inswap(&self.currentQuestionLabel,&self.nextQuestionLabel)swap(&self.currentQuestionLabelCenterXConstraint,&self.nextQuestionLabelCenterXConstraint)

self.updateOffScreenLabel()})}

Buildandruntheapplicationandcyclethroughsomequestionsandanswers.Theminoranimationissueisnowresolved.

WOW! eBook www.wowebook.org

Page 248: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 249: iOS Programming The Big Nerd Ranch Guide, 5th

TimingFunctionsTheaccelerationoftheanimationiscontrolledbyitstimingfunction.Bydefault,animationsuseanease-in/ease-outtimingfunction.Touseadrivinganalogy,thiswouldmeanthedriveracceleratessmoothlyfromresttoaconstantspeedandthengraduallyslowsdownattheend,comingtorest.

Othertimingfunctionsincludelinear(aconstantspeedfrombeginningtoend),ease-in(acceleratingtoaconstantspeedandthenendingabruptly),andease-out(beginningatfullspeedandthenslowingdownattheend).

InViewController.swift,updatetheanimationinanimateLabelTransitions()tousealineartimingfunction.UIView.animateWithDuration(0.5,delay:0,options:[.CurveLinear],animations:{self.currentQuestionLabel.alpha=0self.nextQuestionLabel.alpha=1

self.view.layoutIfNeeded()},completion:{_inswap(&self.currentQuestionLabel,&self.nextQuestionLabel)swap(&self.currentQuestionLabelCenterXConstraint,&self.nextQuestionLabelCenterXConstraint)

self.updateOffScreenLabel()})

Now,asopposedtousingthedefaultease-in/ease-outanimationcurve,theanimationwillhavealinearanimationcurve.TheoptionsparametertakesinaUIViewAnimationOptionsargument.Whyisthisargumentinsquarebrackets?Therearemanyoptionsforananimationinadditiontothetimingfunction.Becauseofthis,youneedawayofspecifyingmorethanoneoption–anarray.UIViewAnimationOptionsconformstotheOptionSetTypeprotocol,whichallowsyoutogroupmultiplevaluesusinganarray.

Herearesomeofthepossibleanimationoptionsthatyoucanpassintotheoptionsparameter.

Animationcurveoptions

Thesecontroltheaccelerationoftheanimation.Possiblevaluesare

UIViewAnimationOptions.CurveEaseInOut

UIViewAnimationOptions.CurveEaseIn

UIViewAnimationOptions.CurveEaseOut

UIViewAnimationOptions.CurveLinear

UIViewAnimationOptions.AllowUserInteraction

Bydefault,viewscannotbeinteractedwithwhenanimating.Specifyingthis

WOW! eBook www.wowebook.org

Page 250: iOS Programming The Big Nerd Ranch Guide, 5th

optionoverridesthedefault.Thiscanbeusefulforrepeatinganimations,suchasapulsingview.

UIViewAnimationOptions.Repeat

Repeatstheanimationindefinitely;oftenpairedwiththeUIViewAnimationOptions.Autoreverseoption.

UIViewAnimationOptions.Autoreverse

Runstheanimationforwardandthenbackward,returningtheviewtoitsinitialstate.

BesuretocheckouttheConstantssectionoftheUIViewClassReferencetoseeallofthepossibleoptions.

WOW! eBook www.wowebook.org

Page 251: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 252: iOS Programming The Big Nerd Ranch Guide, 5th

BronzeChallenge:SpringAnimationsiOShasapowerfulphysicsenginebuiltin.Aneasywaytoharnessthispowerisbyusingaspringanimation.//UIView

classfuncanimateWithDuration(duration:NSTimeInterval,delay:NSTimeInterval,usingSpringWithDampingdampingRatio:CGFloat,initialSpringVelocityvelocity:CGFloat,options:UIViewAnimationOptions,animations:()->Void,completion:((Bool)->Void)?)

Usethismethodtohavethetwolabelsanimateonandoffthescreeninaspring-likefashion.RefertotheUIViewdocumentationtounderstandeachofthearguments.

WOW! eBook www.wowebook.org

Page 253: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 254: iOS Programming The Big Nerd Ranch Guide, 5th

SilverChallenge:LayoutGuidesIfyourotateintolandscape,thenextQuestionLabelbecomesvisible.Insteadofhardcodingthespacingconstraint’sconstant,useaninstanceofUILayoutGuidetospacethetwolabelsapart.ThislayoutguideshouldhaveawidthconstraintequaltotheViewController’sviewtoensurethatthenextQuestionLabelremainsoffscreenwhennotanimating.

WOW! eBook www.wowebook.org

Page 255: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 256: iOS Programming The Big Nerd Ranch Guide, 5th

9UITableViewand

UITableViewControllerManyiOSapplicationsshowtheuseralistofitemsandallowtheusertoselect,delete,orreorderitemsonthelist.Whetheranapplicationdisplaysalistofpeopleintheuser’saddressbookoralistofbest-sellingitemsontheAppStore,itisaUITableViewdoingthework.

AUITableViewdisplaysasinglecolumnofdatawithavariablenumberofrows.Figure9.1showssomeexamplesofUITableView.

Figure9.1ExamplesofUITableView

WOW! eBook www.wowebook.org

Page 257: iOS Programming The Big Nerd Ranch Guide, 5th

BeginningtheHomepwnerApplicationInthischapter,youaregoingtostartanapplicationcalledHomepwnerthatkeepsaninventoryofallyourpossessions.Inthecaseofafireorothercatastrophe,youwillhavearecordforyourinsurancecompany.(“Homepwner,”bytheway,isnotatypo.Ifyouneedadefinitionfortheword“pwn,”pleasevisithttp://www.urbandictionary.com.)

Sofar,youriOSprojectshavebeensmall,butHomepwnerwillgrowintoarealisticallycomplexapplicationoverthecourseofeightchapters.Bytheendofthischapter,HomepwnerwillpresentalistofIteminstancesinaUITableView,asshowninFigure9.2.

Figure9.2Homepwner:phase1

CreateanewiOSSingleViewApplicationprojectandconfigureitasshowninFigure9.3.

WOW! eBook www.wowebook.org

Page 258: iOS Programming The Big Nerd Ranch Guide, 5th

Figure9.3ConfiguringHomepwner

WOW! eBook www.wowebook.org

Page 259: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 260: iOS Programming The Big Nerd Ranch Guide, 5th

UITableViewControllerAUITableViewisaviewobject.RecallthatintheModel-View-Controllerdesignpattern,whichiOSdevelopersdotheirbesttofollow,eachclassfallsintoexactlyoneofthefollowingcategories:

model:holdsdataandknowsnothingabouttheuserinterface

view:isvisibletotheuserandknowsnothingaboutthemodelobjects

controller:keepstheuserinterfaceandthemodelobjectsinsyncandcontrolstheflowoftheapplication

Thus,aUITableView,aviewobject,doesnothandleapplicationlogicordata.WhenusingaUITableView,youmustconsiderwhatelseisnecessarytogetthetableworkinginyourapplication:

AUITableViewtypicallyneedsaviewcontrollertohandleitsappearanceonthescreen.

AUITableViewneedsadatasource.AUITableViewasksitsdatasourceforthenumberofrowstodisplay,thedatatobeshowninthoserows,andothertidbitsthatmakeaUITableViewausefuluserinterface.Withoutadatasource,atableviewisjustanemptycontainer.ThedataSourceforaUITableViewcanbeanytypeofobjectaslongasitconformstotheUITableViewDataSourceprotocol.

AUITableViewtypicallyneedsadelegatethatcaninformotherobjectsofeventsinvolvingtheUITableView.ThedelegatecanbeanyobjectaslongasitconformstotheUITableViewDelegateprotocol.

AninstanceoftheclassUITableViewControllercanfillallthreeroles:viewcontroller,datasource,anddelegate.

UITableViewControllerisasubclassofUIViewController,soaUITableViewControllerhasaview.AUITableViewController’sviewisalwaysaninstanceofUITableView,andtheUITableViewControllerhandlesthepreparationandpresentationoftheUITableView.

WhenaUITableViewControllercreatesitsview,thedataSourceanddelegatepropertiesoftheUITableViewareautomaticallysettopointattheUITableViewController(Figure9.4).

WOW! eBook www.wowebook.org

Page 261: iOS Programming The Big Nerd Ranch Guide, 5th

Figure9.4UITableViewController-UITableViewrelationship

SubclassingUITableViewController

NowyouaregoingtoimplementasubclassofUITableViewControllerforHomepwner.CreateanewSwiftfilenamedItemsViewController.InItemsViewController.swift,defineaUITableViewControllersubclassnamedItemsViewController.importFoundationimportUIKit

classItemsViewController:UITableViewController{

}

NowopenMain.storyboard.Youwanttheinitialviewcontrollertobeatableviewcontroller.SelecttheexistingViewControlleronthecanvasandpressDelete.ThendragaTableViewControllerfromtheobjectlibraryontothecanvas.WiththeTableViewControllerselected,openitsidentityinspectorandchangetheclasstoItemsViewController.Finally,opentheattributesinspectorforItemsViewControllerandchecktheboxforIsInitialViewController.

Buildandrunyourapplication.Youshouldseeanemptytableview,asshowninFigure9.5.AsasubclassofUIViewController,aUITableViewControllerinheritstheviewproperty.Whenthispropertyisaccessedforthefirsttime,theloadView()methodiscalled,whichcreatesandloadsaviewobject.AUITableViewController’sviewisalwaysaninstanceofUITableView,soaskingfortheviewofaUITableViewControllergetsyouabright,shiny,andemptytableview.

WOW! eBook www.wowebook.org

Page 262: iOS Programming The Big Nerd Ranch Guide, 5th

Figure9.5EmptyUITableView

YounolongerneedtheViewController.swiftfilethatthetemplatecreatedforyou.SelectthisfileintheprojectnavigatorandpressDelete.

WOW! eBook www.wowebook.org

Page 263: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 264: iOS Programming The Big Nerd Ranch Guide, 5th

CreatingtheItemClassYourtableviewneedsomerowstodisplay.Eachrowinthetableviewwilldisplayanitemwhichwillhaveinformationsuchasaname,serialnumber,andvalueindollars.

CreateanewSwiftfilenamedItem.InItem.swift,definetheItemclassandgiveitfourproperties.importFoundationimportUIKit

classItem:NSObject{varname:StringvarvalueInDollars:IntvarserialNumber:String?letdateCreated:NSDate}

IteminheritsfromNSObject.NSObjectisthebaseclassthatmostObjective-Cclassesinheritfrom.AlloftheUIKitclassesthatyouhaveworkedwith–UIView,UITextField,andUIViewController,tonameafew–inheriteitherdirectlyorindirectlyfromNSObject.YourownclasseswilloftenneedtoinheritfromNSObjectwhentheyneedtointerfacewiththeruntimesystem.

NoticethatserialNumberisanoptionalString,necessarybecauseanitemmaynothaveaserialnumber.Also,noticethatnoneofthepropertieshaveadefaultvalue.Youwillneedtogivethemvaluesinadesignatedinitializer.

Custominitializers

YoulearnedaboutstructinitializersinChapter2.Initializersonstructsarefairlystraightforwardbecausestructsdonotsupportinheritance.Classes,ontheotherhand,havesomerulesforinitializerstosupportinheritance.

Classescanhavetwokindsofinitializers:designatedinitializersandconvenienceinitializers.

Adesignatedinitializerisaprimaryinitializerfortheclass.Everyclasshasatleastonedesignatedinitializer.Adesignatedinitializerensuresthatallpropertiesintheclasshaveavalue.Onceitensuresthat,adesignatedinitializercallsadesignatedinitializeronitssuperclass(ifithasone).

ImplementanewdesignatedinitializerontheItemclassthatsetstheinitialvaluesforalloftheproperties.importUIKit

classItem:NSObject{varname:StringvarvalueInDollars:IntvarserialNumber:String?letdateCreated:NSDate

init(name:String,serialNumber:String?,valueInDollars:Int){self.name=nameself.valueInDollars=valueInDollarsself.serialNumber=serialNumber

WOW! eBook www.wowebook.org

Page 265: iOS Programming The Big Nerd Ranch Guide, 5th

self.dateCreated=NSDate()

super.init()}}

Thisinitializertakesinargumentsforthename,serialNumber,andvalueInDollars.Sincetheargumentnamesandthepropertynamesarethesame,youmustuseselftodistinguishthepropertyfromtheargument.

Nowthatyouhaveimplementedyourowncustominitializer,youlosethefreeinitializer–init()–thatclasseshave.Thefreeinitializerisusefulwhenallofyourclass’spropertieshavedefaultvaluesandyoudonotneedtodoadditionalworktocreatethenewinstance.TheItemclassdoesnotsatisfythiscriteria,soyouhavedeclaredacustominitializerfortheclass.

Everyclassmusthaveatleastonedesignatedinitializer,butconvenienceinitializersareoptional.Youcanthinkofconvenienceinitializersashelpers.Aconvenienceinitializeralwayscallsanotherinitializeronthesameclass.Convenienceinitializersareindicatedbytheconveniencekeywordbeforetheinitializername.

AddaconvenienceinitializertoItemthatcreatesarandomlygenerateditem.convenienceinit(random:Bool=false){ifrandom{letadjectives=["Fluffy","Rusty","Shiny"]letnouns=["Bear","Spork","Mac"]

varidx=arc4random_uniform(UInt32(adjectives.count))letrandomAdjective=adjectives[Int(idx)]

idx=arc4random_uniform(UInt32(nouns.count))letrandomNoun=nouns[Int(idx)]

letrandomName="\(randomAdjective)\(randomNoun)"letrandomValue=Int(arc4random_uniform(100))letrandomSerialNumber=NSUUID().UUIDString.componentsSeparatedByString("-").first!

self.init(name:randomName,serialNumber:randomSerialNumber,valueInDollars:randomValue)}else{self.init(name:"",serialNumber:nil,valueInDollars:0)}}

Ifrandomistrue,theinstanceisconfiguredwitharandomname,serialnumber,andvalue.(Thearc4random_uniformfunctionreturnsarandomvaluebetween0,inclusive,andthevaluepassedinastheargument,exclusive.)Noticethatattheendofbothbranchesoftheconditional,youarecallingthroughtothedesignatedinitializerforItem.Convenienceinitializersmustcallanotherinitializeronthesametype,whereasdesignatedinitializersmustcalladesignatedinitializeronitssuperclass.

TheItemclassisreadyforwork.InthenextsectionyouwilldisplayanarrayofIteminstancesinatableview.

WOW! eBook www.wowebook.org

Page 266: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 267: iOS Programming The Big Nerd Ranch Guide, 5th

UITableView’sDataSourceTheprocessofprovidingrowstoaUITableViewinCocoaTouch(thecollectionofframeworksusedtobuildiOSapps)isdifferentfromthetypicalproceduralprogrammingtask.Inaproceduraldesign,youtellthetableviewwhatitshoulddisplay.InCocoaTouch,thetableviewasksanotherobject–itsdataSource–whatitshoulddisplay.Inthiscase,theItemsViewControlleristhedatasource,soitneedsawaytostoreitemdata.

YouaregoingtouseanarraytostoretheIteminstances,butwithatwist.ThearraythatholdstheIteminstanceswillbeabstractedintoanotherobject–anItemStore(Figure9.6).

Figure9.6Homepwnerobjectdiagram

Ifanobjectwantstoseealloftheitems,itwillasktheItemStoreforthearraythatcontainsthem.Infuturechapters,thestorewillberesponsibleforperformingoperationsonthearray,likereordering,adding,andremovingitems.Itwillalsoberesponsibleforsavingandloadingtheitemsfromdisk.

CreateanewSwiftfilenamedItemStore.InItemStore.swift,definetheItemStoreclassanddeclareapropertytostorethelistofItems.importFoundationimportUIKit

WOW! eBook www.wowebook.org

Page 268: iOS Programming The Big Nerd Ranch Guide, 5th

classItemStore{

varallItems=[Item]()

}

ItemStoreisaSwiftbaseclass–itdoesnotinheritfromanyotherclass.UnliketheItemclassthatyoudefinedearlier,ItemStoredoesnotrequireanyofthebehaviorthatNSObjectaffords.

TheItemsViewControllerwillcallamethodonItemStorewhenitwantsanewItemtobecreated.TheItemStorewilloblige,creatingtheobjectandaddingittoanarrayofinstancesofItem.

InItemStore.swift,implementcreateItem()tocreateandreturnanewItem.funccreateItem()->Item{letnewItem=Item(random:true)

allItems.append(newItem)

returnnewItem}

Givingthecontrolleraccesstothestore

InItemsViewController.swift,addapropertyforanItemStore.classItemsViewController:UITableViewController{

varitemStore:ItemStore!

Now,whereshouldyousetthispropertyontheItemsViewControllerinstance?Whentheapplicationfirstlaunches,theAppDelegate’sapplication(_:didFinishLaunchingWithOptions:)methodiscalled.TheAppDelegateisdeclaredinAppDelegate.swiftand,asthenameimplies,servesasthedelegatefortheapplicationitself.Itisresponsibleforhandlingthechangesinstatethattheapplicationgoesthrough.YouwilllearnmoreabouttheAppDelegateandthestatesthattheapplicationgoesthroughinChapter15.

OpenAppDelegate.swift.AccesstheItemsViewController(whichwillbetherootViewControllerofthewindow)andsetitsitemStorepropertytobeanewinstanceofItemStore.funcapplication(application:UIApplication,didFinishLaunchingWithOptionslaunchOptions:[NSObject:AnyObject]?)->Bool{//Overridepointforcustomizationafterapplicationlaunch.

//CreateanItemStoreletitemStore=ItemStore()

//AccesstheItemsViewControllerandsetitsitemstoreletitemsController=window!.rootViewControlleras!ItemsViewControlleritemsController.itemStore=itemStore

returntrue}

Finally,inItemStore.swift,implementthedesignatedinitializertoaddfiverandomitems.

WOW! eBook www.wowebook.org

Page 269: iOS Programming The Big Nerd Ranch Guide, 5th

init(){for_in0..<5{createItem()}}

AtthispointyoumaybewonderingwhyitemStorewassetexternallyontheItemsViewController.Whydidn’ttheItemsViewControllerinstanceitselfjustcreateaninstanceofthestore?Thereasonforthisapproachisbasedonafairlycomplextopiccalledthedependencyinversionprinciple.Theessentialgoalofthisprincipleistodecoupleobjectsinanapplicationbyinvertingcertaindependenciesbetweenthem.Thisresultsinmorerobustandmaintainablecode.

Thedependencyinversionprinciplestatesthat:

1. High-levelobjectsshouldnotdependonlow-levelobjects.Bothshoulddependonabstractions.

2. Abstractionsshouldnotdependondetails.Detailsshoulddependonabstractions.

TheabstractionrequiredbythedependencyinversionprincipleinHomepwneristheconceptofa“store.”Astoreisalower-levelobjectthatretrievesandsavesIteminstancesthroughdetailsthatareonlyknowntothatclass.ItemsViewControllerisahigher-levelobjectthatonlyknowsthatitwillbeprovidedwithautilityobject(thestore)fromwhichitcanobtainalistofIteminstancesandtowhichitcanpassneworupdatedIteminstancestobestoredpersistently.ThisresultsinadecouplingbecauseItemsViewControllerisnotdependentonItemStore.Infact,aslongasthestoreabstractionisrespected,ItemStorecouldbereplacedbyanotherobjectthatfetchesIteminstancesdifferently(suchasbyusingawebservice)withoutanychangestoItemsViewController.

Acommonpatternusedwhenimplementingthedependencyinversionprincipleisdependencyinjection.Initssimplestform,higher-levelobjectsdonotassumewhichlower-levelobjectstheyneedtouse.Instead,thosearepassedtothemthroughaninitializerorproperty.InyourimplementationofItemsViewController,youusedinjectionthroughapropertytogiveitastore.

Implementingdatasourcemethods

Nowthattherearesomeitemsinthestore,youneedtoteachItemsViewControllerhowtoturnthoseitemsintorowsthatitsUITableViewcandisplay.WhenaUITableViewwantstoknowwhattodisplay,itcallsmethodsfromthesetofmethodsdeclaredintheUITableViewDataSourceprotocol.

OpenthedocumentationandsearchfortheUITableViewDataSourceprotocolreference.SelectTasksfromthelefthandpane(Figure9.7).

WOW! eBook www.wowebook.org

Page 270: iOS Programming The Big Nerd Ranch Guide, 5th

Figure9.7UITableViewDataSourceprotocoldocumentation

IntheConfiguringaTableViewtask,noticethattwoofthemethodsaremarkedRequired.ForItemsViewControllertoconformtoUITableViewDataSource,itmustimplementtableView(_:numberOfRowsInSection:)andtableView(_:cellForRowAtIndexPath:).Thesemethodstellthetableviewhowmanyrowsitshoulddisplayandwhatcontenttodisplayineachrow.

WheneveraUITableViewneedstodisplayitself,itcallsaseriesofmethods(therequiredmethodsplusanyoptionalonesthathavebeenimplemented)onitsdataSource.TherequiredmethodtableView(_:numberOfRowsInSection:)returnsanintegervalueforthenumberofrowsthattheUITableViewshoulddisplay.InthetableviewforHomepwner,thereshouldbearowforeachentryinthestore.

InItemsViewController.swift,implementtableView(_:numberOfRowsInSection:).overridefunctableView(tableView:UITableView,numberOfRowsInSectionsection:Int)->Int{returnitemStore.allItems.count}

Wonderingaboutthesectionthatthismethodrefersto?Tableviewscanbebrokenupintosections,andeachsectionhasitsownsetofrows.Forexample,intheaddressbook,allnamesbeginningwith“C”aregroupedtogetherinasection.Bydefault,atableviewhasonesection,andinthischapter,youwillworkwithonlyone.Onceyouunderstandhowatableviewworks,itisnothardtousemultiplesections.Infact,usingsectionsisthefirstchallengeattheendofthischapter.

WOW! eBook www.wowebook.org

Page 271: iOS Programming The Big Nerd Ranch Guide, 5th

ThesecondrequiredmethodintheUITableViewDataSourceprotocolistableView(_:cellForRowAtIndexPath:).Toimplementthismethod,youneedtolearnaboutanotherclass–UITableViewCell.

WOW! eBook www.wowebook.org

Page 272: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 273: iOS Programming The Big Nerd Ranch Guide, 5th

UITableViewCellsEachrowofatableviewisaview.TheseviewsareinstancesofUITableViewCell.Inthissection,youwillcreatetheinstancesofUITableViewCelltofillthetableview.

Acellitselfhasonesubview–itscontentView(Figure9.8).ThecontentViewisthesuperviewforthecontentofthecell.Thecellmayalsohaveanaccessoryview.

Figure9.8UITableViewCelllayout

Theaccessoryviewshowsanaction-orientedicon,suchasacheckmark,adisclosureicon,oraninformationbutton.Theseiconsareaccessedthroughpredefinedconstantsfortheappearanceoftheaccessoryview.ThedefaultisUITableViewCellAccessoryType.None,andthatiswhatyouaregoingtouseinthischapter.ButyouwillseetheaccessoryviewagaininChapter22.(Curiousnow?SeethedocumentationforUITableViewCellformoredetails.)

TherealmeatofaUITableViewCellisthecontentView,whichhasthreesubviewsofitsown(Figure9.9).TwoofthosesubviewsareUILabelinstancesthatarepropertiesofUITableViewCellnamedtextLabelanddetailTextLabel.ThethirdsubviewisaUIImageViewcalledimageView.Inthischapter,youwillusetextLabelanddetailTextLabel.

WOW! eBook www.wowebook.org

Page 274: iOS Programming The Big Nerd Ranch Guide, 5th

Figure9.9UITableViewCellhierarchy

EachcellalsohasaUITableViewCellStylethatdetermineswhichsubviewsareusedandtheirpositionwithinthecontentView.ExamplesofthesestylesandtheirconstantsareshowninFigure9.10.

Figure9.10UITableViewCellStyle:stylesandconstants

CreatingandretrievingUITableViewCells

Fornow,eachcellwilldisplaythenameofItemasitstextLabelandtheserialNumberofItemasitsdetailTextLabel.Tomakethishappen,youneed

WOW! eBook www.wowebook.org

Page 275: iOS Programming The Big Nerd Ranch Guide, 5th

toimplementthesecondrequiredmethodfromtheUITableViewDataSourceprotocol,tableView(_:cellForRowAtIndexPath:).Thismethodwillcreateacell,setitstextLabeltothenameoftheItem,setitsdetailTextLabeltothevalueInDollarsoftheItem,andreturnittotheUITableView(Figure9.11).

Figure9.11UITableViewCellretrieval

HowdoyoudecidewhichcellanItemcorrespondsto?OneoftheparameterssenttotableView(_:cellForRowAtIndexPath:)isanNSIndexPath,whichhastwoproperties:sectionandrow.Whenthismethodiscalledonadatasource,thetableviewisasking,“CanIhaveacelltodisplayinsectionX,rowY?”Becausethereisonlyonesectioninthisexercise,yourimplementationwillonlybeconcernedwiththeindexpath’srow.

InItemsViewController.swift,implementtableView(_:cellForRowAtIndexPath:)sothatthenthrowdisplaysthenthentryintheallItemsarray.overridefunctableView(tableView:UITableView,cellForRowAtIndexPathindexPath:NSIndexPath)->UITableViewCell{//CreateaninstanceofUITableViewCell,withdefaultappearanceletcell=UITableViewCell(style:.Value1,reuseIdentifier:"UITableViewCell")

//Setthetextonthecellwiththedescriptionoftheitem//thatisatthenthindexofitems,wheren=rowthiscell//willappearinonthetableviewletitem=itemStore.allItems[indexPath.row]

cell.textLabel?.text=item.namecell.detailTextLabel?.text="$\(item.valueInDollars)"

returncell}

BuildandruntheapplicationnowandyouwillseeaUITableViewpopulatedwithalistWOW! eBook

www.wowebook.org

Page 276: iOS Programming The Big Nerd Ranch Guide, 5th

ofrandomitems.

ReusingUITableViewCells

iOSdeviceshavealimitedamountofmemory.IfyouweredisplayingalistwiththousandsofentriesinaUITableView,youwouldhavethousandsofinstancesofUITableViewCell.Mostofthesecellswouldtakeupmemoryneedlessly.Afterall,iftheusercannotcurrentlyseeacellonscreen,thenthereisnoreasonforthatcelltohaveaclaimonmemory.

Toconservememoryandimproveperformance,youcanreusetableviewcells.Whentheuserscrollsthetable,somecellsmoveoffscreen.Offscreencellsareputintoapoolofcellsavailableforreuse.Then,insteadofcreatingabrandnewcellforeveryrequest,thedatasourcefirstchecksthepool.Ifthereisanunusedcell,thedatasourceconfiguresitwithnewdataandreturnsittothetableview(Figure9.12).

Figure9.12ReusableinstancesofUITableViewCell

Thereisoneproblem:sometimesaUITableViewhasdifferenttypesofcells.Occasionally,yousubclassUITableViewCelltocreateaspeciallookorbehavior.However,differentsubclassesfloatingaroundthepoolofreusablecellscreatethepossibilityofgettingbackacellofthewrongtype.Youmustbesureofthetypeofthecellreturnedsothatyoucanbesureofwhatpropertiesandmethodsithas.

Notethatyoudonotcareaboutgettinganyspecificcelloutofthepoolbecauseyouaregoingtochangethecellcontentanyway.Whatyouneedisacellofaspecifictype.The

WOW! eBook www.wowebook.org

Page 277: iOS Programming The Big Nerd Ranch Guide, 5th

goodnewsisthateverycellhasareuseIdentifierpropertyoftypeString.Whenadatasourceasksthetableviewforareusablecell,itpassesastringandsays,“Ineedacellwiththisreuseidentifier.”Byconvention,thereuseidentifieristypicallythenameofthecellclass.

Toreusecells,youneedtoregistereitheraprototypecelloraclasswiththetableviewforaspecificreuseidentifier.YouaregoingtoregisterthedefaultUITableViewCellclass.Youtellthetableview,“Hey,anytimeIaskforacellwiththisreuseidentifier,givemebackacellthatisthisspecificclass.”Thetableviewwilleithergiveyouacellfromthereusepoolorinstantiateanewcelliftherearenocellsofthattypeinthereusepool.

OpenMain.storyboard.NoticeinthetableviewthatthereisasectionforPrototypeCells(Figure9.13).Inthisarea,youcanconfigurethedifferentkindsofcellsthatyouneedfortheassociatedtableview.Ifyouarecreatingcustomcells,thisiswhereyouwillsetuptheinterfaceforthecells.ItemsViewControlleronlyneedsonekindofcell,andusingoneofthebuilt-instyleswillworkgreatfornow,soyouwillonlyneedtoconfiguresomeattributesonthecellthatisalreadyonthecanvas.

Figure9.13Prototypecells

Selecttheprototypecellandopenitsattributesinspector.ChangetheStyletoRightDetail(whichcorrespondstoUITableViewCellStyle.Value1)andgiveitanIdentifierofUITableViewCell(Figure9.14).

WOW! eBook www.wowebook.org

Page 278: iOS Programming The Big Nerd Ranch Guide, 5th

Figure9.14Tableviewcellattributes

Next,inItemsViewController.swift,updatetableView(_:cellForRowAtIndexPath:)toreusecells.overridefunctableView(tableView:UITableView,cellForRowAtIndexPathindexPath:NSIndexPath)->UITableViewCell{//CreateaninstanceofUITableViewCell,withdefaultappearanceletcell=UITableViewCell(style:.Value1,reuseIdentifier:"UITableViewCell")

//Getaneworrecycledcellletcell=tableView.dequeueReusableCellWithIdentifier("UITableViewCell",forIndexPath:indexPath)

...}

ThemethoddequeueReusableCellWithIdentifier(_:forIndexPath:)willcheckthepool,orqueue,ofcellstoseewhetheracellwiththecorrectreuseidentifieralreadyexists.Ifso,itwill“dequeue”thatcell.Ifthereisnotanexistingcell,anewcellwillbecreatedandreturned.

Buildandruntheapplication.Thebehavioroftheapplicationshouldremainthesame.Reusingcellsmeansthatyouonlyhavetocreateahandfulofcells,whichputsfewerdemandsonmemory.Yourapplication’susers(andtheirdevices)willthankyou.

WOW! eBook www.wowebook.org

Page 279: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 280: iOS Programming The Big Nerd Ranch Guide, 5th

ContentInsetsAsyouhavebeenrunningtheapplicationthroughoutthischapter,youmighthavenoticedthatthefirsttableviewcellunderlapsthestatusbar(Figure9.15).Theinterfacesfortheapplicationsyoucreatefilluptheentirewindowofthedevice.Thestatusbar,ifvisible,isplacedontopoftheinterface,soyourinterfacesmustaccountfortheplacementofthestatusbar.

Figure9.15Tableviewcellunderlappingstatusbar

Tohavethetableviewcellsnotunderlapthestatusbar,youwilladdsomepaddingtothetopofthetableview.AUITableViewisasubclassofUIScrollView,fromwhichitinheritsthecontentInsetproperty.Youcanthinkofthecontentinsetasapaddingforallfoursidesofthescrollview.

InItemsViewController.swift,overrideviewDidLoad()toupdatethetableviewcontentinset.overridefuncviewDidLoad(){super.viewDidLoad()

//GettheheightofthestatusbarletstatusBarHeight=UIApplication.sharedApplication().statusBarFrame.height

letinsets=UIEdgeInsets(top:statusBarHeight,left:0,bottom:0,right:0)tableView.contentInset=insetstableView.scrollIndicatorInsets=insets}

Thetopofthetableviewisgivenacontentinsetequaltotheheightofthestatusbar.Thiswillmakethecontentappearbelowthestatusbarwhenthetableviewisscrolledtothetop.Thescrollindicatorswillalsounderlapthestatusbar,soyougivethemthesameinsetstohavethemappearjustbelowthestatusbar.

NoticethatyouaccessthetableViewpropertyontheItemsViewControllertogetatthetableview.ThispropertyisinheritedfromUITableViewControllerandreturnsthecontroller’stableview.WhileyoucangetthesameobjectbyaccessingtheviewofaUITableViewController,usingtableViewtellsthecompilerthatthe

WOW! eBook www.wowebook.org

Page 281: iOS Programming The Big Nerd Ranch Guide, 5th

returnedobjectwillbeaninstanceofUITableView.Thus,callingamethodoraccessingapropertythatisspecifictoUITableViewwillnotgenerateanerror.

Buildandruntheapplication.Thetableviewcellcontentnolongerunderlapsthestatusbarwhenthetableviewisscrolledtothetop(Figure9.16).

Figure9.16Tableviewwithadjustedcontentinset

WOW! eBook www.wowebook.org

Page 282: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 283: iOS Programming The Big Nerd Ranch Guide, 5th

BronzeChallenge:SectionsHavetheUITableViewdisplaytwosections–oneforitemsworthmorethan$50andonefortherest.Beforeyoustartthischallenge,copythefoldercontainingtheprojectandallofitssourcefilesinFinder.Thentacklethechallengeinthecopiedproject;youwillneedtheoriginaltobuildoninthefollowingchapters.

WOW! eBook www.wowebook.org

Page 284: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 285: iOS Programming The Big Nerd Ranch Guide, 5th

SilverChallenge:ConstantRowsMakeitsothelastrowoftheUITableViewalwayshasthetextNomoreitems!.Makesurethisrowappearsregardlessofthenumberofitemsinthestore(including0items).

WOW! eBook www.wowebook.org

Page 286: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 287: iOS Programming The Big Nerd Ranch Guide, 5th

GoldChallenge:CustomizingtheTableMakeeachrow’sheight60points,exceptforthelastrowfromthesilverchallenge,whichshouldremain44points.Then,changethefontsizeofeveryrowexceptthelastto20points.Finally,makethebackgroundoftheUITableViewdisplayanimage.(Tomakethispixel-perfect,youwillneedanimageofthecorrectsizedependingonyourdevice.RefertothechartinChapter1.)

WOW! eBook www.wowebook.org

Page 288: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 289: iOS Programming The Big Nerd Ranch Guide, 5th

10EditingUITableView

Inthelastchapter,youcreatedanapplicationthatdisplaysalistofIteminstancesinaUITableView.Thenextstepisallowingtheusertointeractwiththetable–toadd,delete,andmoverows.Figure10.1showswhatHomepwnerwilllooklikebytheendofthischapter.

Figure10.1Homepwnerineditingmode

WOW! eBook www.wowebook.org

Page 290: iOS Programming The Big Nerd Ranch Guide, 5th

EditingModeUITableViewhasaneditingproperty,andwhenthispropertyissettotrue,theUITableViewenterseditingmode.Oncethetableviewisineditingmode,therowsofthetablecanbemanipulatedbytheuser.Dependingonhowthetableviewisconfigured,theusercanchangetheorderoftherows,addrows,orremoverows.Editingmodedoesnotallowtheusertoeditthecontentofarow.

Butfirst,theuserneedsawaytoputtheUITableViewineditingmode.Fornow,youaregoingtoincludeabuttonintheheaderviewofthetable.Aheaderviewappearsatthetopofatableandisusefulforaddingsection-wideortable-widetitlesandcontrols.ItcanbeanyUIViewinstance.

Notethatthetableviewusestheword“header”intwodifferentways:therecanbeatableheaderandtherecanbesectionheaders.Likewise,therecanbeatablefooterandsectionfooters(Figure10.2).

Figure10.2Sectionheadersandfooters

Youarecreatingatableheaderview.Itwillhavetwosubviewsthatareinstancesof

WOW! eBook www.wowebook.org

Page 291: iOS Programming The Big Nerd Ranch Guide, 5th

UIButton:onetotoggleeditingmodeandtheothertoaddanewItemtothetable.Youcouldcreatethisviewprogrammatically,butinthiscaseyouwillcreatetheviewanditssubviewsinthestoryboardfile.

First,let’ssetupthenecessarycode.ReopenHomepwner.xcodeproj.InItemsViewController.swift,stubouttwomethodsintheimplementation.classItemsViewController:UITableViewController{

varitemStore:ItemStore!

@IBActionfuncaddNewItem(sender:AnyObject){

}

@IBActionfunctoggleEditingMode(sender:AnyObject){

}

NowopenMain.storyboard.Fromtheobjectlibrary,dragaViewtotheverytopofthetableview.Thiswilladdtheviewasaheaderviewforthetableview.Resizetheheightofthisviewtobeabout60points.(Youcanusethesizeinspectorifyouwanttomakeitexact.)

NowdragtwoButtonsfromtheobjectlibrarytotheheaderview.ChangetheirtextandpositionthemasshowninFigure10.3.Youdonotneedtobeexact–youwilladdconstraintssoontopositionthebuttons.

Figure10.3Addingbuttonstotheheaderview

SelectbothofthebuttonsandopentheAutoLayoutAlignmenu.SelectVerticallyinContainerwithaconstantof0.MakesureUpdateFramesissettoNone,andthenclickAdd2Constraints(Figure10.4).

WOW! eBook www.wowebook.org

Page 292: iOS Programming The Big Nerd Ranch Guide, 5th

Figure10.4Alignmenuconstraints

OpenthePinmenuandconfigureitexactlylikeFigure10.5.Makesurethevaluesfortheleadingandtrailingconstraintssaveafteryouhavetypedthem;sometimesthevaluesdonotsave,soitcanbeabittricky.Whenyouhavedonethat,clickAdd4Constraints.

WOW! eBook www.wowebook.org

Page 293: iOS Programming The Big Nerd Ranch Guide, 5th

Figure10.5Pinmenuconstraints

Finally,connecttheactionsforthetwobuttonsasshowninFigure10.6.

Figure10.6Connectingthetwoactions

Buildandruntheapplicationtoseetheinterface.

Nowlet’simplementthetoggleEditingMode(_:)method.YoucouldtoggletheeditingpropertyofUITableViewdirectly.However,UIViewControlleralso

WOW! eBook www.wowebook.org

Page 294: iOS Programming The Big Nerd Ranch Guide, 5th

hasaneditingproperty.AUITableViewControllerinstanceautomaticallysetstheeditingpropertyofitstableviewtomatchitsowneditingproperty.Bysettingtheeditingpropertyontheviewcontrolleritself,itcanensurethatotheraspectsoftheinterfacealsoenterandleaveeditingmode.YouwillseeanexampleofthisinChapter13withUIViewController’seditButtonItem().

Tosettheeditingpropertyforaviewcontroller,youcallthemethodsetEditing(_:animated:).InItemsViewController.swift,implementtoggleEditingMode(_:).@IBActionfunctoggleEditingMode(sender:AnyObject){//Ifyouarecurrentlyineditingmode...ifediting{//Changetextofbuttontoinformuserofstatesender.setTitle("Edit",forState:.Normal)

//TurnoffeditingmodesetEditing(false,animated:true)}else{//Changetextofbuttontoinformuserofstatesender.setTitle("Done",forState:.Normal)

//EntereditingmodesetEditing(true,animated:true)}}

Buildandrunyourapplication.TaptheEditbuttonandtheUITableViewwillentereditingmode(Figure10.7).

Figure10.7UITableViewineditingmode

WOW! eBook www.wowebook.org

Page 295: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 296: iOS Programming The Big Nerd Ranch Guide, 5th

AddingRowsTherearetwocommoninterfacesforaddingrowstoatableviewatruntime.

Abuttonabovethecellsofthetableview:usuallyforaddingarecordforwhichthereisadetailview.Forexample,intheContactsapp,youtapabuttonwhenyoumeetanewpersonandwanttotakedownhisorherinformation.

Acellwithagreenplussign:usuallyforaddinganewfieldtoarecord,suchaswhenyouwanttoaddabirthdaytoaperson’srecordintheContactsapp.Ineditingmode,youtapthegreenplussignnextto“addbirthday.”

Inthisexercise,youwillusethefirstoptionandcreateaNewbuttonintheheaderview.Whenthisbuttonistapped,anewrowwillbeaddedtotheUITableView.

InItemsViewController.swift,implementaddNewItem(_:).@IBActionfuncaddNewItem(sender:AnyObject){//Makeanewindexpathforthe0thsection,lastrowletlastRow=tableView.numberOfRowsInSection(0)letindexPath=NSIndexPath(forRow:lastRow,inSection:0)

//InsertthisnewrowintothetabletableView.insertRowsAtIndexPaths([indexPath],withRowAnimation:.Automatic)}

Buildandruntheapplication.TaptheAddbuttonand…theapplicationcrashes.Theconsoletellsyouthatthetableviewhasaninternalinconsistencyexception.

Rememberthat,ultimately,itisthedataSourceoftheUITableViewthatdeterminesthenumberofrowsthetableviewshoulddisplay.Afterinsertinganewrow,thetableviewhassixrows(theoriginalfiveplusthenewone).WhentheUITableViewasksitsdataSourceforthenumberofrows,theItemsViewControllerconsultsthestoreandreturnsthatthereshouldbefiverows.TheUITableViewcannotresolvethisinconsistencyandthrowsanexception.

YoumustmakesurethattheUITableViewanditsdataSourceagreeonthenumberofrows.Thus,youmustaddanewItemtotheItemStorebeforeyouinsertthenewrow.

InItemsViewController.swift,updateaddNewItem(_:).@IBActionfuncaddNewItem(sender:AnyObject){//Makeanewindexpathforthe0thsection,lastrowletlastRow=tableView.numberOfRowsInSection(0)letindexPath=NSIndexPath(forRow:lastRow,inSection:0)

//InsertthisnewrowintothetabletableView.insertRowsAtIndexPaths([indexPath],withRowAnimation:.Automatic)

//CreateanewitemandaddittothestoreletnewItem=itemStore.createItem()

//Figureoutwherethatitemisinthearrayifletindex=itemStore.allItems.indexOf(newItem){letindexPath=NSIndexPath(forRow:index,inSection:0)

//InsertthisnewrowintothetabletableView.insertRowsAtIndexPaths([indexPath],withRowAnimation:.Automatic)}

WOW! eBook www.wowebook.org

Page 297: iOS Programming The Big Nerd Ranch Guide, 5th

}

Buildandruntheapplication.TaptheAddbutton,andthenewrowwillslideintothebottompositionofthetable.Rememberthattheroleofaviewobjectistopresentmodelobjectstotheuser;updatingviewswithoutupdatingthemodelobjectsisnotveryuseful.

Nowthatyouhavetheabilitytoaddrowsanditems,younolongerneedthecodethatputsfiverandomitemsintothestore.

OpenItemStore.swiftandremovetheinitializercode.init(){for_in0..<5{createItem()}}

Buildandruntheapplication.Therewillnolongerbeanyrowswhenyoufirstlaunchtheapplication,butyoucanaddsomebytappingtheAddbutton.

WOW! eBook www.wowebook.org

Page 298: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 299: iOS Programming The Big Nerd Ranch Guide, 5th

DeletingRowsIneditingmode,theredcircleswiththeminussign(showninFigure10.7)aredeletioncontrols,andtappingoneshoulddeletethatrow.However,atthispoint,youcannotactuallydeletetherow.(Tryitandsee.)Beforethetableviewwilldeletearow,itcallsamethodonitsdatasourceabouttheproposeddeletionandwaitsforconfirmation.

Whendeletingacell,youmustdotwothings:removetherowfromtheUITableViewandremovetheItemassociatedwithitfromtheItemStore.Topullthisoff,theItemStoremustknowhowtoremoveobjectsfromitself.

InItemStore.swift,implementanewmethodtoremoveaspecificitem.funcremoveItem(item:Item){ifletindex=allItems.indexOf(item){allItems.removeAtIndex(index)}}

NowyouwillimplementtableView(_:commitEditingStyle:forRowAtIndexPath:),amethodfromtheUITableViewDataSourceprotocol.(ThismethodiscalledontheItemsViewController.KeepinmindthatwhiletheItemStoreisthewherethedataiskept,theItemsViewControlleristhetableview’sdataSource.)

WhentableView(_:commitEditingStyle:forRowAtIndexPath:)iscalledonthedatasource,twoextraargumentsarepassedalongwithit.ThefirstistheUITableViewCellEditingStyle,which,inthiscase,is.Delete.TheotherargumentistheNSIndexPathoftherowinthetable.

InItemsViewController.swift,implementthismethodtohavetheItemStoreremovetherightobjectandconfirmtherowdeletionbycallingthemethoddeleteRowsAtIndexPaths(_:withRowAnimation:)onthetableview.overridefunctableView(tableView:UITableView,commitEditingStyleeditingStyle:UITableViewCellEditingStyle,forRowAtIndexPathindexPath:NSIndexPath){//Ifthetableviewisaskingtocommitadeletecommand...ifeditingStyle==.Delete{letitem=itemStore.allItems[indexPath.row]//RemovetheitemfromthestoreitemStore.removeItem(item)

//AlsoremovethatrowfromthetableviewwithananimationtableView.deleteRowsAtIndexPaths([indexPath],withRowAnimation:.Automatic)}}

Buildandrunyourapplication,createsomerows,andthendeletearow.Itwilldisappear.Noticethatswipe-to-deleteworksalso.

WOW! eBook www.wowebook.org

Page 300: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 301: iOS Programming The Big Nerd Ranch Guide, 5th

MovingRowsTochangetheorderofrowsinaUITableView,youwilluseanothermethodfromtheUITableViewDataSourceprotocol–tableView(_:moveRowAtIndexPath:toIndexPath:).

Todeletearow,youhadtocallthemethoddeleteRowsAtIndexPaths(_:withRowAnimation:)ontheUITableViewtoconfirmthedeletion.Movingarow,however,doesnotrequireconfirmation:ThetableviewmovestherowonitsownauthorityandreportsthemovetoitsdatasourcebycallingthemethodtableView(_:moveRowAtIndexPath:toIndexPath:).Youimplementthismethodtoupdateyourdatasourcetomatchtheneworder.

Butbeforeyoucanimplementthismethod,youneedtogivetheItemStoreamethodtochangetheorderofitemsinitsallItemsarray.

InItemStore.swift,implementthisnewmethod.funcmoveItemAtIndex(fromIndex:Int,toIndex:Int){iffromIndex==toIndex{return}

//GetreferencetoobjectbeingmovedsoyoucanreinsertitletmovedItem=allItems[fromIndex]

//RemoveitemfromarrayallItems.removeAtIndex(fromIndex)

//InsertiteminarrayatnewlocationallItems.insert(movedItem,atIndex:toIndex)}

InItemsViewController.swift,implementtableView(_:moveRowAtIndexPath:toIndexPath:)toupdatethestore.overridefunctableView(tableView:UITableView,moveRowAtIndexPathsourceIndexPath:NSIndexPath,toIndexPathdestinationIndexPath:NSIndexPath){//UpdatethemodelitemStore.moveItemAtIndex(sourceIndexPath.row,toIndex:destinationIndexPath.row)}

Buildandrunyourapplication.TapEditandcheckoutthenewreorderingcontrols(thethreehorizontallines)onthesideofeachrow.Touchandholdareorderingcontrolandmovetherowtoanewposition(Figure10.8).

WOW! eBook www.wowebook.org

Page 302: iOS Programming The Big Nerd Ranch Guide, 5th

Figure10.8Movingarow

NotethatsimplyimplementingtableView(_:moveRowAtIndexPath:toIndexPath:)causedthereorderingcontrolstoappear.TheUITableViewcanaskitsdatasourceatruntimewhetheritimplementstableView(_:moveRowAtIndexPath:toIndexPath:).Ifitdoes,thenthetableviewaddsthereorderingcontrolswheneverthetableviewenterseditingmode.

WOW! eBook www.wowebook.org

Page 303: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 304: iOS Programming The Big Nerd Ranch Guide, 5th

DisplayingUserAlertsInthissection,youaregoingtolearnaboutuseralertsandthedifferentwaysofconfiguringanddisplayingthem.Useralertscanprovideyourapplicationwithabetteruserexperience,soyouwillusethemfairlyoften.

Alertsareoftenusedtowarnusersthatanimportantactionisabouttohappenandperhapsgivethemtheopportunitytocancelthataction.Whenyouwanttodisplayanalert,youcreateaninstanceofUIAlertControllerwithapreferredstyle.ThetwoavailablestylesareUIAlertControllerStyle.ActionSheetandUIAlertControllerStyle.Alert(Figure10.9).The.ActionSheetstyleisusedtopresenttheuserwithalistofactionsfromwhichtochoose.The.Alerttypeisusedtodisplaycriticalinformationtorequiretheusertodecidehowtoproceed.Thedistinctionmayseemsubtle,butiftheusercanbackoutofadecisionoriftheactionisnotcritical,thenan.ActionSheetisprobablythebestchoice.

Figure10.9UIAlertControllerstyles

YouaregoingtouseaUIAlertControllertoconfirmthedeletionofitems.Youwillusethe.ActionSheetstylesincethepurposeofthealertistoconfirmorcancelapossiblydestructiveaction.

WOW! eBook www.wowebook.org

Page 305: iOS Programming The Big Nerd Ranch Guide, 5th

OpenItemsViewController.swiftandmodifytableView(_:commitEditingStyle:forRowAtIndexPath:)toasktheusertoconfirmorcancelthedeletionofanitem.overridefunctableView(tableView:UITableView,commitEditingStyleeditingStyle:UITableViewCellEditingStyle,forRowAtIndexPathindexPath:NSIndexPath){//Ifthetableviewisaskingtocommitadeletecommand...ifeditingStyle==.Delete{letitem=itemStore.allItems[indexPath.row]

lettitle="Delete\(item.name)?"letmessage="Areyousureyouwanttodeletethisitem?"

letac=UIAlertController(title:title,message:message,preferredStyle:.ActionSheet)

//RemovetheitemfromthestoreitemStore.removeItem(item)

//AlsoremovethatrowfromthetableviewwithananimationtableView.deleteRowsAtIndexPaths([indexPath],withRowAnimation:.Automatic)}}

Afterdeterminingthattheuserwantstodeleteanitem,youcreateaninstanceofUIAlertControllerwithanappropriatetitleandmessagedescribingwhatactionisabouttotakeplace.Also,youspecifythe.ActionSheetstyleforthealert.

TheactionsthattheusercanchoosefromwhenshownanalertareinstancesofUIAlertAction,andyoucanaddmultipleonesregardlessofthealert’sstyle.ActionsareaddedtotheUIAlertControllerusingtheaddAction(_:)method.

AddthenecessaryactionstotheactionsheetintableView(_:commitEditingStyle:forRowAtIndexPath:)....

letac=UIAlertController(title:title,message:message,preferredStyle:.ActionSheet)

letcancelAction=UIAlertAction(title:"Cancel",style:.Cancel,handler:nil)ac.addAction(cancelAction)

letdeleteAction=UIAlertAction(title:"Delete",style:.Destructive,handler:{(action)->Voidin//Removetheitemfromthestoreself.itemStore.removeItem(item)

//Alsoremovethatrowfromthetableviewwithananimationself.tableView.deleteRowsAtIndexPaths([indexPath],withRowAnimation:.Automatic)})ac.addAction(deleteAction)

...

Thefirstactionhasatitleof“Cancel”andiscreatedusingthe.Cancelstyle.The.Cancelstyleresultsintextinastandardbluefont.ThisactionwillallowtheusertobackoutofdeletinganItem.Thehandlerparameterallowsaclosuretobeexecutedwhenthatactionoccurs.Sincenootheractionisneeded,nilispassedastheargument.

Thesecondactionhasatitleof“Delete”andiscreatedusingthe.Destructivestyle.Sincedestructiveactionsshouldbeclearlymarkedandnoticed,the.Destructive

WOW! eBook www.wowebook.org

Page 306: iOS Programming The Big Nerd Ranch Guide, 5th

styleresultsinbrightredtext.Iftheuserselectsthisaction,thentheitemandthetableviewcellneedtoberemoved.Thisisalldonewithinthehandlerclosurethatispassedtotheaction’sinitializer.

Nowthattheactionshavebeenadded,thealertcontrollercanbedisplayedtotheuser.SinceUIAlertControllerisasubclassofUIViewController,youcanpresentittotheusermodally.Amodalviewcontrollertakesovertheentirescreenuntilithasfinisheditswork.

Topresentaviewcontrollermodally,youcallpresentViewController(_:animated:completion:)ontheviewcontrollerwhoseviewisonthescreen.Theviewcontrollertobepresentedispassedtoit,andthisviewcontroller’sviewtakesoverthescreen....

letdeleteAction=UIAlertAction(title:"Delete",style:.Destructive,handler:{(action)->Voidin//Removetheitemfromthestoreself.itemStore.removeItem(item)

//Alsoremovethatrowfromthetableviewwithananimationself.tableView.deleteRowsAtIndexPaths([indexPath],withRowAnimation:.Automatic)})ac.addAction(deleteAction)

//PresentthealertcontrollerpresentViewController(ac,animated:true,completion:nil)

...

Buildandruntheapplicationanddeleteanitem.Anactionsheetwillbepresentedforyoutoconfirmthedeletion(Figure10.10).

WOW! eBook www.wowebook.org

Page 307: iOS Programming The Big Nerd Ranch Guide, 5th

Figure10.10Deletinganitem

WOW! eBook www.wowebook.org

Page 308: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 309: iOS Programming The Big Nerd Ranch Guide, 5th

DesignPatternsAdesignpatternsolvesacommonsoftwareengineeringproblem.Designpatternsarenotactualsnippetsofcode,butinsteadareabstractideasorapproachesthatyoucanuseinyourapplications.Gooddesignpatternsarevaluableandpowerfultoolsforanydeveloper.

Theconsistentuseofdesignpatternsthroughoutthedevelopmentprocessreducesthementaloverheadinsolvingaproblemsoyoucancreatecomplexapplicationsmoreeasilyandrapidly.Herearesomeofthedesignpatternsthatyouhavealreadyused:

Delegation:Oneobjectdelegatescertainresponsibilitiestoanotherobject.YouuseddelegationwiththeUITextFieldtobeinformedwhenthecontentsofthetextfieldchange.

Datasource:Adatasourceissimilartoadelegate,butinsteadofreactingtoanotherobject,adatasourceisresponsibleforprovidingdatatoanotherobjectwhenrequested.Youusedthedatasourcepatternwithtableviews:Eachtableviewhasadatasourcethatisresponsiblefor,ataminimum,tellingthetableviewhowmanyrowstodisplayandwhichcellitshoulddisplayateachindexpath.

Model-View-Controller:Eachobjectinyourapplicationsfulfillsoneofthreeroles.Modelobjectsarethedata.Viewsdisplaytheuserinterface.Controllersprovidethegluethattiesthemodelsandviewstogether.

Target-actionpairs:Oneobjectcallsamethodonanotherobjectwhenaspecificeventoccurs.Thetargetistheobjectthathasamethodcalledonit,andtheactionisthemethodbeingcalled.Forexample,youusedtarget-actionpairswithbuttons:whenatoucheventoccurs,amethodwillbecalledonanotherobject(oftenaviewcontroller).

Appleisveryconsistentinitsuseofthesedesignpatterns,andsoitisimportanttounderstandandrecognizethem.Keepaneyeoutforthesepatternsasyoucontinuethroughthisbook!Recognizingthemwillhelpyoulearnnewclassesandframeworksmuchmoreeasily.

WOW! eBook www.wowebook.org

Page 310: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 311: iOS Programming The Big Nerd Ranch Guide, 5th

BronzeChallenge:RenamingtheDeleteButtonWhendeletingarow,aconfirmationbuttonappearslabeledDelete.ChangethelabelofthisbuttontoRemove.

WOW! eBook www.wowebook.org

Page 312: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 313: iOS Programming The Big Nerd Ranch Guide, 5th

SilverChallenge:PreventingReorderingMakeitsothetableviewalwaysshowsafinalrowthatsaysNomoreitems!.(Thispartofthechallengeisthesameasachallengefromthelastchapter.Ifyouhavealreadydoneit,youcancopyyourcodefrombefore.)Now,makeitsothatthefinalrowcannotbemoved.

WOW! eBook www.wowebook.org

Page 314: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 315: iOS Programming The Big Nerd Ranch Guide, 5th

GoldChallenge:ReallyPreventingReorderingAftercompletingthesilverchallenge,youmaynoticethateventhoughyoucannotmovetheNomoreitems!rowitself,youcanstilldragotherrowsunderneathit.Makeitsothatnomatterwhat,theNomoreitems!rowcanneverbeknockedoutofthelastposition.Finally,makeitundeletable.

WOW! eBook www.wowebook.org

Page 316: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 317: iOS Programming The Big Nerd Ranch Guide, 5th

11SubclassingUITableViewCell

AUITableViewdisplaysalistofUITableViewCellobjects.Formanyapplications,thebasiccellwithitstextLabel,detailTextLabel,andimageViewissufficient.However,whenyouneedacellwithmoredetailoradifferentlayout,yousubclassUITableViewCell.

Inthischapter,youwillcreateasubclassofUITableViewCellnamedItemCellthatwilldisplayIteminstancesmoreeffectively.EachofthesecellswillshowanItem’sname,itsvalueindollars,anditsserialnumber(Figure11.1).

Figure11.1Homepwnerwithsubclassedtableviewcells

YoucustomizetheappearanceofUITableViewCellsubclassesbyaddingsubviewstoitscontentView.AddingsubviewstothecontentViewinsteadofdirectlytothecellitselfisimportantbecausethecellwillresizeitscontentViewatcertaintimes.Forexample,whenatableviewenterseditingmode,thecontentViewresizesitselftomakeroomfortheeditingcontrols(Figure11.2).IfyouaddedsubviewsdirectlytotheUITableViewCell,theseeditingcontrolswouldobscurethesubviews.Thecellcannotadjustitssizewhenenteringeditmode(itmustremainthewidthofthetableview),butthecontentViewcanresize,anditdoes.

WOW! eBook www.wowebook.org

Page 318: iOS Programming The Big Nerd Ranch Guide, 5th

Figure11.2Tableviewcelllayoutinstandardandeditingmode

WOW! eBook www.wowebook.org

Page 319: iOS Programming The Big Nerd Ranch Guide, 5th

CreatingItemCellCreateanewSwiftfilenamedItemCell.InItemCell.swift,defineItemCellasaUITableViewCellsubclass.importFoundationimportUIKit

classItemCell:UITableViewCell{

}

TheeasiestwaytoconfigureaUITableViewCellsubclassisthroughastoryboard.InChapter9,yousawthatstoryboardsfortableviewcontrollershaveaPrototypeCellssection.ThisiswhereyouwilllayoutthecontentfortheItemCell.

OpenMain.storyboardandselecttheUITableViewCellinthedocumentoutline.Openitsattributesinspector,changetheStyletoCustom,andchangetheIdentifiertoItemCell.

Nowopenitsidentityinspector(the tab).IntheClassfield,enterItemCell(Figure11.3).

Figure11.3Changingthecellclass

Changetheheightoftheprototypecelltobeabout65pointstall.YoucanchangeiteitheronthecanvasorbyselectingthetableviewcellandchangingtheRowHeightfromitssizeinspector.

AnItemCellwilldisplaythreetextelements,sodragthreeUILabelobjectsontothecell.ConfigurethemasshowninFigure11.4.Makethetextofthebottomlabelaslightlysmallerfontinalightshadeofgray.

Figure11.4ItemCell’slayout

Addconstraintstothesethreelabelsasfollows.

1. Selectthetop-leftlabelandopentheAutoLayoutPinmenu.SelectthetopandleftstrutandthenclickAdd2Constraints.

WOW! eBook www.wowebook.org

Page 320: iOS Programming The Big Nerd Ranch Guide, 5th

2. Youwantthebottom-leftlabeltoalwaysbealignedwiththetop-leftlabel.Control-dragfromthebottom-leftlabeltothetop-leftlabelandselectLeading.

3. Withthebottom-leftlabelstillselected,openthePinmenu,selectthebottomstrut,andthenclickAdd1Constraint.

4. SelecttherightlabelandControl-dragfromthislabeltoitssuperviewonitsrightside.SelectbothTrailingSpacetoContainerMarginandCenterVerticallyinContainer.

5. Selectthebottom-leftlabelandopenitssizeinspector.FindtheVerticalContentHuggingPriorityandloweritto250.LowertheVerticalContentCompressionResistancePriorityto749.YouwilllearnwhattheseAutoLayoutpropertiesdoinChapter12.

6. Yourframesmightbemisplaced,soopentheResolveAutoLayoutIssuesmenuandupdatetheframesforthethreelabels.

WOW! eBook www.wowebook.org

Page 321: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 322: iOS Programming The Big Nerd Ranch Guide, 5th

ExposingthePropertiesofItemCellForItemsViewControllertoconfigurethecontentofanItemCellintableView(_:cellForRowAtIndexPath:),thecellmusthavepropertiesthatexposethethreelabels.ThesepropertieswillbesetthroughoutletconnectionsinMain.storyboard.

Thenextstep,then,istocreateandconnectoutletsonItemCellforeachofitssubviews.

OpenItemCell.swiftandaddthreepropertiesfortheoutlets.importUIKit

classItemCell:UITableViewCell{

@IBOutletvarnameLabel:UILabel!@IBOutletvarserialNumberLabel:UILabel!@IBOutletvarvalueLabel:UILabel!

}

YouaregoingtoconnecttheoutletsforthethreeviewstotheItemCell.Whenconnectingoutletsearlierinthebook,youControl-draggedfromviewcontrollerinthestoryboardtotheappropriateview.ButtheoutletsforItemCellarenotoutletsonacontroller.Theyareoutletsonaview:thecustomUITableViewCellsubclass.

Therefore,toconnecttheoutletsforItemCell,youwillconnectthemtotheItemCell.

OpenMain.storyboard.Control-clickontheItemViewCellinthedocumentoutlineandmakethethreeoutletconnectionsshowninFigure11.5.

Figure11.5Connectingtheoutlets

WOW! eBook www.wowebook.org

Page 323: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 324: iOS Programming The Big Nerd Ranch Guide, 5th

UsingItemCellLet’sgetyourcustomcellsonscreen.InItemsViewController’stableView(_:cellForRowAtIndexPath:)method,youwilldequeueaninstanceofItemCellforeveryrowinthetable.

NowthatyouareusingacustomUITableViewCellsubclass,thetableviewneedstoknowhowtalleachrowis.Thereareafewwaystoaccomplishthis,butthesimplestwayistosettherowHeightpropertyofthetableviewtoaconstantvalue.Youwillseeanotherwaylaterinthischapter.

OpenItemsViewController.swiftandupdateviewDidLoad()tosettheheightofthetableviewcells.overridefuncviewDidLoad(){super.viewDidLoad()

//GettheheightofthestatusbarletstatusBarHeight=UIApplication.sharedApplication().statusBarFrame.height

letinsets=UIEdgeInsets(top:statusBarHeight,left:0,bottom:0,right:0)tableView.contentInset=insetstableView.scrollIndicatorInsets=insets

tableView.rowHeight=65}

NowthatyouhaveregisteredtheItemCellwiththetableview(usingtheprototypecellsinthestoryboard),youcanaskthetableviewtodequeueacellwiththeidentifier“ItemCell.”

InItemsViewController.swift,modifytableView(_:cellForRowAtIndexPath:).overridefunctableView(tableView:UITableView,cellForRowAtIndexPathindexPath:NSIndexPath)->UITableViewCell{//Getaneworrecycledcellletcell=tableView.dequeueReusableCellWithIdentifier("UITableViewCell",forIndexPath:indexPath)

letcell=tableView.dequeueReusableCellWithIdentifier("ItemCell",forIndexPath:indexPath)as!ItemCell

//Setthetextonthecellwiththedescriptionoftheitem//thatisatthenthindexofitems,wheren=rowthiscell//willappearinonthetableviewletitem=itemStore.allItems[indexPath.row]

cell.textLabel?.text=item.namecell.detailTextLabel?.text="$\(item.valueInDollars)"

//ConfigurethecellwiththeItemcell.nameLabel.text=item.namecell.serialNumberLabel.text=item.serialNumbercell.valueLabel.text="$\(item.valueInDollars)"

returncell}

First,thereuseidentifierisupdatedtoreflectyournewsubclass.Thecodeattheendofthismethodisfairlyobvious–foreachlabelonthecell,setitstexttosomepropertyfromtheappropriateItem.

WOW! eBook www.wowebook.org

Page 325: iOS Programming The Big Nerd Ranch Guide, 5th

Buildandruntheapplication.ThenewcellsnowloadwiththeirlabelspopulatedwiththevaluesfromeachItem.

WOW! eBook www.wowebook.org

Page 326: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 327: iOS Programming The Big Nerd Ranch Guide, 5th

DynamicCellHeightsCurrently,thecellshaveafixedheightof65points.Itismuchbettertoallowthecontentofthecelltodriveitsheight.Thatway,ifthecontenteverchanges,thetableviewcell’sheightcanchangeautomatically.

Youcanachievethisgoal,asyouhaveprobablyguessed,withAutoLayout.TheUITableViewCellneedstohaveverticalconstraintsthatwillexactlydeterminetheheightofthecell.Currently,ItemCelldoesnothavesufficientconstraintsforthis.Youneedtoaddaconstraintbetweenthetwoleftlabelsthatfixestheverticalspacingbetweenthem.

OpenMain.storyboard.Control-dragfromthenameLabeltotheserialNumberLabelandselectVerticalSpacing.

NowopenItemsViewController.swiftandupdateviewDidLoad(_:)totellthetableviewthatitshouldcomputethecellheightsbasedontheconstraints.overridefuncviewDidLoad(){super.viewDidLoad()

//GettheheightofthestatusbarletstatusBarHeight=UIApplication.sharedApplication().statusBarFrame.height

letinsets=UIEdgeInsets(top:statusBarHeight,left:0,bottom:0,right:0)tableView.contentInset=insetstableView.scrollIndicatorInsets=insets

tableView.rowHeight=65tableView.rowHeight=UITableViewAutomaticDimensiontableView.estimatedRowHeight=65}

UITableViewAutomaticDimensionisthedefaultvalueforrowHeight,sowhileitisnotnecessarytoadd,itisusefulforunderstandingwhatisgoingon.SettingtheestimatedRowHeightpropertyonthetableviewcanimproveperformance.Insteadofaskingeachcellforitsheightwhenthetableviewloads,settingthispropertyallowssomeofthatperformancecosttobedeferreduntiltheuserstartsscrolling.

Buildandruntheapplication.Theapplicationwilllookthesameasitdidbefore.Inthenextsection,youwilllearnaboutatechnologycalledDynamicTypethatwilltakeadvantageoftheautomaticallyresizingtableviewcells.

WOW! eBook www.wowebook.org

Page 328: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 329: iOS Programming The Big Nerd Ranch Guide, 5th

DynamicTypeCreatinganinterfacethatappealstoeveryonecanbedaunting.Somepeopleprefermorecompactinterfacessotheycanseemoreinformationatatime.Othersmightwanttobeabletoeasilyseeinformationataglance,orperhapstheyhavepooreyesight.Inshort:peoplehavedifferentneeds.Gooddevelopersstrivetomakeappsthatmeetthoseneeds.

DynamicTypeisatechnologythathelpsrealizethisgoalbyprovidingspecificallydesignedtextstylesthatareoptimizedforlegibility.UserscanselectoneofsevenpreferredtextsizesfromwithinApple’sSettingsapplication(plusafewadditionallargersizesfromwithintheAccessibilitysection),andappsthatsupportDynamicTypewillhavetheirfontsscaledappropriately.Inthissection,youwillupdateItemCelltosupportDynamicType.Figure11.6showstheapplicationrenderedatthesmallestandlargestuser-selectableDynamicTypesizes.

Figure11.6ItemCellwithDynamicTypesupported

TheDynamicTypesystemiscenteredaroundtextstyles.Whenafontisrequestedforagiventextstyle,thesystemwillconsidertheuser’spreferredtextsizeinassociationwiththetextstyletoreturnanappropriatelyconfiguredfont.Figure11.7showsthesixdifferenttextstyles.

WOW! eBook www.wowebook.org

Page 330: iOS Programming The Big Nerd Ranch Guide, 5th

Figure11.7Differenttextstyles

OpenMain.storyboard.Let’supdatethelabelstousethetextstylesinsteadoffixedfonts.SelectthenameLabelandvalueLabelandopentheattributesinspector.ClickonthetexticontotherightofFont.ForFont,chooseTextStyles-Body(Figure11.8).RepeatthesamestepsfortheserialNumberLabel,choosingtheCaption1textstyle.

Figure11.8Changingthetextstyle

Nowlet’schangethepreferredfontsize.YoudothisthroughtheSettingsapplication.

Buildandruntheapplication.PresstheHomebutton(oruseHomefromtheHardware

WOW! eBook www.wowebook.org

Page 331: iOS Programming The Big Nerd Ranch Guide, 5th

menu)andopenApple’sSettingsapplication.UnderGeneral,selectAccessibilityandthenLargerText.(Onanactualdevice,thismenuisaccessedinSettingsunderDisplay&BrightnessandthenTextSize.)Dragthesliderallthewaytothelefttosetthefontsizetothesmallestvalue(Figure11.9).PresstheHomebuttonagaintosavethesechanges.

Figure11.9Textsizesettings

Buildandruntheapplication.(Ifyouswitchbacktotheapplication,eitherusingthetaskswitcherorthroughtheHomescreen,youwillnotseethechanges.Youwillfixthatinthenextsection.)Addsomeitemstothetableviewandyouwillseethenewsmallerfontsizesinaction.

Respondingtouserchanges

Whentheuserchangesthepreferredtextsizeandreturnstotheapplication,thetableviewwillgetreloaded.Unfortunately,thelabelswillnotknowaboutthenewpreferredtext

WOW! eBook www.wowebook.org

Page 332: iOS Programming The Big Nerd Ranch Guide, 5th

size.Tofixthis,youneedtoupdatethelabelsmanually.

OpenItemCell.swiftandaddanewmethodthatupdatesallthreelabels.funcupdateLabels(){letbodyFont=UIFont.preferredFontForTextStyle(UIFontTextStyleBody)nameLabel.font=bodyFontvalueLabel.font=bodyFont

letcaption1Font=UIFont.preferredFontForTextStyle(UIFontTextStyleCaption1)serialNumberLabel.font=caption1Font}

NowopenItemsViewController.swiftandcallthismethodintableView(_:cellForRowAtIndexPath:).overridefunctableView(tableView:UITableView,cellForRowAtIndexPathindexPath:NSIndexPath)->UITableViewCell{//Getaneworrecycledcellletcell=tableView.dequeueReusableCellWithIdentifier("UITableViewCell",forIndexPath:indexPath)as!ItemCell

//Updatethelabelsforthenewpreferredtextsizecell.updateLabels()

letitem=itemStore.allItems[indexPath.row]

cell.nameLabel.text=item.namecell.serialNumberLabel.text=item.serialNumbercell.valueLabel.text="$\(item.valueInDollars)"

returncell}

Buildandruntheapplication.GointoSettingsandchangethepreferredreadingsizetothelargestsize.Unlikebefore,youcannowswitchbacktoHomepwner,eitherbyopeningthetaskswitcherorthroughtheHomescreen,andthetableviewwillupdatetoreflectthenewpreferredtextsize.

WOW! eBook www.wowebook.org

Page 333: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 334: iOS Programming The Big Nerd Ranch Guide, 5th

BronzeChallenge:CellColorsUpdatetheItemCelltodisplaythevalueInDollarsingreenifthevalueislessthan50andredifthevalueisgreaterthanorequalto50.

WOW! eBook www.wowebook.org

Page 335: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 336: iOS Programming The Big Nerd Ranch Guide, 5th

12StackViews

YouhavebeenusingAutoLayoutthroughoutthisbooktocreateflexibleinterfacesthatscaleacrossdevicetypesandsizes.AutoLayoutisaverypowerfultechnology,butwiththatpowercomescomplexity.Layingoutaninterfacewelloftenneedsalotofconstraints,anditcanbedifficulttocreatedynamicinterfacesduetotheneedtoconstantlyaddandremoveconstraints.

Often,aninterface(orasubsectionoftheinterface)canbelaidoutinalinearfashion.Let’sthinkaboutsomeoftheotherapplicationsyouhavewritteninthisbook.TheQuizapplicationthatyouwroteinChapter1consistedoffoursubviewsthatwerelaidoutvertically.ThesameistruefortheWorldTrotterapplicationthatyouwrote:theConversionViewControllerhadaverticalinterfaceconsistingofatextfieldandafewlabels.

Interfacesthathavealinearlayoutaregreatcandidatesforusingastackview.AstackviewisaninstanceofUIStackViewthatallowsyoutocreateaverticalorhorizontallayoutthatiseasytolayoutandmanagesmostoftheconstraintsthatyouwouldtypicallyhavetomanageyourself.Perhapsbestofall,youareabletoneststackviewswithinotherstackviews,whichallowsyoutocreatetrulyamazinginterfacesinafractionofthetime.

Inthischapter,youaregoingtocontinueworkingonHomepwnertocreateaninterfacefordisplayingthedetailsofaspecificItem.Theinterfacethatyoucreatewillconsistofmultiplenestedstackviews,bothverticalandhorizontal(Figure12.1).

WOW! eBook www.wowebook.org

Page 337: iOS Programming The Big Nerd Ranch Guide, 5th

Figure12.1Homepwnerwithstackviews

WOW! eBook www.wowebook.org

Page 338: iOS Programming The Big Nerd Ranch Guide, 5th

UsingUIStackViewYouaregoingtocreateaninterfaceforeditingthedetailsforanItem.Youwillgetthebasicinterfaceworkinginthischapter,andthenyouwillfinishimplementingthedetailsinChapter13.

Atthetoplevel,youwillhaveaverticalstackviewwithfourelementsdisplayingtheitem’sname,serialnumber,value,anddatecreated(Figure12.2).

Figure12.2Verticalstackviewlayout

OpenyourHomepwnerprojectandthenopenMain.storyboard.DraganewViewControllerfromtheobjectlibraryontothecanvas.DragaVerticalStackViewfromtheobjectlibraryontotheviewfortheViewController.Addconstraintstothestackviewtopinittotheleadingandtrailingmargins,andpinthetopandbottomedgestobe8pointsfromthetopandbottomlayoutguides.

NowdragfourinstancesofUILabelfromtheobjectlibraryontothestackview.Fromtoptobottom,givetheselabelsthetext“Name,”“Serial,”“Value,”and“DateCreated.”(Figure12.3).

WOW! eBook www.wowebook.org

Page 339: iOS Programming The Big Nerd Ranch Guide, 5th

Figure12.3Labelsaddedtothestackview

Youcanseeaproblemrightaway:thelabelsallhavearedborder(indicatinganAutoLayoutproblem)andthereisawarningthatsomeviewsareverticallyambiguous.Therearetwowaysyoucanfixthisissue:byusingAutoLayout,orbyusingapropertyonthestackview.Let’sworkthroughtheAutoLayoutsolutionfirstbecauseithighlightsanimportantaspectofAutoLayout.

Implicitconstraints

YoulearnedinChapter3thateveryviewhasanintrinsiccontentsize.Youalsolearnedthatifyoudonotspecifyconstraintsthatexplicitlydeterminethewidthorheight,theviewwillderiveitswidthorheightfromitsintrinsiccontentsize.Howdoesthiswork?

Itdoesthisusingimplicitconstraintsderivedfromaview’scontenthuggingprioritiesanditscontentcompressionresistancepriorities.Aviewhasoneoftheseprioritiesforeachaxis:

horizontalcontenthuggingpriority

WOW! eBook www.wowebook.org

Page 340: iOS Programming The Big Nerd Ranch Guide, 5th

verticalcontenthuggingpriority

horizontalcontentcompressionresistancepriority

verticalcontentcompressionresistancepriority

Contenthuggingpriorities

Thecontenthuggingpriorityislikearubberbandthatisplacedaroundaview.Therubberbandmakestheviewnotwanttobebiggerthanitsintrinsiccontentsizeinthatdimension.Eachpriorityisassociatedwithavaluefrom0to1000.Avalueof1000meansthataviewcannotgetbiggerthanitsintrinsiccontentsizeonthatdimension.

Let’slookatanexamplewithjustthehorizontaldimension.Sayyouhavetwolabelsnexttooneanotherwithconstraintsbothbetweenthetwoviewsandbetweeneachviewanditssuperview,asshowninFigure12.4.

Figure12.4Twolabelssidebyside

Thisworksgreatuntilthesuperviewbecomeswider.Atthatpoint,whichlabelshouldbecomewider?Thefirstlabel,thesecondlabel,orboth?AsFigure12.5shows,theinterfaceiscurrentlyambiguous.

Figure12.5Ambiguouslayout

Thisiswherethecontenthuggingprioritybecomesrelevant.Theviewwiththehighercontenthuggingpriorityistheonethatdoesnotstretch.Youcanthinkaboutthepriorityvalueasthe“strength”oftherubberband.Thehigherthepriorityvalue,thestrongerthe

WOW! eBook www.wowebook.org

Page 341: iOS Programming The Big Nerd Ranch Guide, 5th

rubberband,andthemoreitwantstohugtoitsintrinsiccontentsize.

Contentcompressionresistancepriorities

Thecontentcompressionresistanceprioritiesdeterminehowmuchaviewresistsgettingsmallerthanitsintrinsiccontentsize.ConsiderthesametwolabelsfromFigure12.4.Whatwouldhappenifthesuperview’swidthdecreased?Oneofthelabelswouldneedtotruncateitstext(Figure12.6).Butwhichone?

Figure12.6Compressedambiguouslayout

Theviewwiththegreatercontentcompressionresistancepriorityistheonethatwillresistcompressionand,therefore,nottruncateitstext.

Withthisknowledge,youcannowfixtheproblemwiththestackview.

SelecttheDateCreatedlabelandopenitssizeinspector.FindtheVerticalContentHuggingPriorityandloweritto249.Nowtheotherthreelabelshaveahighercontenthuggingpriority,sotheywillallhugtotheirintrinsiccontentheight.TheDateCreatedlabelwillstretchtofillintheremainingspace.

Stackviewdistribution

Let’stakealookatanotherwayofsolvingtheproblem.Stackviewshaveanumberofpropertiesthatdeterminehowtheircontentislaidout.

Selectthestackview,eitheronthecanvasorusingthedocumentoutline.OpenitsattributesinspectorandfindthesectionatthetoplabeledStackView.OneofthepropertiesthatdetermineshowthecontentislaidoutistheDistributionproperty.CurrentlyitissettoFill,whichletstheviewslayouttheircontentbasedontheirintrinsiccontentsize.ChangethevaluetoFillEqually.Thiswillresizethelabelssothattheyallhavethesameheight,ignoringtheintrinsiccontentsize(Figure12.7).Besuretoreadthedocumentationfortheotherdistributionvaluesthatastackviewcanhave.

WOW! eBook www.wowebook.org

Page 342: iOS Programming The Big Nerd Ranch Guide, 5th

Figure12.7Stackviewsettofillequally

ChangetheDistributionofthestackviewbacktoFill;thisisthevalueyouwillwantgoingforwardinthischapter.

Nestedstackviews

Oneofthemostpowerfulfeaturesofstackviewsisthattheycanbenestedwithinoneanother.Youwillusethistonesthorizontalstackviewswithinthelargerverticalstackview.ThetopthreelabelswillhaveatextfieldnexttothemthatdisplaysthecorrespondingvaluefortheItemandwillalsoallowtheusertoeditthatvalue.

SelecttheNamelabelonthecanvas.Clicktheleft-mosticonintheAutoLayoutconstraintsmenu: .Thiswillembedtheselectedviewinastackview.

Selectthenewstackviewandopenitsattributesinspector.Thestackviewiscurrentlyaverticalstackview,butyouwantittobeahorizontalstackview.ChangetheAxistoHorizontal.

NowdragaTextFieldfromtheobjectlibrarytotherightoftheNamelabel.Sincelabels,by

WOW! eBook www.wowebook.org

Page 343: iOS Programming The Big Nerd Ranch Guide, 5th

default,haveagreatercontenthuggingprioritythantextfields,thelabelhugstoitsintrinsiccontentwidthandthetextfieldstretches.Thelabelandthetextfieldcurrentlyhavethesamecontentcompressionresistancepriorities,whichwouldresultinanambiguouslayoutifthetextfield’stextwastoolong.OpenthesizeinspectorforthetextfieldandsetitsHorizontalContentCompressionResistancePriorityto749.

Stackviewspacing

Thelabelandtextfieldlookalittlesquishedsincethereisnospacingbetweenthem.Stackviewsallowyoutocustomizethespacingbetweenitems.

Selectthehorizontalstackviewandopenitsattributesinspector.ChangetheSpacingtobe8points.Noticethatthetextfieldshrinkstoaccommodatethespacing,becauseitislessresistanttocompressionthanthelabel.

RepeatthesesamestepsfortheSerialandValuelabels.

1. Selectthelabelandclickthe icon.

2. Changethestackviewtobeahorizontalstackview.

3. Dragatextfieldontothehorizontalstackviewandchangeitshorizontalcontentcompressionresistanceprioritytobe749.

4. Updatethestackviewtohaveaspacingof8points.

Thereareacoupleofothertweaksyouwillwanttomaketotheinterface.Theverticalstackviewneedssomespacing.TheDateCreatedlabelshouldhaveacentertextalignment.AndtheName,Serial,andValuelabelsshouldbethesamewidth.

Selecttheverticalstack,openitsattributesinspector,andupdatetheSpacingtobe8points.ThenselecttheDateCreatedlabel,openitsattributesinspector,andchangetheAlignmenttobecentered.Thatsolvesthefirsttwoissues.

Althoughstackviewssubstantiallyreducethenumberofconstraintsthatyouneedtoaddtoyourinterface,someconstraintsarestillimportant.Withtheinterfaceasis,thetextfieldsdonotalignontheirleadingedgeduetothedifferenceinthewidthsofthelabels.(ThedifferenceisnotverynoticeableinEnglish,butitbecomesmorepronouncedwhenlocalizedintootherlanguages.)Tosolvethis,youwilladdleadingedgeconstraintsbetweenthethreetextfields.

Control-dragfromtheNametextfieldtotheSerialtextfieldandselectLeading.ThendothesamefortheSerialtextfieldandtheValuetextfield.ThecompletedinterfacewilllooklikeFigure12.8.

WOW! eBook www.wowebook.org

Page 344: iOS Programming The Big Nerd Ranch Guide, 5th

Figure12.8Finalstackviewinterface

Stackviewsallowyoutocreateveryrichinterfacesinafractionofthetimeitwouldtaketoconfigurethemmanuallyusingconstraints.Constraintsarestilladded,buttheyarebeingmanagedbythestackviewitselfinsteadofbyyou.Stackviewsallowyoutohaveverydynamicinterfacesatruntime.YoucanaddandremoveviewsfromstackviewsbyusingaddArrangedSubview(_:),insertArrangedSubview(_:atIndex:),andremoveArrangedSubview(_:).Youcanalsotogglethehiddenpropertyonaviewinastackview.Thestackviewwillautomaticallylayoutitscontenttoreflectthatvalue.

WOW! eBook www.wowebook.org

Page 345: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 346: iOS Programming The Big Nerd Ranch Guide, 5th

SeguesMostiOSapplicationshaveanumberofviewcontrollersthatusersnavigatebetween.Storyboardsallowyoutosetuptheseinteractionsassegueswithouthavingtowritecode.

Aseguemovesanotherviewcontroller’sviewontothescreenandisrepresentedbyaninstanceofUIStoryboardSegue.Eachseguehasastyle,anactionitem,andanidentifier.Thestyleofaseguedetermineshowtheviewcontrollerwillbepresented.Theactionitemistheviewobjectinthestoryboardfilethattriggersthesegue,likeabutton,atableviewcell,orsomeotherUIControl.Theidentifierisusedtoprogrammaticallyaccessthesegue.Thisisusefulwhenyouwanttotriggeraseguethatdoesnotcomefromanactionitem,likeashakeorsomeotherinterfaceelementthatcannotbesetupinthestoryboardfile.

Let’sstartwithashowsegue.Ashowseguedisplaysaviewcontrollerdependingonthecontextinwhichitisdisplayed.Theseguewillbebetweenthetableviewcontrollerandthenewviewcontroller.Theactionitemswillbethetableview’scells;tappingacellwillshowtheviewcontrollermodally.

InMain.storyboard,selecttheItemCellprototypecellontheItemsViewController.Control-dragfromthecelltothenewviewcontrollerthatyousetupintheprevioussection.(MakesureyouareControl-draggingfromthecellandnotthetableview!)Ablackpanelwillappearthatliststhepossiblestylesforthissegue.SelectShowfromtheSelectionSeguesection(Figure12.9).

Figure12.9Settingupashowsegue

Noticethearrowthatgoesfromthetableviewcontrollertothenewviewcontroller.Thisisasegue.Theiconinthecircletellsyouthatthissegueisashowsegue–eachseguehas

WOW! eBook www.wowebook.org

Page 347: iOS Programming The Big Nerd Ranch Guide, 5th

auniqueicon.

Buildandruntheapplication.Tapacellandthenewviewcontrollerwillslideupfromthebottomofthescreen.(Slidingupfromthebottomisthedefaultbehaviorwhenpresentingaviewcontrollermodally.Thiscanbecustomized,however,asyousawinChapter10whenyoupresentedtheUIAlertControllermodally.)

Sofar,sogood!Buttherearetwoproblemsatthemoment:theviewcontrollerisnotdisplayingtheinformationfortheItemthatwasselected,andthereisnowaytodismisstheviewcontrollertoreturntotheItemsViewController.Youwillfixthefirstissueinthenextsection,andyouwillfixthesecondissueinChapter13.

WOW! eBook www.wowebook.org

Page 348: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 349: iOS Programming The Big Nerd Ranch Guide, 5th

HookingUptheContentTodisplaytheinformationfortheselectedItem,youwillneedtocreateanewUIViewControllersubclass.

CreateanewSwiftfileandnameitDetailViewController.OpenDetailViewController.swiftanddeclareanewUIViewControllersubclassnamedDetailViewController.importFoundationimportUIKit

classDetailViewController:UIViewController{

}

Becauseyouneedtobeabletoaccessthesesubviewsduringruntime,DetailViewControllerneedsoutletsforthem.TheplanistoaddfournewoutletstoDetailViewControllerandthenmaketheconnections.Inpreviousexercises,youdidthisintwodistinctsteps:first,youaddedtheoutletsintheSwiftfile,andsecond,youmadeconnectionsinthestoryboardfile.Youcandobothatonceusingtheassistanteditor.

WithDetailViewController.swiftopen,Option-clickonMain.storyboardintheprojectnavigator.ThiswillopenthefileintheassistanteditorrightnexttoDetailViewController.swift.(YoucantoggletheassistanteditorbyclickingthemiddlebuttonfromtheEditorcontrolatthetopoftheworkspace.TheshortcuttodisplaytheassistanteditorisCommand-Option-Return,andtheshortcuttoreturntothestandardeditorisCommand-Return.)

Yourwindowhasbecomealittlecluttered.Let’smakesometemporaryspace.HidethenavigatorareabyclickingtheleftbuttonintheViewcontrolatthetopoftheworkspace(theshortcutforthisisCommand-0).Then,hidethedocumentoutlineinInterfaceBuilderbyclickingthetogglebuttoninthelowerleftcorneroftheeditor.YourworkspaceshouldnowlooklikeFigure12.10.

WOW! eBook www.wowebook.org

Page 350: iOS Programming The Big Nerd Ranch Guide, 5th

Figure12.10Layingouttheworkspace

Beforeyouconnecttheoutlets,youneedtotellthedetailinterfacethatitshouldbeassociatedwiththeDetailViewController.SelecttheViewControlleronthecanvasandopenitsidentityinspector.ChangetheClasstobeDetailViewController(Figure12.11).

Figure12.11Settingtheviewcontrollerclass

ThethreeinstancesofUITextFieldandbottominstanceofUILabelwillbeoutletsinDetailViewController.Control-dragfromtheUITextFieldnexttotheNamelabeltothetopofDetailViewController.swift,asshowninFigure12.12.

WOW! eBook www.wowebook.org

Page 351: iOS Programming The Big Nerd Ranch Guide, 5th

Figure12.12Draggingfromstoryboardtosourcefile

Letgoandapop-upwindowwillappear.EnternameFieldintotheNamefield,selectStrongfromtheStoragepop-upmenu,andclickConnect(Figure12.13).

Figure12.13Autogeneratinganoutletandmakingaconnection

Thiswillcreatean@IBOutletpropertyoftypeUITextFieldnamednameFieldinDetailViewController.

Inaddition,thisUITextFieldisnowconnectedtothenameFieldoutletoftheDetailViewController.YoucanverifythisbyControl-clickingontheDetailViewControllertoseetheconnections.AlsonoticethathoveringyourmouseabovethenameFieldconnectioninthepanelthatappearswillrevealtheUITextFieldthatyouconnected.Twobirds,onestone.

CreatetheotherthreeoutletsthesamewayandnamethemasshowninFigure12.14.

WOW! eBook www.wowebook.org

Page 352: iOS Programming The Big Nerd Ranch Guide, 5th

Figure12.14Connectiondiagram

Aftermakingtheconnections,DetailViewController.swiftshouldlooklikethis:importUIKit

classDetailViewController:UIViewController{

@IBOutletvarnameField:UITextField!@IBOutletvarserialNumberField:UITextField!@IBOutletvarvalueField:UITextField!@IBOutletvardateLabel:UILabel!

}

Ifyourfilelooksdifferent,thenyouroutletsarenotconnectedcorrectly.Fixanydisparitiesbetweenyourfileandthecodeshownaboveinthreesteps:First,gothroughtheControl-dragprocessandmakeconnectionsagainuntilyouhavethefourlinesshownaboveinyourDetailViewController.swift.Second,removeanywrongcode(likenon-propertymethoddeclarationsorproperties)thatgotcreated.Finally,checkforanybadconnectionsinthestoryboardfile.InMain.storyboard,Control-clickontheDetailViewController.Ifthereareyellowwarningsignsnexttoanyconnection,clickthexiconnexttothoseconnectionstodisconnectthem.

Itisimportanttoensurethattherearenobadconnectionsinaninterfacefile.Abadconnectiontypicallyhappenswhenyouchangethenameofapropertybutdonotupdatetheconnectionintheinterfacefile.Or,youcompletelyremoveapropertybutdonotremoveitfromtheinterfacefile.Eitherway,abadconnectionwillcauseyourapplicationtocrashwhentheinterfacefileisloaded.

WOW! eBook www.wowebook.org

Page 353: iOS Programming The Big Nerd Ranch Guide, 5th

Withtheconnectionsmade,youcanclosetheassistanteditorandreturntoviewingjustDetailViewController.swift.

DetailViewControllerwillholdontoareferencetotheItemthatisbeingdisplayed.Whenitsviewisloaded,youwillsetthetextoneachtextfieldtotheappropriatevaluefromtheIteminstance.

InDetailViewController.swift,addapropertyforanIteminstanceandoverrideviewWillAppear(_:)tosetuptheinterface.classDetailViewController:UIViewController{

@IBOutletvarnameField:UITextField!@IBOutletvarserialNumberField:UITextField!@IBOutletvarvalueField:UITextField!@IBOutletvardateLabel:UILabel!

varitem:Item!

overridefuncviewWillAppear(animated:Bool){super.viewWillAppear(animated)

nameField.text=item.nameserialNumberField.text=item.serialNumbervalueField.text="\(item.valueInDollars)"dateLabel.text="\(item.dateCreated)"}}

InsteadofusingstringinterpolationtoprintoutthevalueInDollarsanddateCreated,itwouldbebettertouseaformatter.YouusedaninstanceofNSNumberFormatterinChapter4.Youwilluseanotheronehere,aswellasaninstanceofNSDateFormattertoformatthedateCreated.

AddaninstanceofNSNumberFormatterandNSDateFormattertotheDetailViewController.UsetheseformattersinviewWillAppear(_:)toformatthevalueInDollarsanddateCreated.varitem:Item!

letnumberFormatter:NSNumberFormatter={letformatter=NSNumberFormatter()formatter.numberStyle=.DecimalStyleformatter.minimumFractionDigits=2formatter.maximumFractionDigits=2returnformatter}()

letdateFormatter:NSDateFormatter={letformatter=NSDateFormatter()formatter.dateStyle=.MediumStyleformatter.timeStyle=.NoStylereturnformatter}()

overridefuncviewWillAppear(animated:Bool){super.viewWillAppear(animated)

nameField.text=item.nameserialNumberField.text=item.serialNumbervalueField.text="\(item.valueInDollars)"dateLabel.text="\(item.dateCreated)"valueField.text=numberFormatter.stringFromNumber(item.valueInDollars)dateLabel.text=dateFormatter.stringFromDate(item.dateCreated)}

WOW! eBook www.wowebook.org

Page 354: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 355: iOS Programming The Big Nerd Ranch Guide, 5th

PassingDataAroundWhenarowinthetableviewistapped,youneedawayoftellingtheDetailViewControllerwhichitemwasselected.Wheneverasegueistriggered,theprepareForSegue(_:sender:)methodiscalledontheviewcontrollerinitiatingthesegue.Thismethodhastwoarguments:theUIStoryboardSegue,whichgivesyouinformationaboutwhichsegueishappening,andthesender,whichistheobjectthattriggeredthesegue(aUITableViewCelloraUIButton,forexample).

TheUIStoryboardSeguegivesyouthreepiecesofinformation:thesourceviewcontroller(wherethesegueoriginates),thedestinationviewcontroller(wherethesegueends),andtheidentifierofthesegue.Theidentifierletsyoudifferentiatesegues.Let’sgivethesegueausefulidentifier.

OpenMain.storyboardagain.Selecttheshowseguebyclickingonthearrowbetweenthetwoviewcontrollersandopentheattributesinspector.Fortheidentifier,enterShowItem(Figure12.15).

Figure12.15Segueidentifier

Withyoursegueidentified,youcannowpassyourIteminstancesaround.OpenItemsViewController.swiftandimplementprepareForSegue(_:sender:).overridefuncprepareForSegue(segue:UIStoryboardSegue,sender:AnyObject?){//Ifthetriggeredsegueisthe"ShowItem"segueifsegue.identifier=="ShowItem"{

//Figureoutwhichrowwasjusttappedifletrow=tableView.indexPathForSelectedRow?.row{

//Gettheitemassociatedwiththisrowandpassitalongletitem=itemStore.allItems[row]letdetailViewController=segue.destinationViewControlleras!DetailViewControllerdetailViewController.item=item}}}

Buildandruntheapplication.TaponarowandtheDetailViewControllerwill

WOW! eBook www.wowebook.org

Page 356: iOS Programming The Big Nerd Ranch Guide, 5th

slideonscreen,displayingthedetailsforthatitem.YouwillfixtheinabilitytogobacktotheItemsViewControllerinChapter13.

ManyprogrammersnewtoiOSstrugglewithhowdataispassedbetweenviewcontrollers.HavingallofthedataintherootviewcontrollerandpassingsubsetsofthatdatatothenextUIViewController(likeyoudid)isacleanandefficientwayofperformingthistask.

WOW! eBook www.wowebook.org

Page 357: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 358: iOS Programming The Big Nerd Ranch Guide, 5th

BronzeChallenge:MoreStackViewsQuizandWorldTrotteraregoodcandidatesforusingstackviews.UpdatebothoftheseapplicationstouseUIStackView.

WOW! eBook www.wowebook.org

Page 359: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 360: iOS Programming The Big Nerd Ranch Guide, 5th

13UINavigationController

InChapter5,youlearnedaboutUITabBarControllerandhowitallowsausertoaccessdifferentscreens.Atabbarcontrollerisgreatforscreensthatareindependentofeachother,butwhatifyouhavescreensthatproviderelatedinformation?

Forexample,theSettingsapplicationhasmultiplerelatedscreensofinformation:alistofsettings(likeSounds),adetailedpageforeachsetting,andaselectionpageforeachdetail(Figure13.1).Thistypeofinterfaceiscalledadrill-downinterface.

Figure13.1Drill-downinterfaceinSettings

Inthischapter,youwilluseaUINavigationControllertoaddadrill-downinterfacetoHomepwnerthatletstheuserseeandeditthedetailsofanItem.ThesedetailswillbepresentedbytheDetailViewControllerthatyoucreatedinChapter12(Figure13.2).

WOW! eBook www.wowebook.org

Page 361: iOS Programming The Big Nerd Ranch Guide, 5th

Figure13.2HomepwnerwithUINavigationController

WOW! eBook www.wowebook.org

Page 362: iOS Programming The Big Nerd Ranch Guide, 5th

UINavigationControllerAUINavigationControllermaintainsanarrayofviewcontrollerspresentingrelatedinformationinastack.WhenaUIViewControllerisontopofthestack,itsviewisvisible.

WhenyouinitializeaninstanceofUINavigationController,yougiveitaUIViewController.ThisUIViewControllerisaddedtothenavigationcontroller’sviewControllersarrayandbecomesthenavigationcontroller’srootviewcontroller.Therootviewcontrollerisalwaysonthebottomofthestack.(Notethatwhilethisviewcontrollerisreferredtoasthenavigationcontroller’s“rootviewcontroller,”UINavigationControllerdoesnothavearootViewControllerproperty.)

MoreviewcontrollerscanbepushedontopoftheUINavigationController’sstackwhiletheapplicationisrunning.TheseviewcontrollersareaddedtotheendoftheviewControllersarraythatcorrespondstothetopofthestack.UINavigationController’stopViewControllerpropertykeepsareferencetotheviewcontrolleratthetopofthestack.

Whenaviewcontrollerispushedontothestack,itsviewslidesontothescreenfromtheright.Whenthestackispopped(i.e.,thelastitemisremoved),thetopviewcontrollerisremovedfromthestackanditsviewslidesofftotheright,exposingtheviewofnextviewcontrolleronthestack,whichbecomesthetopviewcontroller.Figure13.3showsanavigationcontrollerwithtwoviewcontrollers.TheviewofthetopViewControlleriswhattheusersees.

Figure13.3UINavigationController’sstack

WOW! eBook www.wowebook.org

Page 363: iOS Programming The Big Nerd Ranch Guide, 5th

UINavigationControllerisasubclassofUIViewController,soithasaviewofitsown.Itsviewalwayshastwosubviews:aUINavigationBarandtheviewoftopViewController(Figure13.4).

Figure13.4AUINavigationController’sview

Inthischapter,youwilladdaUINavigationControllertotheHomepwnerapplicationandmaketheItemsViewControllertheUINavigationController’srootviewcontroller.TheDetailViewControllerwillbepushedontotheUINavigationController’sstackwhenanItemisselected.ThisviewcontrollerwillallowtheusertoviewandeditthepropertiesofanItemselectedfromthetableviewofItemsViewController.TheobjectdiagramfortheupdatedHomepwnerapplicationisshowninFigure13.5.

WOW! eBook www.wowebook.org

Page 364: iOS Programming The Big Nerd Ranch Guide, 5th

Figure13.5Homepwnerobjectdiagram

Thisapplicationisgettingfairlylarge,asyoucansee.Fortunately,viewcontrollersandUINavigationControllerknowhowtodealwiththistypeofcomplicatedobjectdiagram.WhenwritingiOSapplications,itisimportanttotreateachUIViewControllerasitsownlittleworld.ThestuffthathasalreadybeenimplementedinCocoaTouchwilldotheheavylifting.

BeginbygivingHomepwneranavigationcontroller.ReopentheHomepwnerproject.TheonlyrequirementsforusingaUINavigationControllerarethatyougiveitarootviewcontrollerandadditsviewtothewindow.

OpenMain.storyboardandselecttheItemsViewController.Then,fromtheEditormenu,chooseEmbedIn→NavigationController.ThiswillsettheItemsViewControllertobetherootviewcontrollerofaUINavigationController.ItwillalsoupdatethestoryboardtosettheNavigationControllerastheinitialviewcontroller.

SelecttheDetailViewController,andyouwillseethatitsinterfacehasmisplacedviewsnow.Thestackviewhasaconstrainttothetoplayoutguide,butwhenaviewcontrollerisembeddedwithinanavigationcontroller,thebottomofthetoplayoutguidecorrespondstothebottomofthenavigationbar(notthebottomofthestatusbar).WiththeDetailView

WOW! eBook www.wowebook.org

Page 365: iOS Programming The Big Nerd Ranch Guide, 5th

Controllerselected,opentheResolveAutoLayoutIssuespop-up.SelectUpdateFramesfromthesectionAllViewsinDetailViewControllerandthestackviewwillrepositionitself.

Buildandruntheapplicationand…theapplicationcrashes.Whatishappening?YoupreviouslycreatedacontractwiththeAppDelegatethataninstanceofItemsViewControllerwouldbetherootViewControllerofthewindow:letitemsController=window!.rootViewControlleras!ItemsViewController

YouhavenowbrokenthiscontractbyembeddingtheItemsViewControllerwithinaUINavigationController.Youneedtoupdatethecontract.

OpenAppDelegate.swiftandupdateapplication(_:didFinishLaunchingWithOptions:)toreflectthenewviewcontrollerhierarchy.funcapplication(application:UIApplication,didFinishLaunchingWithOptionslaunchOptions:[NSObject:AnyObject]?)->Bool{//Overridepointforcustomizationafterapplicationlaunch

letitemStore=ItemStore()

//AccesstheItemsViewControllerandsetitsitemstoreletitemsController=window!.rootViewControlleras!ItemsViewControllerletnavController=window!.rootViewControlleras!UINavigationControllerletitemsController=navController.topViewControlleras!ItemsViewControlleritemsController.itemStore=itemStore

returntrue}

Buildandruntheapplicationagain.Homepwnerworksagainandhasaverynice,iftotallyempty,UINavigationBaratthetopofthescreen(Figure13.6).NoticehowthescreenadjustedtofitItemsViewController’sviewaswellasthenewnavigationbar.UINavigationControllerdidthisforyou.

Figure13.6Homepwnerwithanemptynavigationbar

WOW! eBook www.wowebook.org

Page 366: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 367: iOS Programming The Big Nerd Ranch Guide, 5th

NavigatingwithUINavigationControllerWiththeapplicationstillrunning,createanewitemandselectthatrowfromtheUITableView.NotonlyareyoutakentoDetailViewController’sview,butyoualsogetafreeanimationandaBackbuttonintheUINavigationBar.TapthisbuttontogetbacktoItemsViewController.

NoticethatyoudidnothavetochangetheshowseguethatyoucreatedinChapter12togetthisbehavior.Asmentionedinthatchapter,theshowseguepresentsthedestinationviewcontrollerinawaythatmakessensegiventhesurroundingcontext.Whenashowsegueistriggeredfromaviewcontrollerembeddedwithinanavigationcontroller,thedestinationviewcontrollerispushedontothenavigationcontroller’sviewcontrollerstack.

SincetheUINavigationController’sstackisanarray,itwilltakeownershipofanyviewcontrolleraddedtoit.Thus,theDetailViewControllerisownedonlybytheUINavigationControlleraftertheseguefinishes.Whenthestackispopped,theDetailViewControllerisdestroyed.Thenexttimearowistapped,anewinstanceofDetailViewControlleriscreated.

Havingaviewcontrollerpushthenextviewcontrollerisacommonpattern.Therootviewcontrollertypicallycreatesthenextviewcontroller,andthenextviewcontrollercreatestheoneafterthat,andsoon.Someapplicationsmayhaveviewcontrollersthatcanpushdifferentviewcontrollersdependingonuserinput.Forexample,thePhotosapppushesavideoviewcontrolleroranimageviewcontrollerontothenavigationstackdependingonwhattypeofmediaisselected.

Buildandrunyourapplication.CreateanewitemandselectthatrowintheUITableView.TheviewthatappearswillcontaintheinformationfortheselectedItem.Whileyoucaneditthisdata,theUITableViewwillnotreflectthosechangeswhenyoureturntoit.Tofixthisproblem,youneedtoimplementcodetoupdatethepropertiesoftheItembeingedited.Inthenextsection,youwillseewhentodothis.

WOW! eBook www.wowebook.org

Page 368: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 369: iOS Programming The Big Nerd Ranch Guide, 5th

AppearingandDisappearingViewsWheneveraUINavigationControllerisabouttoswapviews,itcallstwomethods:viewWillDisappear(_:)andviewWillAppear(_:).TheUIViewControllerthatisabouttobepoppedoffthestackhasviewWillDisappear(_:)called.TheUIViewControllerthatwillthenbeontopofthestackhasviewWillAppear(_:)calledonit.

Toholdontochangesinthedata,whenaDetailViewControllerispoppedoffthestackyouwillsetthepropertiesofitsitemtothecontentsofthetextfields.Whenimplementingthesemethodsforviewsappearinganddisappearing,itisimportanttocallthesuperclass’simplementation–itmighthavesomeworktodoandneedstobegiventhechancetodoit.InDetailViewController.swift,implementviewWillDisappear(_:).overridefuncviewWillDisappear(animated:Bool){super.viewWillDisappear(animated)

//"Save"changestoitemitem.name=nameField.text??""item.serialNumber=serialNumberField.text

ifletvalueText=valueField.text,value=numberFormatter.numberFromString(valueText){item.valueInDollars=value.integerValue}else{item.valueInDollars=0}}

NowthevaluesoftheItemwillbeupdatedwhentheusertapstheBackbuttonontheUINavigationBar.WhenItemsViewControllerappearsbackonthescreen,themethodviewWillAppear(_:)iscalled.TakethisopportunitytoreloadtheUITableViewsotheusercanimmediatelyseethechanges.

InItemsViewController.swift,overrideviewWillAppear(_:)toreloadthetableview.overridefuncviewWillAppear(animated:Bool){super.viewWillAppear(animated)

tableView.reloadData()}

Buildandrunyourapplicationonceagain.Nowyoucanmovebackandforthbetweentheviewcontrollersthatyoucreatedandchangethedatawithease.

WOW! eBook www.wowebook.org

Page 370: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 371: iOS Programming The Big Nerd Ranch Guide, 5th

DismissingtheKeyboardRuntheapplication,addanitem,andtouchthetextfieldwiththeitem’sname.Whenyoutouchthetextfield,akeyboardappearsonscreen,asyousawinyourWorldTrotterappinChapter4(Figure13.7).(Ifyouareusingthesimulatorandthekeyboarddoesnotappear,rememberthatyoucanpressCommand-Ktotogglethedevicekeyboard.)

Figure13.7Keyboardappearswhenatextfieldistouched

TheappearanceofthekeyboardinresponsetoatouchisbuiltintotheUITextFieldclassaswellasUITextView,soyoudonothavetodoanythingextraforthekeyboardtoappear.However,attimesyouwillwanttomakesurethekeyboardbehavesasyouwantitto.

Forexample,thekeyboardcoversmorethanathirdofthescreen.Rightnow,itdoesnotobscureanything,butsoonyouwilladdmoredetailsthatextendtothebottomofthe

WOW! eBook www.wowebook.org

Page 372: iOS Programming The Big Nerd Ranch Guide, 5th

screen,anduserswillwantawaytohidethekeyboardwhenitisnotneeded.Inthissection,youaregoingtogivetheusertwowaystodismissthekeyboard:pressingthekeyboard’sReturnkeyandtappinganywhereelseonthedetailviewcontroller’sview.Butfirst,let’slookatthecombinationofeventsthatmaketexteditingpossible.

Eventhandlingbasics

Whenyoutouchaview,aneventiscreated.Thisevent(knownasa“touchevent”)istiedtoaspecificlocationintheviewcontroller’sview.Thatlocationdetermineswhichviewinthehierarchythetoucheventisdeliveredto.

Forexample,whenyoutapaUIButtonwithinitsbounds,itwillreceivethetoucheventandrespondinbutton-likefashion–bycallingtheactionmethodonitstarget.Itisperfectlyreasonabletoexpectthatwhenaviewinyourapplicationistouched,thatviewreceivesatouchevent,anditmaychoosetoreacttothateventorignoreit.However,viewsinyourapplicationcanalsorespondtoeventswithoutbeingtouched.Agoodexampleofthisisashake.Ifyoushakethedevicewithyourapplicationrunning,oneofyourviewsonthescreencanrespond.Butwhichone?Anotherinterestingcaseisrespondingtothekeyboard.DetailViewController’sviewcontainsthreeUITextFields.Whichonewillreceivethetextwhentheusertypes?

Forboththeshakeandkeyboardevents,thereisnoeventlocationwithinyourviewhierarchytodeterminewhichviewwillreceivetheevent,soanothermechanismmustbeused.Thismechanismisthefirstresponderstatus.Manyviewsandcontrolscanbeafirstresponderwithinyourviewhierarchy–butonlyoneatatime.Thinkofitasaflagthatcanbepassedamongviews.Whicheverviewholdstheflagwillreceivetheshakeorkeyboardevent.

InstancesofUITextFieldandUITextViewhaveanuncommonresponsetotouchevents.Whentouched,atextfieldoratextviewbecomesthefirstresponder,whichinturntriggersthesystemtoputthekeyboardonscreenandsendthekeyboardeventstothetextfieldorview.Thekeyboardandthetextfieldorviewhavenodirectconnection,buttheyworktogetherthroughthefirstresponderstatus.

Thisisaneatwaytoensurethatthekeyboardinputisdeliveredtothecorrecttextfield.TheconceptofafirstresponderispartofthebroadertopicofeventhandlinginCocoaTouchprogramming,andthisincludestheUIResponderclassandtheresponderchain.YouwilllearnmoreaboutthemwhenyouhandletoucheventsinChapter17,andyoucanalsovisitApple’sEventHandlingGuideforiOSformoreinformation.

DismissingbypressingtheReturnkey

Nowlet’sgetbacktoallowinguserstodismissthekeyboard.Ifyoutouchanothertextfieldintheapplication,thattextfieldwillbecomethefirstresponder,andthekeyboardwillstayonscreen.Thekeyboardwillonlygiveupandgoawaywhennotextfield(ortextview)isthefirstresponder.Todismissthekeyboardthen,youcallresignFirstResponder()onthetextfieldthatisthefirstresponder.

WOW! eBook www.wowebook.org

Page 373: iOS Programming The Big Nerd Ranch Guide, 5th

TohavethetextfieldresigninresponsetotheReturnkeybeingpressed,youaregoingtoimplementtheUITextFieldDelegatemethodtextFieldShouldReturn(_:).ThismethodiscalledwhenevertheReturnkeyispressed.

First,inDetailViewController.swift,haveDetailViewControllerconformtotheUITextFieldDelegateprotocol.classDetailViewController:UIViewController,UITextFieldDelegate{

Next,implementtextFieldShouldReturn(_:)tocallresignFirstResponder()onthetextfieldthatispassedin.functextFieldShouldReturn(textField:UITextField)->Bool{textField.resignFirstResponder()returntrue}

Finally,openMain.storyboardandconnectthedelegatepropertyofeachtextfieldtotheDetailViewController(Figure13.8).(Control-dragfromeachUITextFieldtotheDetailViewControllerandchoosedelegate.)

Figure13.8Connectingthedelegatepropertyofatextfield

Buildandruntheapplication.TapatextfieldandthenpresstheReturnkeyonthekeyboard.Thekeyboardwilldisappear.Togetthekeyboardback,touchanytextfield.

Dismissingbytappingelsewhere

ItwouldbestylishtoalsodismissthekeyboardiftheusertapsanywhereelseonDetailViewController’sview.Todothis,youaregoingtouseagesturerecognizerwhentheviewistapped,justasyoudidintheWorldTrotterapp.Intheactionmethod,youwillcallresignFirstResponder()onthetextfield.

OpenMain.storyboardandfindTapGestureRecognizerintheobjectlibrary.DragthisobjectontothebackgroundviewfortheDetailViewController.Youwillseeareferencetothisgesturerecognizerinthescenedock.

WOW! eBook www.wowebook.org

Page 374: iOS Programming The Big Nerd Ranch Guide, 5th

Intheprojectnavigator,Option-clickDetailViewController.swifttoopenitintheassistanteditor.Control-dragfromthetapgesturerecognizerinthestoryboardtotheimplementationofDetailViewController.

Inthepop-upthatappears,selectActionfromtheConnectionmenu.NametheactionbackgroundTapped.FortheType,chooseUITapGestureRecognizer(Figure13.9).

Figure13.9ConfiguringaUITapGestureRecognizeraction

ClickConnectandthestubfortheactionmethodwillappearinDetailViewController.swift.UpdatethemethodtocallendEditing(_:)ontheviewofDetailViewController.@IBActionfuncbackgroundTapped(sender:AnyObject){view.endEditing(true)}

CallingendEditing(_:)isaconvenientwaytodismissthekeyboardwithouthavingtoknow(orcare)whichtextfieldisthefirstresponder.Whentheviewgetsthiscall,itcheckstoseeifanytextfieldinitshierarchyisthefirstresponder.Ifso,thenresignFirstResponder()iscalledonthatparticularview.

Buildandrunyourapplication.Taponatextfieldtoshowthekeyboard.Tapontheviewoutsideofatextfieldandthekeyboardwilldisappear.

Thereisonefinalcasewhereyouneedtodismissthekeyboard.WhentheusertapstheBackbutton,viewWillDisappear(_:)iscalledontheDetailViewControllerbeforeitispoppedoffthestack.InDetailViewController.swift,updatetheimplementationofviewWillDisappear(_:)tocallendEditing(_:).overridefuncviewWillDisappear(animated:Bool){super.viewWillDisappear(animated)

//Clearfirstresponderview.endEditing(true)

//"Save"changestoitemitem.name=nameField.text??""item.serialNumber=serialNumberField.text

ifletvalueText=valueField.text,letvalue=numberFormatter.numberFromString(valueText){item.valueInDollars=value.integerValue}else{item.valueInDollars=0}}

WOW! eBook www.wowebook.org

Page 375: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 376: iOS Programming The Big Nerd Ranch Guide, 5th

UINavigationBarInthissection,youaregoingtogivetheUINavigationBaradescriptivetitlefortheUIViewControllerthatiscurrentlyontopoftheUINavigationController’sstack.

EveryUIViewControllerhasanavigationItempropertyoftypeUINavigationItem.However,unlikeUINavigationBar,UINavigationItemisnotasubclassofUIView,soitcannotappearonthescreen.Instead,thenavigationitemsuppliesthenavigationbarwiththecontentitneedstodraw.WhenaUIViewControllercomestothetopofaUINavigationController’sstack,theUINavigationBarusestheUIViewController’snavigationItemtoconfigureitself,asshowninFigure13.10.

Figure13.10UINavigationItem

Bydefault,aUINavigationItemisempty.Atthemostbasiclevel,aUINavigationItemhasasimpletitlestring.WhenaUIViewControllerismovedtothetopofthenavigationstackanditsnavigationItemhasavalidstringforitstitleproperty,thenavigationbarwilldisplaythatstring(Figure13.11).

Figure13.11UINavigationItemwithtitle

ThetitlefortheItemsViewControllerwillalwaysremainthesame,soyoucansetthetitleofitsnavigationitemwithinthestoryboarditself.

WOW! eBook www.wowebook.org

Page 377: iOS Programming The Big Nerd Ranch Guide, 5th

OpenMain.storyboard.Double-clickonthecenterofthenavigationbarabovetheItemsViewControllertoedititstitle.Giveitatitleof“Homepwner”(Figure13.12).

Figure13.12Settingthetitleinastoryboard

Buildandruntheapplication.NoticethestringHomepwneronthenavigationbar.Createandtaponarowandnoticethatthenavigationbarnolongerhasatitle.ItwouldbenicetohavetheDetailViewController’snavigationitemtitlebethenameoftheItemitisdisplaying.SincethetitlewilldependontheItemthatisbeingdisplayed,youneedtosetthetitleofthenavigationItemdynamicallyincode.

InDetailViewController.swift,addapropertyobservertotheitempropertythatupdatesthetitleofthenavigationItem.varitem:Item!{didSet{navigationItem.title=item.name}}

Buildandruntheapplication.CreateandtaparowandyouwillseethatthetitleofthenavigationbaristhenameoftheItemyouselected.

Anavigationitemcanholdmorethanjustatitlestring,asshowninFigure13.13.TherearethreecustomizableareasforeachUINavigationItem:aleftBarButtonItem,arightBarButtonItem,andatitleView.TheleftandrightbarbuttonitemsarereferencestoinstancesofUIBarButtonItem,whichcontaintheinformationforabuttonthatcanonlybedisplayedonaUINavigationBaroraUIToolbar.

Figure13.13UINavigationItemwitheverything

RecallthatUINavigationItemisnotasubclassofUIView.Instead,

WOW! eBook www.wowebook.org

Page 378: iOS Programming The Big Nerd Ranch Guide, 5th

UINavigationItemencapsulatesinformationthatUINavigationBarusestoconfigureitself.Similarly,UIBarButtonItemisnotaview,butholdstheinformationabouthowasinglebuttonontheUINavigationBarshouldbedisplayed.(AUIToolbaralsousesinstancesofUIBarButtonItemtoconfigureitself.)

ThethirdcustomizableareaofaUINavigationItemisitstitleView.YoucaneitheruseabasicstringasthetitleorhaveasubclassofUIViewsitinthecenterofthenavigationitem.Youcannothaveboth.Ifitsuitsthecontextofaspecificviewcontrollertohaveacustomview(likeasegmentedcontroloratextfield,forexample),youwouldsetthetitleViewofthenavigationitemtothatcustomview.Figure13.13showsanexamplefromthebuilt-inMapsapplicationofaUINavigationItemwithacustomviewasitstitleView.Typically,however,atitlestringissufficient.

Addingbuttonstothenavigationbar

Inthissection,youaregoingtoreplacethetwobuttonsthatareinthetable’sheaderviewwithtwobarbuttonitemsthatwillappearintheUINavigationBarwhentheItemsViewControllerisontopofthestack.

First,let’sworkonabarbuttonitemforaddingnewitems.ThisbuttonwillsitontherightsideofthenavigationbarwhentheItemsViewControllerisontopofthestack.Whentapped,itwilladdanewItem.

Abarbuttonitemhasatarget-actionpairthatworkslikeUIControl’starget-actionmechanism:whentapped,itsendstheactionmessagetothetarget.

InMain.storyboard,opentheobjectlibraryanddragaBarButtonItemtotherightsideofItemsViewController’snavigationbar.Selectthisbarbuttonitemandopenitsattributesinspector.ChangetheSystemItemtoAdd(Figure13.14).

Figure13.14Systembarbuttonitem

Control-dragfromthisbarbuttonitemtotheItemsViewControllerandselectaddNewItem:WOW! eBook

www.wowebook.org

Page 379: iOS Programming The Big Nerd Ranch Guide, 5th

(Figure13.15).

Figure13.15ConnectingtheaddNewItem:action

Buildandruntheapplication.Tapthe+buttonandanewrowwillappearinthetable.

Nowlet’sreplacetheEditbutton.Viewcontrollersexposeabarbuttonitemthatwillautomaticallytoggletheireditingmode.ThereisnowaytoaccessthisthroughInterfaceBuilder,soyouwillneedtoaddthisbarbuttonitemprogrammatically.

InItemsViewController.swift,overridetheinit(coder:)methodtosettheleftbarbuttonitem.requiredinit?(coderaDecoder:NSCoder){super.init(coder:aDecoder)

navigationItem.leftBarButtonItem=editButtonItem()}

Buildandruntheapplication,addsomeitems,andtaptheEditbutton.TheUITableViewenterseditingmode!TheeditButtonItem()methodcreatesaUIBarButtonItemwiththetitleEdit.Evenbetter,thisbuttoncomeswithatarget-actionpair:itcallsthemethodsetEditing(_:animated:)toitsUIViewControllerwhentapped.

OpenMain.storyboard.NowthatHomepwnerhasafullyfunctionalnavigationbar,youcangetridoftheheaderviewandtheassociatedcode.SelecttheheaderviewonthetableviewandpressDelete.

Also,theUINavigationControllerwillhandleupdatingtheinsetsforthetableview.InItemsViewController.swift,deletethefollowingcode.overridefuncviewDidLoad(){super.viewDidLoad()

//GettheheightofthestatusbarletstatusBarHeight=UIApplication.sharedApplication().statusBarFrame.height

letinsets=UIEdgeInsets(top:statusBarHeight,left:0,bottom:0,right:0)tableView.contentInset=insetstableView.scrollIndicatorInsets=insets

tableView.rowHeight=UITableViewAutomaticDimension

WOW! eBook www.wowebook.org

Page 380: iOS Programming The Big Nerd Ranch Guide, 5th

tableView.estimatedRowHeight=65}

Finally,removethetoggleEditingMode(_:)method.@IBActionfunctoggleEditingMode(sender:AnyObject){ifediting{//Changetextofbuttontoinformuserofstatesender.setTitle("Edit",forState:.Normal)

//TurnoffeditingmodesetEditing(false,animated:true)}else{//Changetextofbuttontoinformuserofstatesender.setTitle("Done",forState:.Normal)

//EntereditingmodesetEditing(true,animated:true)}}

Buildandrunagain.TheoldEditandAddbuttonsaregone,leavingyouwithalovelyUINavigationBar(Figure13.16).

Figure13.16Homepwnerwithnavigationbar

WOW! eBook www.wowebook.org

Page 381: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 382: iOS Programming The Big Nerd Ranch Guide, 5th

BronzeChallenge:DisplayingaNumberPadThekeyboardfortheUITextFieldthatdisplaysaItem’svalueInDollarsisaQWERTYkeyboard.Itwouldbebetterifitwereanumberpad.ChangetheKeyboardTypeofthatUITextFieldtotheNumberPad.(Hint:youcandothisinthestoryboardfileusingtheattributesinspector.)

WOW! eBook www.wowebook.org

Page 383: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 384: iOS Programming The Big Nerd Ranch Guide, 5th

SilverChallenge:ACustomUITextFieldMakeasubclassofUITextFieldandoverridethebecomeFirstResponder()andresignFirstResponder()methods(inheritedfromUIResponder)sothatitsborderstylechangeswhenitisthefirstresponder.YoucanusetheborderStylepropertyofUITextFieldtoaccomplishthis.UseyoursubclassforthetextfieldsinDetailViewController.

WOW! eBook www.wowebook.org

Page 385: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 386: iOS Programming The Big Nerd Ranch Guide, 5th

GoldChallenge:PushingMoreViewControllersCurrently,instancesofItemcannothavetheirdateCreatedpropertychanged.ChangeItemsothattheycan,andthenaddabuttonunderneaththedateLabelinDetailViewControllerwiththetitleChangeDate.Whenthisbuttonistapped,pushanotherviewcontrollerinstanceontothenavigationstack.ThisviewcontrollershouldhaveaUIDatePickerinstancethatmodifiesthedateCreatedpropertyoftheselectedItem.

WOW! eBook www.wowebook.org

Page 387: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 388: iOS Programming The Big Nerd Ranch Guide, 5th

14Camera

Inthischapter,youaregoingtoaddphotostotheHomepwnerapplication.YouwillpresentaUIImagePickerControllersothattheusercantakeandsaveapictureofeachitem.TheimagewillthenbeassociatedwithanIteminstanceandviewableintheitem’sdetailview(Figure14.1).

Figure14.1Homepwnerwithcameraaddition

Imagestendtobeverylarge,soitisagoodideatostoreimagesseparatelyfromotherdata.Thus,youaregoingtocreateasecondstoreforimages.ImageStorewillfetchandcacheimagesastheyareneeded.

WOW! eBook www.wowebook.org

Page 389: iOS Programming The Big Nerd Ranch Guide, 5th

DisplayingImagesandUIImageViewYourfirststepistohavetheDetailViewControllergetanddisplayanimage.AneasywaytodisplayanimageistoputaninstanceofUIImageViewonthescreen.

OpenHomepwner.xcodeprojandMain.storyboard.ThendraganinstanceofUIImageViewontotheviewatthebottomofthestackview(Figure14.2).Selecttheimageviewandopenitssizeinspector.Youwanttheverticalcontenthuggingandcontentcompressionresistanceprioritiesfortheimageviewtobelowerthanthoseoftheotherviews.ChangetheVerticalContentHuggingPrioritytobe248andtheVerticalContentCompressionResistancePrioritytobe749.

Figure14.2UIImageViewonDetailViewController’sview

AUIImageViewdisplaysanimageaccordingtotheimageview’scontentModeproperty.Thispropertydetermineswheretopositionandhowtoresizethecontentwithintheimageview’sframe.UIImageView’sdefaultvalueforcontentModeisUIViewContentMode.ScaleToFill,whichadjuststheimagetoexactlymatchtheboundsoftheimageview.Ifyoukeepthedefault,animagetakenbythecamerawillbe

WOW! eBook www.wowebook.org

Page 390: iOS Programming The Big Nerd Ranch Guide, 5th

scaledtofitintothesquareUIImageView.Tomaintaintheimage’saspectratio,youhavetoupdatecontentMode.

WiththeUIImageViewselected,opentheattributesinspector.FindtheModeattributeandchangeittoAspectFit(Figure14.3).Youwillnotseeachangeonthestoryboard,butnowimageswillberesizedtofitwithintheboundsoftheUIImageView.

Figure14.3ChangeUIImageView’smodetoAspectFit

Next,Option-clickDetailViewController.swiftintheprojectnavigatortoopenitintheassistanteditor.Control-dragfromtheUIImageViewtothetopofDetailViewController.swift.NametheoutletimageViewandchooseStrongasthestoragetype.ClickConnect(Figure14.4).

Figure14.4CreatingtheimageViewoutlet

ThetopofDetailViewController.swiftshouldnowlooklikethis:classDetailViewController:UIViewController{

@IBOutletvarnameField:UITextField!@IBOutletvarserialNumberField:UITextField!@IBOutletvarvalueField:UITextField!@IBOutletvardateLabel:UILabel!@IBOutletvarimageView:UIImageView!

AddingacamerabuttonWOW! eBook

www.wowebook.org

Page 391: iOS Programming The Big Nerd Ranch Guide, 5th

Nowyouneedabuttontoinitiatethephoto-takingprocess.YouwillcreateaninstanceofUIToolbarandplaceitatthebottomofDetailViewController’sview.

InMain.storyboard,pressCommand-Returntoclosetheassistanteditorandgiveyourselfmoreroomtoworkinthestoryboard.Youaregoingtoneedtotemporarilybreakyourinterfaceinordertoaddthetoolbartotheinterface.

SelectthebottomconstraintforthestackviewandpressDeletetoremoveit.Youneedtomakeroomforthetoolbaronthebottom.AsofXcode7.1,itisdifficulttoresizethestackview.Soinstead,dragthestackviewupabit(Figure14.5).Theviewwillbemisplacedfornow,butyouwillfixthisshortly.

Figure14.5Movingthestackviewoutoftheway

Nowdragatoolbarfromtheobjectlibraryontothebottomoftheview.SelectthetoolbarandopentheAutoLayoutPinmenu.ConfiguretheconstraintsexactlyasshowninFigure14.6andthenclickAdd5Constraints.Sinceyouchosetheoptiontoupdateframes,thestackviewisrepositionedtoitscorrectlocation.

WOW! eBook www.wowebook.org

Page 392: iOS Programming The Big Nerd Ranch Guide, 5th

Figure14.6Toolbarconstraints

AUIToolbarworksalotlikeaUINavigationBar–youcanaddinstancesofUIBarButtonItemtoit.However,whereanavigationbarhastwoslotsforbarbuttonitems,atoolbarhasanarrayofbarbuttonitems.Youcanplaceasmanybarbuttonitemsinatoolbarascanfitonthescreen.

Bydefault,anewinstanceofUIToolbarthatiscreatedinaninterfacefilecomeswithoneUIBarButtonItem.Selectthisbarbuttonitemandopentheattributesinspector.ChangetheSystemItemtoCamera,andtheitemwillshowacameraicon(Figure14.7).

WOW! eBook www.wowebook.org

Page 393: iOS Programming The Big Nerd Ranch Guide, 5th

Figure14.7UIToolbarwithbarbuttonitem

Buildandruntheapplicationandnavigatetoanitem’sdetailstoseethetoolbarwithitscamerabarbuttonitem.Youhavenotconnectedthecamerabuttontoanactionyet,sotappingonitwillnotdoanything.

Thecamerabuttonneedsatargetandanaction.WithMain.storyboardstillopen,Option-clickDetailViewController.swiftintheprojectnavigatortoreopenitintheassistanteditor.

InMain.storyboard,selectthecamerabuttonbyfirstclickingonthetoolbarandthenthebuttonitself.Control-dragfromtheselectedbuttontoDetailViewController.swift.

IntheConnectionpop-upmenu,selectActionastheconnectiontype,nameittakePicture,selectUIBarButtonItemasthetype,andclickConnect(Figure14.8).

Figure14.8Creatinganaction

WOW! eBook www.wowebook.org

Page 394: iOS Programming The Big Nerd Ranch Guide, 5th

Ifyoumadeanymistakeswhilemakingthisconnection,youwillneedtoopenMain.storyboardanddisconnectanybadconnections.(Lookforyellowwarningsignsintheconnectionsinspector.)

WOW! eBook www.wowebook.org

Page 395: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 396: iOS Programming The Big Nerd Ranch Guide, 5th

TakingPicturesandUIImagePickerControllerInthetakePicture(_:)method,youwillinstantiateaUIImagePickerControllerandpresentitonthescreen.WhencreatinganinstanceofUIImagePickerController,youmustsetitssourceTypepropertyandassignitadelegate.Sincethereissetupworkneededfortheimagepickercontroller,youneedtocreateandpresentitprogrammaticallyinsteadofthroughthestoryboard.

Settingtheimagepicker’ssourceType

ThesourceTypeconstanttellstheimagepickerwheretogetimages.Ithasthreepossiblevalues(Figure14.9):

UIImagePickerControllerSourceType.Camera

Theuserwilltakeanewpicture.

UIImagePickerControllerSourceType.PhotoLibrary

Theuserwillbepromptedtoselectanalbumandthenaphotofromthatalbum.

UIImagePickerControllerSourceType.SavedPhotosAlbum

Theuserpicksfromthemostrecentlytakenphotos.

Figure14.9Examplesofthreesourcetypes

Thefirstsourcetype,.Camera,willnotworkonadevicethatdoesnothaveacamera.Sobeforeusingthistype,youhavetocheckforacamerabycallingthemethod

WOW! eBook www.wowebook.org

Page 397: iOS Programming The Big Nerd Ranch Guide, 5th

isSourceTypeAvailable(_:)ontheUIImagePickerControllerclass:classfuncisSourceTypeAvailable(type:UIImagePickerControllerSourceType)->Bool

CallingthismethodreturnsaBooleanvalueforwhetherthedevicesupportsthepassed-insourcetype.

InDetailViewController.swift,findthestubfortakePicture(_:).AddthefollowingcodetocreatetheimagepickerandsetitssourceType.@IBActionfunctakePicture(sender:UIBarButtonItem){

letimagePicker=UIImagePickerController()

//Ifthedevicehasacamera,takeapicture;otherwise,//justpickfromphotolibraryifUIImagePickerController.isSourceTypeAvailable(.Camera){imagePicker.sourceType=.Camera}else{imagePicker.sourceType=.PhotoLibrary}}

Settingtheimagepicker’sdelegate

Inadditiontoasourcetype,theUIImagePickerControllerinstanceneedsadelegate.WhentheuserselectsanimagefromtheUIImagePickerController’sinterface,thedelegateissentthemessageimagePickerController(_:didFinishPickingMediaWithInfo:).(Iftheusertapsthecancelbutton,thenthedelegatereceivesthemessageimagePickerControllerDidCancel(_:).)

Theimagepicker’sdelegatewillbetheinstanceofDetailViewController.AtthetopofDetailViewController.swift,declarethatDetailViewControllerconformstotheUINavigationControllerDelegateandtheUIImagePickerControllerDelegateprotocols.classDetailViewController:UIViewController,UITextFieldDelegate,UINavigationControllerDelegate,UIImagePickerControllerDelegate{

WhyUINavigationControllerDelegate?UIImagePickerController’sdelegatepropertyisactuallyinheritedfromitssuperclass,UINavigationController,andwhileUIImagePickerControllerhasitsowndelegateprotocol,itsinheriteddelegatepropertyisdeclaredtoreferenceanobjectthatconformstoUINavigationControllerDelegate.

InDetailViewController.swift,addthefollowingcodetotakePicture(_:)tosettheinstanceofDetailViewControllertobetheimagepicker’sdelegate.@IBActionfunctakePicture(sender:AnyObject){

letimagePicker=UIImagePickerController()

//Ifthedevicehasacamera,takeapicture;otherwise,//justpickfromphotolibraryifUIImagePickerController.isSourceTypeAvailable(.Camera){imagePicker.sourceType=.Camera

WOW! eBook www.wowebook.org

Page 398: iOS Programming The Big Nerd Ranch Guide, 5th

}else{imagePicker.sourceType=.PhotoLibrary}

imagePicker.delegate=self}

Presentingtheimagepickermodally

OncetheUIImagePickerControllerhasasourcetypeandadelegate,youcandisplayitbypresentingtheviewcontrollermodally.

InDetailViewController.swift,addcodetotheendoftakePicture(_:)topresenttheUIImagePickerController.imagePicker.delegate=self

//PlaceimagepickeronthescreenpresentViewController(imagePicker,animated:true,completion:nil)}

Buildandruntheapplication.SelectanItemtoseeitsdetailsandthentapthecamerabuttonontheUIToolbar.UIImagePickerController’sinterfacewillappearonthescreen(Figure14.10),andyoucantakeapictureorchooseanexistingimageifyourdevicedoesnothaveacamera.

(Ifyouareworkingonthesimulator,therearesomedefaultimagesalreadyinthephotolibrary.Ifyouwouldliketoaddyourown,youcandraganimagefromyourcomputerontothesimulator,anditwillbeaddedtothesimulator’sphotolibrary.Alternatively,youcanopenSafariinthesimulatorandnavigatetoapagewithanimage.ClickandholdtheimageandchooseSaveImagetosaveitinthesimulator’sphotolibrary.)

WOW! eBook www.wowebook.org

Page 399: iOS Programming The Big Nerd Ranch Guide, 5th

Figure14.10UIImagePickerController’spreviewinterface

Savingtheimage

SelectinganimagedismissestheUIImagePickerControllerandreturnsyoutothedetailview.However,youdonothaveareferencetothephotooncetheimagepickerisdismissed.Tofixthis,youaregoingtoimplementthedelegatemethodimagePickerController(_:didFinishPickingMediaWithInfo:).Thismethodiscalledontheimagepicker’sdelegatewhenaphotohasbeenselected.

InDetailViewController.swift,implementthismethodtoputtheimageintotheUIImageViewandthencallthemethodtodismisstheimagepicker.funcimagePickerController(picker:UIImagePickerController,didFinishPickingMediaWithInfoinfo:[String:AnyObject]){

//Getpickedimagefrominfodictionaryletimage=info[UIImagePickerControllerOriginalImage]as!UIImage

WOW! eBook www.wowebook.org

Page 400: iOS Programming The Big Nerd Ranch Guide, 5th

//PutthatimageonthescreenintheimageviewimageView.image=image

//Takeimagepickeroffthescreen-//youmustcallthisdismissmethoddismissViewControllerAnimated(true,completion:nil)}

Buildandruntheapplicationagain.Take(orselect)aphoto.Theimagepickerisdismissed,andyouarereturnedtotheDetailViewController’sview,whereyouwillseetheselectedphoto.

Homepwner’suserscouldhavehundredsofitemstocatalog,andeachonecouldhavealargeimageassociatedwithit.KeepinghundredsofinstancesofIteminmemoryisnotabigdeal.Keepinghundredsofimagesinmemorywouldbebad:First,youwillgetalowmemorywarning.Then,ifyourapp’smemoryfootprintcontinuestogrow,theOSwillterminateit.Thesolution,whichyouaregoingtoimplementinthenextsection,istostoreimagestodiskandonlyfetchthemintoRAMwhentheyareneeded.Thisfetchingwillbedonebyanewclass,ImageStore.Whentheapplicationreceivesalow-memorynotification,theImageStore’scachewillbeflushedtofreethememorythatthefetchedimageswereoccupying.

WOW! eBook www.wowebook.org

Page 401: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 402: iOS Programming The Big Nerd Ranch Guide, 5th

CreatingImageStoreInChapter15,youwillhaveinstancesofItemwriteouttheirpropertiestoafile,whichwillthenbereadinwhentheapplicationstarts.However,becauseimagestendtobeverylarge,itisagoodideatokeepthemseparatefromotherdata.YouaregoingtostorethepicturestheusertakesinaninstanceofaclassnamedImageStore.Theimagestorewillfetchandcachetheimagesastheyareneeded.Itwillalsobeabletoflushthecacheifthedevicerunslowonmemory.

CreateanewSwiftfilenamedImageStore.InImageStore.swift,definetheImageStoreclassandaddapropertythatisaninstanceofNSCache.importFoundationimportUIKit

classImageStore{

letcache=NSCache()

}

Thecacheworksverymuchlikeadictionary(whichyousawinChapter2).Youareabletoadd,remove,andupdatevaluesassociatedwithagivenkey.Unlikeadictionary,thecachewillautomaticallyremoveobjectsifthesystemgetslowonmemory.Whilethiscouldbeaprobleminthischapter(sinceimageswillonlyexistwithinthecache),youwillfixtheprobleminChapter15whenyouwillalsowritetheimagestothefilesystem.

Nowimplementthreemethodsforadding,retrieving,anddeletinganimagefromthedictionary.classImageStore:NSObject{

letcache=NSCache()

funcsetImage(image:UIImage,forKeykey:String){cache.setObject(image,forKey:key)}

funcimageForKey(key:String)->UIImage?{returncache.objectForKey(key)as?UIImage}

funcdeleteImageForKey(key:String){cache.removeObjectForKey(key)}

}

WOW! eBook www.wowebook.org

Page 403: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 404: iOS Programming The Big Nerd Ranch Guide, 5th

GivingViewControllersAccesstotheImageStoreTheDetailViewControllerneedsaninstanceofImageStoretofetchandstoreimages.YouwillinjectthisdependencyintotheDetailViewController’sdesignatedinitializer,justasyoudidforItemsViewControllerandItemStoreinChapter9.

InDetailViewController.swift,addapropertyforanImageStore.varitem:Item!{didSet{navigationItem.title=item.name}}varimageStore:ImageStore!

NowdothesameinItemsViewController.swift.letitemStore:ItemStorevarimageStore:ImageStore!

Next,stillinItemsViewController.swift,updateprepareForSegue(_:sender:)tosettheimageStorepropertyonDetailViewController.overridefuncprepareForSegue(segue:UIStoryboardSegue,sender:AnyObject?){ifsegue.identifier=="ShowItem"{ifletrow=tableView.indexPathForSelectedRow?.row{letitem=itemStore.allItems[row]letdetailViewController=segue.destinationViewControlleras!DetailViewControllerdetailViewController.item=itemdetailViewController.imageStore=imageStore}}}

Finally,updateAppDelegate.swifttocreateandinjecttheImageStore.funcapplication(application:UIApplication,didFinishLaunchingWithOptionslaunchOptions:[NSObject:AnyObject]?)->Bool{//Overridepointforcustomizationafterapplicationlaunch.

//CreateanItemStoreletitemStore=ItemStore()

//CreateanImageStoreletimageStore=ImageStore()

//GettheItemsViewControllerletnavController=window!.rootViewControlleras!UINavigationControllerletitemsController=navController.topViewControlleras!ItemsViewController

//AccesstheItemsViewControllerandsetitsitemstoreandimagestoreitemsController.itemStore=itemStoreitemsController.imageStore=imageStore

WOW! eBook www.wowebook.org

Page 405: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 406: iOS Programming The Big Nerd Ranch Guide, 5th

CreatingandUsingKeysWhenanimageisaddedtothestore,itwillbeputintothecacheunderauniquekey,andtheassociatedItemobjectwillbegiventhatkey.WhentheDetailViewControllerwantsanimagefromthestore,itwillaskitsitemforthekeyandsearchthecachefortheimage.

AddapropertytoItem.swifttostorethekey.letdateCreated:NSDateletitemKey:String

Theimagekeysneedtobeuniqueforyourcachetowork.Whiletherearemanywaystohacktogetherauniquestring,youaregoingtousetheCocoaTouchmechanismforcreatinguniversallyuniqueidentifiers(UUIDs),alsoknownasgloballyuniqueidentifiers(GUIDs).ObjectsoftypeNSUUIDrepresentaUUIDandaregeneratedusingthetime,acounter,andahardwareidentifier,whichisusuallytheMACaddressoftheWiFicard.Whenrepresentedasastring,UUIDslooksomethinglikethis:4A73B5D2-A6F4-4B40-9F82-EA1E34C1DC04

InItem.swift,generateaUUIDandsetitastheitemKey.init(name:String,serialNumber:String?,valueInDollars:Int){

self.name=nameself.serialNumber=serialNumberself.valueInDollars=valueInDollarsself.dateCreated=NSDate()self.itemKey=NSUUID().UUIDString

super.init()}

Then,inDetailViewController.swift,updateimagePickerController(_:didFinishPickingMediaWithInfo:)tostoretheimageintheImageStore.funcimagePickerController(picker:UIImagePickerController,didFinishPickingMediaWithInfoinfo:[String:AnyObject]){

//Getpickedimagefrominfodictionaryletimage=info[UIImagePickerControllerOriginalImage]as!UIImage

//StoretheimageintheImageStorefortheitem'skeyimageStore.setImage(image,forKey:item.itemKey)

//PutthatimageonthescreenintheimageviewimageView.image=image

//Takeimagepickeroffthescreen-//youmustcallthisdismissmethoddismissViewControllerAnimated(true,completion:nil)}

Eachtimeanimageiscaptured,itwillbeaddedtothestore.BoththeImageStoreandtheItemwillknowthekeyfortheimage,sobothwillbeabletoaccessitasneeded(Figure14.11).

WOW! eBook www.wowebook.org

Page 407: iOS Programming The Big Nerd Ranch Guide, 5th

Figure14.11Accessingimagesfromthecache

Similarly,whenanitemisdeleted,youneedtodeleteitsimagefromtheimagestore.InItemsViewController.swift,updatetableView(_:commitEditingStyle:forRowAtIndexPath:)toremovetheitem’simagefromtheimagestore.overridefunctableView(tableView:UITableView,commitEditingStyleeditingStyle:UITableViewCellEditingStyle,forRowAtIndexPathindexPath:NSIndexPath){

//Ifthetableviewisaskingtocommitadeletecommand...ifeditingStyle==.Delete{letitem=itemStore.allItems[indexPath.row]

lettitle="Delete\(item.name)?"letmessage="Areyousureyouwanttodeletethisitem?"

letac=UIAlertController(title:title,message:message,preferredStyle:.ActionSheet)

letdeleteAction=UIAlertAction(title:"Delete",style:.Destructive,handler:{(action)->Voidin//Removetheitemfromthestoreself.itemStore.removeItem(item)

//Removetheitem'simagefromtheimagestoreself.imageStore.deleteImageForKey(item.itemKey)

//Alsoremovethatrowfromthetableviewwithananimationself.tableView.deleteRowsAtIndexPaths([indexPath],withRowAnimation:.Automatic)})ac.addAction(deleteAction)

//PresentthealertcontrollerpresentViewController(ac,animated:true,completion:nil)

WOW! eBook www.wowebook.org

Page 408: iOS Programming The Big Nerd Ranch Guide, 5th

}}

WOW! eBook www.wowebook.org

Page 409: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 410: iOS Programming The Big Nerd Ranch Guide, 5th

WrappingUpImageStoreNowthattheImageStorecanstoreimagesandinstancesofItemhaveakeytogetanimage(Figure14.11),youneedtoteachDetailViewControllerhowtograbtheimagefortheselectedItemandplaceitinitsimageView.

TheDetailViewController’sviewwillappearwhentheusertapsarowinItemsViewControllerandwhentheUIImagePickerControllerisdismissed.Inbothofthesesituations,theimageViewshouldbepopulatedwiththeimageoftheItembeingdisplayed.

InDetailViewController.swift,addcodetoviewWillAppear(_:)todothis.overridefuncviewWillAppear(animated:Bool){super.viewWillAppear(animated)

nameField.text=item.nameserialNumberField.text=item.serialNumbervalueField.text=currencyFormatter.stringFromNumber(item.valueInDollars)dateLabel.text=dateFormatter.stringFromDate(item.dateCreated)

//Gettheitemkeyletkey=item.itemKey

//Ifthereisanassociatedimagewiththeitem//displayitontheimageviewletimageToDisplay=imageStore.imageForKey(key)imageView.image=imageToDisplay}

Buildandruntheapplication.CreateanItemandselectitfromtheUITableView.Then,tapthecamerabuttonandtakeapicture.Theimagewillappearasitshould.

WOW! eBook www.wowebook.org

Page 411: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 412: iOS Programming The Big Nerd Ranch Guide, 5th

BronzeChallenge:EditinganImageUIImagePickerControllerhasabuilt-ininterfaceforeditinganimageonceithasbeenselected.AllowtheusertoedittheimageandusetheeditedimageinsteadoftheoriginalimageinDetailViewController.

WOW! eBook www.wowebook.org

Page 413: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 414: iOS Programming The Big Nerd Ranch Guide, 5th

SilverChallenge:RemovinganImageAddabuttonthatclearstheimageforanitem.

WOW! eBook www.wowebook.org

Page 415: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 416: iOS Programming The Big Nerd Ranch Guide, 5th

GoldChallenge:CameraOverlayAUIImagePickerControllerhasacameraOverlayViewproperty.MakeitsothatpresentingtheUIImagePickerControllershowsacrosshairinthemiddleoftheimagecapturearea.

WOW! eBook www.wowebook.org

Page 417: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 418: iOS Programming The Big Nerd Ranch Guide, 5th

FortheMoreCurious:NavigatingImplementationFilesBothofyourviewcontrollershavequiteafewmethodsintheirimplementationfiles.TobeaneffectiveiOSdeveloper,youmustbeabletogotothecodeyouarelookingforquicklyandeasily.ThesourceeditorjumpbarinXcodeisonetoolatyourdisposal(Figure14.12).

Figure14.12Sourceeditorjumpbar

Thejumpbarshowsyouwhereexactlyyouarewithintheproject(andalsowherethecursoriswithinagivenfile).Figure14.13breaksdownthejumpbardetails.

Figure14.13Jumpbardetails

Thebreadcrumbtrailnavigationofthejumpbarmirrorstheprojectnavigationhierarchy.Ifyouclickonanyofthesections,youwillbepresentedwithapopoverofthatsectionintheprojecthierarchy.Fromthere,youcaneasilynavigatetootherpartsoftheproject.

Figure14.14showsthefilepopoverfortheHomepwnerfolder.

Figure14.14Filepopover

WOW! eBook www.wowebook.org

Page 419: iOS Programming The Big Nerd Ranch Guide, 5th

Perhapsmostusefulistheabilitytonavigateeasilywithinanimplementationfile.Ifyouclickonthelastelementinthebreadcrumbtrail,youwillgetapopoverwiththecontentsofthefile,includingallofthemethodsimplementedwithinthatfile.

Whilethepopoverisstillvisible,youcanstarttypingtofiltertheitemsinthelist.Atanypoint,youcanusetheupanddownarrowkeysandthenpresstheReturnkeytojumptothatmethodinthecode.Figure14.15showswhatyougetwhenyousearchfor“indexpath”inItemsViewController.swift.

Figure14.15Filepopoverwith“indexpath”search

//MARK:

Asyourclassesgetlonger,itcangetmoredifficulttofindamethodburiedinalonglistofmethods.Agoodwaytoorganizeyourmethodsistouse//MARK:comments.

Twouseful//MARK:commentsarethedividerandthelabel://Thisisadivider//MARK:-

//Thisisalabel//MARK:MyAwesomeMethods

Thedividerandlabelcanbecombined://MARK:-ViewlifecycleoverridefuncviewDidLoad(){...}overridefuncviewWillAppear(animated:Bool){...}

//MARK:-ActionsfuncaddNewItem(sender:AnyObject){...}

Adding//MARK:commentstoyourcodedoesnotchangethecodeitself;itjusttellsXcodehowtovisuallyorganizeyourmethods.Youcanseetheresultsbyopeningthecurrentfileiteminthejumpbar.Figure14.16presentsawell-organizedItemsViewController.swift.

WOW! eBook www.wowebook.org

Page 420: iOS Programming The Big Nerd Ranch Guide, 5th

Figure14.16Filepopoverwith//MARK:s

Ifyoumakeahabitofusing//MARK:comments,youwillforceyourselftoorganizeyourcode.Ifdonethoughtfully,thiswillmakeyourcodemorereadableandeasiertoworkwith.

WOW! eBook www.wowebook.org

Page 421: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 422: iOS Programming The Big Nerd Ranch Guide, 5th

15Saving,Loading,andApplication

StatesTherearemanywaystosaveandloaddatainaniOSapplication.ThischapterwilltakeyouthroughsomeofthemostcommonmechanismsaswellastheconceptsyouneedforwritingtoorreadingfromthefilesysteminiOS.Alongtheway,youwillbeupdatingHomepwnersothatitsdatapersistsbetweenruns(Figure15.1).

Figure15.1Homepwnerinthetaskswitcher

WOW! eBook www.wowebook.org

Page 423: iOS Programming The Big Nerd Ranch Guide, 5th

ArchivingMostiOSapplicationsare,atbase,doingthesamething:providinganinterfacefortheusertomanipulatedata.Everyobjectinanapplicationhasaroleinthisprocess.Modelobjectsareresponsibleforholdingontothedatathattheusermanipulates.Viewobjectsreflectthatdata,andcontrollersareresponsibleforkeepingtheviewsandthemodelobjectsinsync.Therefore,savingandloading“data”almostalwaysmeanssavingandloadingmodelobjects.

InHomepwner,themodelobjectsthatausermanipulatesareinstancesofItem.ForHomepwnertobeausefulapplication,instancesofItemmustpersistbetweenrunsoftheapplication.YouwillbeusingarchivingtosaveandloadItemobjects.

ArchivingisoneofthemostcommonwaysofpersistingmodelobjectsiniOS.Archivinganobjectinvolvesrecordingallofitspropertiesandsavingthemtothefilesystem.Unarchivingrecreatestheobjectfromthatdata.

ClasseswhoseinstancesneedtobearchivedandunarchivedmustconformtotheNSCodingprotocolandimplementitstworequiredmethods,encodeWithCoder(_:)andinit(coder:).protocolNSCoding{funcencodeWithCoder(aCoder:NSCoder)init?(coderaDecoder:NSCoder)}

Whenobjectsareaddedtoaninterfacefile,suchasastoryboardfile,theyarearchived.Atruntime,theobjectsareloadedintomemorybybeingunarchivedfromtheinterfacefile.UIViewandUIViewControllerbothconformtotheNSCodingprotocol,sobothcanbearchivedandunarchivedwithoutanyextraeffortfromyou.

YourItemclass,ontheotherhand,doesnotcurrentlyconformtoNSCoding.OpenHomepwner.xcodeprojandaddthisprotocoldeclarationinItem.swift.classItem:NSObject,NSCoding{

Thenextstepistoimplementtherequiredmethods.Let’sstartwithencodeWithCoder(_:).WhenanItemissentthemessageencodeWithCoder(_:),itwillencodeallofitspropertiesintotheNSCoderobjectthatispassedasanargument.Whilesaving,youwilluseNSCodertowriteoutastreamofdata.Thatstreamwillbestoredonthefilesystemandisorganizedaskey-valuepairs.

InItem.swift,implementencodeWithCoder(_:)toaddthenamesandvaluesoftheitem’spropertiestothestream.funcencodeWithCoder(aCoder:NSCoder){aCoder.encodeObject(name,forKey:"name")aCoder.encodeObject(dateCreated,forKey:"dateCreated")aCoder.encodeObject(itemKey,forKey:"itemKey")aCoder.encodeObject(serialNumber,forKey:"serialNumber")

aCoder.encodeInteger(valueInDollars,forKey:"valueInDollars")}

NoticethatreferencestoobjectsareencodedwithencodeObject(_:forKey:),but

WOW! eBook www.wowebook.org

Page 424: iOS Programming The Big Nerd Ranch Guide, 5th

valueInDollarsisencodedwithencodeInteger(_:forKey:).BecausevalueInDollarsisanIntandnotanobject,youcannotencodeitwithencodeObject(_:forKey:).

Why,then,canyouuseencodeObject(_:forKey:)forname,whichisaString?Stringisnotaclass,andthusinstancesofStringarenotobjects.WhenyoupassaStringtoamethodthatexpectsanobject,likeencodeObject(_:forKey:),SwiftconvertsittoanNSString–thestringclassfromtheFoundationframework–thankstothespecialrelationshipbetweenStringandNSString.

TofindoutwhichencodingmethodstouseforotherSwifttypes,youcancheckthedocumentationforNSCoder.Regardlessofthetypeoftheencodedvalue,thereisalwaysakey,whichisastringthatidentifieswhichpropertyisbeingencoded.Byconvention,thiskeyisthenameofthepropertybeingencoded.

Encodingisarecursiveprocess.Whenanobjectisencoded(thatis,whenitisthefirstargumentinencodeObject(_:forKey:)),thatobjectissentencodeWithCoder(_:).DuringtheexecutionofitsencodeWithCoder(_:)method,itencodesitsobject(orString)propertiesusingencodeObject(_:forKey:)(Figure15.2).Thus,eachobjectencodesanyobjectsthatitreferences,whichencodeanyobjectsthattheyreference,andsoon.

Figure15.2Encodinganobject

Tobeencoded,theseobjectsmustalsoconformtoNSCoding.Let’sconfirmthisforNSDate.Theprotocolsthatatypeconformstoarelistedinitsreference.Insteadofopeningupthedocumentationbrowserasyouhavedonebefore,youcantakeashortcuttogettothereferencedirectlyfromyourcode.

InItem.swift,holddowntheOptionkey,mouseoveranoccurrenceofNSDateinyourcode,andclick.Apop-upwindowwillappearwithabriefdescriptionofthetypeand

WOW! eBook www.wowebook.org

Page 425: iOS Programming The Big Nerd Ranch Guide, 5th

linkstoitsreference,asshowninFigure15.3.

Figure15.3Option-clickingNSDate

Atthetop,noticetheDeclarationlinewheretheclassdeclarationforNSDateisgiven.HereyoucanseethatNSDatedoesconformtotheNSCodingprotocol.

YoucanalsoclicktheReferencelinkatthebottomtobetakentotheNSDatereference.Atthetopofthereferenceyouwillseealistofprotocolsthattheclassconformsto.

(YouarenotdoingthesamecheckforStringbecauseStringdoesnotconformtoNSCoding.RecallthatwhenyoupassaStringtoencodeObject(_:forKey:),thestringisconvertedtoanNSString,andNSStringdoesconformtoNSCoding.)

Option-clickingisnotjustforclassesandtypes.Youcanusethesameshortcutformethods,protocols,andmore.Keepthisinmindasyourunacrossitemsinyourcodethatyouwanttoknowmoreabout.

Nowbacktokeysandencoding.ThepurposeofthekeyistoretrievetheencodedvaluewhenthisItemisloadedfromthefilesystemlater.Objectsbeingloadedfromanarchivearesentthemessageinit(coder:).ThismethodshouldgraballoftheobjectsthatwereencodedinencodeWithCoder(_:)andassignthemtotheappropriateproperty.

InItem.swift,implementinit(coder:).requiredinit(coderaDecoder:NSCoder){name=aDecoder.decodeObjectForKey("name")as!StringdateCreated=aDecoder.decodeObjectForKey("dateCreated")as!NSDateitemKey=aDecoder.decodeObjectForKey("itemKey")as!StringserialNumber=aDecoder.decodeObjectForKey("serialNumber")as!String?

valueInDollars=aDecoder.decodeIntegerForKey("valueInDollars")

super.init()}

NoticethatthismethodhasanNSCoderargument,too.Ininit(coder:),theNSCoderisfullofdatatobeconsumedbytheItembeinginitialized.AlsonoticethatyoucalldecodeObjectForKey:tothecontainertogetobjects(includinginstancesofStringasNSStringobjects)anddecodeIntegerForKey(_:)togetthe

WOW! eBook www.wowebook.org

Page 426: iOS Programming The Big Nerd Ranch Guide, 5th

valueInDollars.

InChapter9,wetalkedabouttheinitializerchainanddesignatedinitializers.Theinit(coder:)methodisnotpartofthisdesignpattern.YouwillkeepItem’sdesignatedinitializerthesame,andinit(coder:)willnotcallit.

InstancesofItemarenowNSCoding-compliantandcanbesavedtoandloadedfromthefilesystemusingarchiving.Youcanbuildtheapplicationtomakesuretherearenosyntaxerrors,butyoustillneedawaytokickoffthesavingandloading.Youalsoneedaplaceonthefilesystemtostorethesaveditems.

WOW! eBook www.wowebook.org

Page 427: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 428: iOS Programming The Big Nerd Ranch Guide, 5th

ApplicationSandboxEveryiOSapplicationhasitsownapplicationsandbox.Anapplicationsandboxisadirectoryonthefilesystemthatisbarricadedfromtherestofthefilesystem.Yourapplicationmuststayinitssandbox,andnootherapplicationcanaccessyoursandbox.Figure15.4showstheapplicationsandboxforiTunes/iCloud.

Figure15.4Applicationsandbox

Theapplicationsandboxcontainsanumberofdirectories:

Documents/

Thisdirectoryiswhereyouwritedatathattheapplicationgeneratesduringruntimeandthatyouwanttopersistbetweenrunsoftheapplication.ItisbackedupwhenthedeviceissynchronizedwithiTunesoriCloud.Ifsomethinggoeswrongwiththedevice,filesinthisdirectorycanberestoredfromiTunesoriCloud.InHomepwner,thefilethatholdsthedataforallyouritemswillbestoredhere.

Library/Caches/

Thisdirectoryiswhereyouwritedatathattheapplicationgeneratesduringruntimeandthatyouwanttopersistbetweenrunsoftheapplication.However,unliketheDocumentsdirectory,itdoesnotgetbackedupwhenthedeviceissynchronizedwithiTunesoriCloud.Amajorreasonfornotbackingupcacheddataisthatthedatacanbeverylargeandextendthetimeittakestosynchronizeyourdevice.Datastoredsomewhereelse–likeawebserver–canbeplacedinthisdirectory.Iftheuserneedstorestorethedevice,thisdatacanbedownloadedfromthewebserveragain.Ifthedeviceisverylowondiskspace,thesystemmaydeletethecontentsofthisdirectory.

Library/Preferences/

ThisdirectoryiswhereanypreferencesarestoredandwheretheSettingsapplicationlooksforapplicationpreferences.Library/PreferencesishandledautomaticallybytheclassNSUserDefaultsandisbackedupwhen

WOW! eBook www.wowebook.org

Page 429: iOS Programming The Big Nerd Ranch Guide, 5th

thedeviceissynchronizedwithiTunesoriCloud.

tmp/

Thisdirectoryiswhereyouwritedatathatyouwillusetemporarilyduringanapplication’sruntime.TheOSmaypurgefilesinthisdirectorywhenyourapplicationisnotrunning.However,tobetidyyoushouldstillexplicitlyremovefilesfromthisdirectorywhenyounolongerneedthem.ThisdirectorydoesnotgetbackedupwhenthedeviceissynchronizedwithiTunesoriCloud.

ConstructingafileURL

TheinstancesofItemfromHomepwnerwillbesavedtoasinglefileintheDocumentsdirectory.TheItemStorewillhandlewritingtoandreadingfromthatfile.Todothis,theItemStoreneedstoconstructaURLtothisfile.

ImplementanewpropertyinItemStore.swifttostorethisURL.varallItems=[Item]()letitemArchiveURL:NSURL={letdocumentsDirectories=NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory,inDomains:.UserDomainMask)letdocumentDirectory=documentsDirectories.first!returndocumentDirectory.URLByAppendingPathComponent("items.archive")}()

Insteadofassigningavaluetothepropertydirectly,thevalueisbeingsetusingaclosure.YoumayrecallthatyoudidthiswiththenumberFormatterpropertyinChapter4.Noticethattheclosureherehasasignatureof()->NSURL,meaningitdoesnottakeinanyargumentsanditreturnsaninstanceofNSURL.WhentheItemStoreclassisinstantiated,thisclosurewillberunandthereturnvaluewillbeassignedtotheitemArchiveURLproperty.Usingaclosurelikethisallowsyoutosetthevalueforavariableorconstantthatrequiresmultiplelinesofcode,whichcanbeveryusefulwhenconfiguringobjects.Thismakesyourcodemoremaintainablebecauseitkeepsthepropertyandthecodeneededtogeneratethepropertytogether.

ThemethodURLsForDirectory(_:inDomains:)searchesthefilesystemforaURLthatmeetsthecriteriagivenbythearguments.(Double-checkthatyourfirstargumentis.DocumentDirectoryandnot.DocumentationDirectory.Autocomplete’sfirstsuggestionis.DocumentationDirectory,soitiseasytointroducethiserrorandendupwiththewrongURL.)

IniOS,thelastargumentisalwaysthesame.(ThismethodisborrowedfromOSX,wheretherearesignificantlymoreoptions.)ThefirstargumentisanNSSearchPathDirectoryenumerationthatspecifiesthedirectoryinthesandboxyouwanttheURLto.Forexample,searchingfor.CachesDirectorywillreturntheCachesdirectoryintheapplication’ssandbox.

YoucansearchthedocumentationforNSSearchPathDirectorytolocatetheotheroptions.RememberthattheseenumerationvaluesaresharedbyiOSandOSX,sonotallofthemwillworkoniOS.

WOW! eBook www.wowebook.org

Page 430: iOS Programming The Big Nerd Ranch Guide, 5th

ThereturnvalueofURLsForDirectory(_:inDomains:)isanarrayofURLs.Itisanarraybecause,inOSX,theremaybemultipleURLsthatmeetthesearchcriteria.IniOS,however,therewillonlybeone(ifthedirectoryyousearchedforisanappropriatesandboxdirectory).Therefore,thenameofthearchivefileisappendedtothefirstandonlyURLinthearray.ThiswillbewherethearchiveofIteminstanceswilllive.

WOW! eBook www.wowebook.org

Page 431: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 432: iOS Programming The Big Nerd Ranch Guide, 5th

NSKeyedArchiverandNSKeyedUnarchiverYounowhaveaplacetosavedataonthefilesystemandamodelobjectthatcanbesavedtothefilesystem.Thefinaltwoquestionsare:howdoyoukickoffthesavingandloadingprocesses,andwhendoyoudoit?TosaveinstancesofItem,youwillusetheclassNSKeyedArchiverwhentheapplication“exits.”

InItemStore.swift,implementanewmethodthatcallsarchiveRootObject(_:toFile:)ontheNSKeyedArchiverclass.funcsaveChanges()->Bool{print("Savingitemsto:\(itemArchiveURL.path!)")returnNSKeyedArchiver.archiveRootObject(allItems,toFile:itemArchiveURL.path!)}

ThearchiveRootObject(_:toFile:)methodtakescareofsavingeverysingleIteminallItemstotheitemArchiveURL.Yes,itisthatsimple.HereishowarchiverRootObject(_:toFile:)works:

ThemethodbeginsbycreatinganinstanceofNSKeyedArchiver.(NSKeyedArchiverisaconcretesubclassoftheabstractclassNSCoder.)

ThemethodencodeWithCoder:iscalledonallItemsandispassedtheinstanceofNSKeyedArchiverasanargument.

TheallItemsarraythencallsencodeWithCoder(_:)toalloftheobjectsitcontains,passingthesameNSKeyedArchiver.Thus,allyourinstancesofItemencodetheirinstancevariablesintotheverysameNSKeyedArchiver(Figure15.5).

TheNSKeyedArchiverwritesthedataitcollectedtothepath.

Figure15.5ArchivingtheallItemsarray

WOW! eBook www.wowebook.org

Page 433: iOS Programming The Big Nerd Ranch Guide, 5th

WhentheuserpressestheHomebuttononthedevice,themessageapplicationDidEnterBackground(_:)issenttotheAppDelegate.ThatiswhenyouwanttosendsaveChangestotheItemStore.

OpenAppDelegate.swiftandaddapropertytotheclasstostoretheItemStoreinstance.YouwillneedapropertyinordertoreferencetheinstanceinapplicationDidEnterBackground(_:).classAppDelegate:UIResponder,UIApplicationDelegate{

varwindow:UIWindow?letitemStore=ItemStore()

Thenupdateapplication(_:didFinishLaunchingWithOptions:)tousethispropertyinsteadofthelocalconstant.funcapplication(application:UIApplication,didFinishLaunchingWithOptionslaunchOptions:[NSObject:AnyObject]?)->Bool{//Overridepointforcustomizationafterapplicationlaunch.

//CreateanItemStoreletitemStore=ItemStore()

//CreateanImageStoreletimageStore=ImageStore()

//GettheItemsViewControllerletnavController=window!.rootViewControlleras!UINavigationControllerletitemsController=navController.topViewControlleras!ItemsViewController

//SettheitemstoreaswellastheimagestoreitemsController.itemStore=itemStoreitemsController.imageStore=imageStore

returntrue}

Sincethepropertyandthelocalconstantwerenamedthesame,youonlyneededtoremovethecodethatcreatedthelocalconstant.

InAppDelegate.swift,implementapplicationDidEnterBackground(_:)tokickoffsavingtheIteminstances.(Thismethodmayhavealreadybeenimplementedbythetemplate.Ifso,makesuretoaddcodetotheexistingmethodinsteadofwritingabrandnewone.)funcapplicationDidEnterBackground(application:UIApplication){letsuccess=itemStore.saveChanges()if(success){print("SavedalloftheItems")}else{print("CouldnotsaveanyoftheItems")}}

Buildandruntheapplicationonthesimulator.CreateafewinstancesofItem,thenpresstheHomebuttontoleavetheapplication.Checktheconsoleandyoushouldseealogstatementindicatingthattheitemsweresaved.

WhileyoucannotyetloadtheseinstancesofItembackintotheapplication,youcanstillverifythatsomethingwassaved.

Intheconsole’slogstatements,findonethatlogsouttheitemArchiveURLlocationandanotherthatindicateswhethersavingwassuccessful.Ifsavingwasnotsuccessful,

WOW! eBook www.wowebook.org

Page 434: iOS Programming The Big Nerd Ranch Guide, 5th

confirmthatyouritemArchiveURLisbeingcreatedcorrectly.Iftheitemsweresavedsuccessfully,copythepaththatisprintedtotheconsole.

OpenFinderandpressCommand-Shift-G.PastethefilepaththatyoucopiedfromtheconsoleandpressReturn.Youwillbetakentothedirectorythatcontainstheitems.archivefile.PressCommand-Uptonavigatetotheparentdirectoryofitems.archive.Thisistheapplication’ssandboxdirectory.Here,youcanseetheDocuments,Library,andtmpdirectoriesalongsidetheapplicationitself(Figure15.6).

Figure15.6Homepwner’ssandbox

Thelocationofthesandboxdirectorycanchangebetweenrunsoftheapplication;however,thecontentsofthatsandboxwillremainunchanged.Duetothis,youmayneedtocopyandpastethedirectoryintoFinderfrequentlywhileworkingonanapplication.

Loadingfiles

Nowlet’sturntoloadingthesefiles.ToloadinstancesofItemwhentheapplicationlaunches,youwillusetheclassNSKeyedUnarchiverwhentheItemStoreiscreated.

InItemStore.swift,overrideinit()toaddthefollowingcode.init(){ifletarchivedItems=NSKeyedUnarchiver.unarchiveObjectWithFile(itemArchiveURL.path!)as?[Item]{allItems+=archivedItems}}

TheunarchiveObjectWithFile(_:)methodwillcreateaninstanceofNSKeyedUnarchiverandloadthearchivelocatedattheitemArchiveURLintothatinstance.TheNSKeyedUnarchiverwilltheninspectthetypeoftherootobjectinthearchiveandcreateaninstanceofthattype.Inthiscase,thetypewillbeanarrayofItemsbecauseyoucreatedthisarchivewitharootobjectoftype[Item].(IftherootobjectwasaninstanceofIteminstead,thenunarchiveObjectWithFile(_:)wouldreturnanItem.)

Thenewlycreatedarrayisthensentinit(coder:)and,asyoumayhaveguessed,theNSKeyedUnarchiverispassedastheargument.Thearraystartsdecodingitscontents(instancesofItem)fromtheNSKeyedUnarchiverandsendseachoftheseobjectsthemessageinit(coder:),passingthesameNSKeyedUnarchiver.

Buildandruntheapplication.Youritemswillbeavailableuntilyouexplicitlydelete

WOW! eBook www.wowebook.org

Page 435: iOS Programming The Big Nerd Ranch Guide, 5th

them.Onethingtonoteabouttestingyoursavingandloadingcode:ifyoukillHomepwnerfromXcode,themethodapplicationDidEnterBackground(_:)willnotgetachancetobecalledandtheitemarraywillnotbesaved.YoumustpresstheHomebuttonfirstandthenkillitfromXcodebyclickingtheStopbutton.

WOW! eBook www.wowebook.org

Page 436: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 437: iOS Programming The Big Nerd Ranch Guide, 5th

ApplicationStatesandTransitionsInHomepwner,theitemsarearchivedwhentheapplicationentersthebackgroundstate.Itisusefultounderstandthestatesanapplicationcanbein,whatcausesapplicationstotransitionbetweenstates,andhowyourcodecanbenotifiedofthesetransitions.ThisinformationissummarizedinFigure15.7.

Figure15.7Statesofatypicalapplication

Whenanapplicationisnotrunning,itisinthenotrunningstateanditdoesnotexecuteanycodeorhaveanymemoryreservedinRAM.

Aftertheuserlaunchesanapplication,itenterstheactivestate.Whenintheactivestate,anapplication’sinterfaceisonthescreen,itisacceptingevents,anditscodeishandlingthoseevents.

Whileintheactivestate,anapplicationcanbetemporarilyinterruptedbyasystemeventlikeanSMSmessage,pushnotification,phonecall,oralarm.Anoverlaywillappearontopofyourapplicationtohandlethisevent,andtheapplicationenterstheinactivestate.Intheinactivestate,anapplicationisvisiblebehindtheoverlayandisexecutingcode,butitisnotreceivingevents.Applicationstypicallyspendverylittletimeintheinactivestate.YoucanforceanactiveapplicationintotheinactivestatebypressingtheLockbuttonatthetopofthedevice.Theapplicationwillstayinactiveuntilthedeviceisunlocked.

WhentheuserpressestheHomebuttonorswitchestoanotherapplicationinsomeother

WOW! eBook www.wowebook.org

Page 438: iOS Programming The Big Nerd Ranch Guide, 5th

way,theapplicationentersthebackgroundstate.(Actually,itspendsabriefmomentintheinactivestatebeforetransitioningtothebackgroundstate.)Inthebackgroundstate,anapplication’sinterfaceisnotvisibleorreceivingevents,butitcanstillexecutecode.Bydefault,anapplicationthatentersthebackgroundstatehasabout10secondsbeforeitentersthesuspendedstate.Yourapplicationshouldnotrelyonthisnumber;instead,itshouldsaveuserdataandreleaseanysharedresourcesasquicklyaspossible.

Anapplicationinthesuspendedstatecannotexecutecode.Youcannotseeitsinterface,andanyresourcesitdoesnotneedwhilesuspendedaredestroyed.Asuspendedapplicationisessentiallyflash-frozenandcanbequicklythawedwhentheuserrelaunchesit.Table15.1summarizesthecharacteristicsofthedifferentapplicationstates.

Table15.1Applicationstates

State Visible ReceivesEvents ExecutesCode

NotRunning No No No

Active Yes Yes Yes

Inactive Mostly No Yes

Background No No Yes

Suspended No No No

Youcanseewhatapplicationsareinthebackgroundorsuspendedbydouble-tappingtheHomebuttontogettothetaskswitcher(Figure15.8).YoucandothisinthesimulatorbypressingCommand-Shift-Htwice.(Recentlyrunapplicationsthathavebeenterminatedmayalsoappearinthisdisplay.)

WOW! eBook www.wowebook.org

Page 439: iOS Programming The Big Nerd Ranch Guide, 5th

Figure15.8Backgroundandsuspendedapplicationsinthetaskswitcher

Anapplicationinthesuspendedstatewillremaininthatstateaslongasthereisadequatesystemmemory.WhentheOSdecidesmemoryisgettinglow,itterminatessuspendedapplicationsasneeded.Asuspendedapplicationgetsnoindicationthatitisabouttobeterminated.Itissimplyremovedfrommemory.(Anapplicationmayremaininthetaskswitcherafterithasbeenterminated,butitwillhavetorelaunchwhentapped.)

Whenanapplicationchangesitsstate,amethodiscalledontheapplicationdelegate.HerearesomeofthemethodsfromtheUIApplicationDelegateprotocolthatannounceapplicationstatetransitions.(ThesearealsoshowninFigure15.7.)optionalfuncapplication(application:UIApplication,didFinishLaunchingWithOptionslaunchOptions:[NSObject:AnyObject]?)->BooloptionalfuncapplicationDidBecomeActive(application:UIApplication)optionalfuncapplicationWillResignActive(application:UIApplication)optionalfuncapplicationDidEnterBackground(application:UIApplication)optionalfuncapplicationWillEnterForeground(application:UIApplication)

Youcanimplementthesemethodstotaketheappropriateactionsforyourapplication.WOW! eBook

www.wowebook.org

Page 440: iOS Programming The Big Nerd Ranch Guide, 5th

Transitioningtothebackgroundstateisagoodplacetosaveanyoutstandingchangesbecauseitisthelasttimeyourapplicationcanexecutecodebeforeitentersthesuspendedstate.Onceinthesuspendedstate,anapplicationcanbeterminatedatthewhimoftheOS.

WOW! eBook www.wowebook.org

Page 441: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 442: iOS Programming The Big Nerd Ranch Guide, 5th

WritingtotheFilesystemwithNSDataYourarchivinginHomepwnersavesandloadstheitemKeyforeachItem,butwhatabouttheimages?Atthemoment,theyarelostwhentheappentersthebackgroundstate.Inthissection,youwillextendtheimagestoretosaveimagesastheyareaddedandfetchthemastheyareneeded.

TheimagesforIteminstancesshouldalsobestoredintheDocumentsdirectory.Youcanusetheimagekeygeneratedwhentheusertakesapicturetonametheimageinthefilesystem.

ImplementanewmethodinImageStore.swiftnamedimageURLForKey(_:)tocreateaURLinthedocumentsdirectoryusingagivenkey.funcimageURLForKey(key:String)->NSURL{

letdocumentsDirectories=NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory,inDomains:.UserDomainMask)letdocumentDirectory=documentsDirectories.first!

returndocumentDirectory.URLByAppendingPathComponent(key)}

Tosaveandloadanimage,youaregoingtocopytheJPEGrepresentationoftheimageintoabufferinmemory.Insteadofjustcreatingabuffer,Swiftprogrammershaveahandyclasstocreate,maintain,anddestroythesesortsofbuffers–NSData.AnNSDatainstanceholdssomenumberofbytesofbinarydata,andyouwilluseNSDatatostoreimagedata.

InImageStore.swift,modifysetImage(_:forKey:)togetaURLandsavetheimage.funcsetImage(image:UIImage,forKeykey:String){cache.setObject(image,forKey:key)

//CreatefullURLforimageletimageURL=imageURLForKey(key)

//TurnimageintoJPEGdataifletdata=UIImageJPEGRepresentation(image,0.5){//WriteittofullURLdata.writeToURL(imageURL,atomically:true)}}

Let’sexaminethiscodemoreclosely.ThefunctionUIImageJPEGRepresentationtakestwoparameters:aUIImageandacompressionquality.ThecompressionqualityisaFloatfrom0to1,where1isthehighestquality(leastcompression).ThefunctionreturnsaninstanceofNSDataifthecompressionsucceedsandnilifitdoesnot.

ThisNSDatainstancecanbewrittentothefilesystembycallingwriteToURL(_:atomically:).ThebytesheldinthisNSDataarethenwrittentotheURLspecifiedbythefirstparameter.Thesecondparameter,atomically,isaBooleanvalue.Ifitistrue,thefileiswrittentoatemporaryplaceonthefilesystem,and,oncethewritingoperationiscomplete,thatfileisrenamedtotheURLofthefirstparameter,replacinganypreviouslyexistingfile.Writingatomicallypreventsdata

WOW! eBook www.wowebook.org

Page 443: iOS Programming The Big Nerd Ranch Guide, 5th

corruptionshouldyourapplicationcrashduringthewriteprocedure.

Itisworthnotingthatthiswayofwritingdatatothefilesystemisnotarchiving.WhileNSDatainstancescanbearchived,usingthemethodwriteToURL(_:atomically:)copiesthebytesintheNSDatadirectlytothefilesystem.

Nowthattheimageisstoredinthefilesystem,theImageStorewillneedtoloadthatimagewhenitisrequested.TheclassmethodimageWithContentsOfFile(_:)ofUIImagewillreadinanimagefromafile,givenaURL.

InImageStore.swift,updatethemethodimageForKey(_:)sothattheImageStorewillloadtheimagefromthefilesystemifitdoesnotalreadyhaveit.funcimageForKey(key:String)->UIImage?{

returncache.objectForKey(key)as?UIImage

ifletexistingImage=cache.objectForKey(key)as?UIImage{returnexistingImage}

letimageURL=imageURLForKey(key)guardletimageFromDisk=UIImage(contentsOfFile:imageURL.path!)else{returnnil}

cache.setObject(imageFromDisk,forKey:key)returnimageFromDisk}

Whatisthatguardstatement?guardisaconditionalstatement,likeanifstatement.Thecompilerwillonlycontinuepasttheguardstatementiftheconditionwithintheguardistrue.Here,theconditioniswhethertheUIImageinitializationissuccessful.Iftheinitializationfails,theelseblockisexecuted,whichallowsyoutohaveanearlyreturn.Iftheinitializationsucceeds,anyvariablesorconstantsboundintheguardstatement(here,imageFromDisk)areusableaftertheguardstatement.

Thecodeaboveisfunctionallyequivalenttothefollowingcode:ifletimageFromDisk=UIImage(contentsOfFile:imageURL.path!){cache.setObject(imageFromDisk,forKey:key)returnimageFromDisk}

returnnil

Whileyoucoulddothis,guardprovidesbothacleaner–and,moreimportantly,asafer–waytoensurethatyouexitifdonothavewhatyouneed.

Usingguardalsoforcesthefailurecasetobedirectlytiedtotheconditionbeingchecked.Thismakesthecodemorereadableandeasiertoreasonabout.

Youareabletosaveanimagetodiskandretrieveanimagefromdisk,sothelastthingyouneedtodoisaddfunctionalitytoremoveanimagefromdisk.

InImageStore.swift,makesurethatwhenanimageisdeletedfromthestore,itisalsodeletedfromthefilesystem.(Youwillseeanerrorwhenyoutypeinthiscode,whichwewilldiscussnext.)funcdeleteImageForKey(key:String){

WOW! eBook www.wowebook.org

Page 444: iOS Programming The Big Nerd Ranch Guide, 5th

cache.removeObjectForKey(key)

letimageURL=imageURLForKey(key)NSFileManager.defaultManager().removeItemAtURL(imageURL)}

Let’stakealookattheerrormessagethatthiscodegenerated,showninFigure15.9.

Figure15.9Errorwhenremovingtheimagefromdisk

ThiserrormessageislettingyouknowthatthemethodremoveItemAtURL(_:)canfail,butyouarenothandlingtheerror.Let’sfixthis.

WOW! eBook www.wowebook.org

Page 445: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 446: iOS Programming The Big Nerd Ranch Guide, 5th

ErrorHandlingItisoftenusefultohaveawayofrepresentingthepossibilityoffailurewhencreatingmethods.Youhaveseenonewayofrepresentingfailurethroughoutthisbookwiththeuseofoptionals.Optionalsprovideasimplewaytorepresentfailurewhenyoudonotcareaboutthereasonforfailure.ConsiderthecreationofanIntfromaString.lettheMeaningOfLife="42"letnumberFromString=Int(theMeaningOfLife)

ThisinitializeronInttakesaStringparameterandreturnsanoptionalInt(anInt?).ThisisbecausethestringmaynotbeabletoberepresentedasanInt.ThecodeabovewillsuccessfullycreateanInt,butthefollowingcodewillnot:letpi="ApplePie"letnumberFromString=Int(pi)

Thestring“ApplePie”cannotberepresentedasanInt,sonumberFromStringwillcontainnil.Anoptionalworkswellforrepresentingfailureherebecauseyoudonotcarewhyitfailed.Youjustwanttoknowwhetheritwassuccessful.

Whenyouneedtoknowwhysomethingfailed,anoptionalwillnotprovideenoughinformation.Thinkaboutremovingtheimagefromthefilesystem–whymightitfail?PerhapsthereisnofileatthespecifiedURL,ortheURLismalformed,oryoudonothavepermissiontoremovethatfile.Thereareanumberofreasonsthismethodcouldfail,andyoumightwanttohandleeachcasedifferently.

Swiftprovidesaricherrorhandlingsystemwithcompilersupporttoensurethatyourecognizewhensomethingbadcouldhappen.YoualreadysawthiswhentheSwiftcompilertoldyouthatyouwerenothandlingapossibleerrorwhenremovingthefilefromdisk.

Ifamethodcouldgenerateanerror,itsmethodsignatureneedstoindicatethisusingthethrowskeyword.HereisthemethoddefinitionforremoveItemAtURL(_:):funcremoveItemAtURL(URL:NSURL)throws

Thethrowskeywordindicatesthatthismethodcouldthrowanerror.(Ifyouarefamiliarwiththrowingexceptioninotherlanguages,Swift’serrorhandlingisnotthesameasthrowingexception.)Byusingthiskeyword,thecompilerensuresthatanyonewhousesthismethodknowsthatthismethodcanthrowanerror–and,moreimportantly,thatthecalleralsohandlesanypotentialerrors.Thisishowthecompilerwasabletoletyouknowthatyouarenothandlingerrorswhenattemptingtoremoveafilefromdisk.

Tocallamethodthatcanthrow,youuseado-catchstatement.Withinthedoblock,youannotateanymethodsthatmightthrowanerrorusingthetrykeywordtoreinforcetheideathatthecallmightfail.

InImageStore.swift,updatedeleteImageForKey(_:)tocallremoveItemAtURL(_:)usingado-catchstatement.funcdeleteImageForKey(key:String){cache.removeObjectForKey(key)

letimageURL=imageURLForKey(key)

WOW! eBook www.wowebook.org

Page 447: iOS Programming The Big Nerd Ranch Guide, 5th

NSFileManager.defaultManager().removeItemAtURL(imageURL)do{tryNSFileManager.defaultManager().removeItemAtURL(imageURL)}catch{

}}

Ifamethoddoesthrowanerror,thentheprogramimmediatelyexitsthedoblock;nofurthercodeinthedoblockisexecuted.Atthatpoint,theerrorispassedtothecatchblockforittobehandledinsomeway.

UpdatedeleteImageForKey(_:)toprintouttheerrortotheconsole.funcdeleteImageForKey(key:String){cache.removeObjectForKey(key)

letimageURL=imageURLForKey(key)do{tryNSFileManager.defaultManager().removeItemAtURL(imageURL)}catch{print("Errorremovingtheimagefromdisk:\(error)")}}

Withinthecatchblock,thereisanimpliciterrorconstantthatcontainsinformationdescribingtheerror.Youcanoptionallygivethisconstantanexplicitname.

UpdatedeleteImageForKey(_:)touseanexplicitnamefortheerrorbeingcaught.funcdeleteImageForKey(key:String){cache.removeObjectForKey(key)

letimageURL=imageURLForKey(key)do{tryNSFileManager.defaultManager().removeItemAtURL(imageURL)}catch{print("Errorremovingtheimagefromdisk:\(error)")}catchletdeleteError{print("Errorremovingtheimagefromdisk:\(deleteError)")}}

Thereisalotmorethatyoucandowitherrorhandling,butthisisthebasicknowledgethatyouneedfornow.Wewillcovermoredetailsasyouprogressthroughthisbook.

BuildandruntheapplicationnowthattheImageStoreiscomplete.TakeaphotoforanitemandexittheapplicationtotheHomescreen(onthesimulator,selectHardware→HomeorpressShift-Command-H;onahardwaredevicesimplypresstheHomebutton).Launchtheapplicationagain.Selectingthatsameitemwillshowallitssaveddetails–includingthephotoyoujusttook.

Noticethattheimagesaresavedimmediatelyafterbeingtaken,whiletheinstancesofItemaresavedonlywhentheapplicationentersthebackground.Yousavetheimagesrightawaybecausetheyarejusttoobigtokeepinmemoryforlong.

WOW! eBook www.wowebook.org

Page 448: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 449: iOS Programming The Big Nerd Ranch Guide, 5th

BronzeChallenge:PNGInsteadofsavingeachimageasaJPEG,saveitasaPNG.

WOW! eBook www.wowebook.org

Page 450: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 451: iOS Programming The Big Nerd Ranch Guide, 5th

FortheMoreCurious:ApplicationStateTransitionsLet’swritesomequickcodetogetabetterunderstandingofthedifferentapplicationstatetransitions.

InAppDelegate.swift,implementtheapplicationstatetransitiondelegatemethodssothattheyprintoutthenameofthemethod.Youwillneedtoaddfourmoremethods.(Checktomakesurethetemplatehasnotalreadycreatedthesemethodsbeforewritingbrandnewones.)Ratherthanhardcodingthenameofthemethodintheprint,usethe__FUNCTION__expression.Atcompiletime,the__FUNCTION__expressionwillevaluatetoaStringrepresentingthenameofthemethod.funcapplicationWillResignActive(application:UIApplication){print(__FUNCTION__)}

funcapplicationDidEnterBackground(application:UIApplication){print(__FUNCTION__)

letsuccess=itemStore.saveChanges()if(success){print("SavedalloftheItems")}else{print("CouldnotsaveanyoftheItems")}}

funcapplicationWillEnterForeground(application:UIApplication){print(__FUNCTION__)}

funcapplicationDidBecomeActive(application:UIApplication){print(__FUNCTION__)}

funcapplicationWillTerminate(application:UIApplication){print(__FUNCTION__)}

Now,addthesameprint()statementtothetopofapplication(_:didFinishLaunchingWithOptions:).funcapplication(application:UIApplication,didFinishLaunchingWithOptionslaunchOptions:[NSObject:AnyObject]?)->Bool{print(__FUNCTION__)...}

Buildandruntheapplication.Youwillseethattheapplicationgetssentapplication(_:didFinishLaunchingWithOptions:)andthenapplicationDidBecomeActive(_:).Playaroundtoseewhatactionscausewhattransitions.

PresstheHomebuttonandtheconsolewillreportthattheapplicationbrieflyinactivatedandthenwentintothebackgroundstate.RelaunchtheapplicationbytappingitsiconontheHomescreenorinthetaskswitcher.Theconsolewillreportthattheapplicationenteredtheforegroundandthenbecameactive.

PresstheHomebuttontoexittheapplicationagain.Then,double-presstheHomebuttontoopenthetaskswitcher.SwipetheHomepwnerapplicationupandoffthisdisplaytoquit

WOW! eBook www.wowebook.org

Page 452: iOS Programming The Big Nerd Ranch Guide, 5th

theapplication.Notethatnomethodiscalledonyourapplicationdelegateatthispoint–itissimplyterminated.

WOW! eBook www.wowebook.org

Page 453: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 454: iOS Programming The Big Nerd Ranch Guide, 5th

FortheMoreCurious:ReadingandWritingtotheFilesystemInadditiontoarchivingandNSData’sbinaryreadandwritemethods,thereareafewmoremethodsfortransferringdatatoandfromthefilesystem.Oneofthem,CoreData,iscomingupinChapter21.Acoupleothersareworthmentioninghere.

UsingNSDataworkswellforbinarydata.Fortextdata,Stringhastwoinstancemethods:writeToURL(_:atomically:encoding:)andinit(contentsOfURL:encoding:).Theyareusedasfollows://SavesomeStringtothefilesystemdo{trysomeString.writeToURL(someURL,atomically:true,encoding:NSUTF8StringEncoding)}catch{print("ErrorwritingtoURL:\(error)")}

//LoadsomeStringfromthefilesystemdo{letmyEssay=tryString(contentsOfURL:someURL,encoding:NSUTF8StringEncoding)print(myEssay)}catch{print("ErrorreadingfromURL:\(error)")}

Notethatinmanylanguages,anythingunexpectedresultsinanexceptionbeingthrown.InSwift,exceptionsarenearlyalwaysusedtoindicateprogrammererror.Whenanexceptionisthrown,theinformationaboutwhatwentwrongisinanNSExceptionobject.Thatinformationisusuallyjustahinttotheprogrammer,like,“Youtriedtoaccessthe7thobjectinthisarray,butthereareonlytwo.”Thesymbolsforthecallstack(asitappearedwhentheexceptionwasthrown)arealsointheNSException.

Whendoyouuseexceptions,andwhendoyouuseerrorhandling?Ifyouarewritingamethodthatshouldonlybecalledwithanoddnumberasanargument,throwanexceptionifitiscalledwithanevennumber–thecallerismakinganerrorandyouwanttohelpthatprogrammerfindtheerror.Ifyouarewritingamethodthatwantstoreadthecontentsofaparticulardirectorybutdoesnothavethenecessaryprivileges,useSwift’serrorhandlingandthrowanerrortothecallertoindicatewhyyouwereunabletofulfillthisveryreasonablerequest.

Propertylistserializabletypescanalsobewrittentothefilesystem.TheonlytypesthatarepropertylistserializableareString,NSNumber(includingprimitiveslikeInt,Double,andBool),NSDate,NSData,Array<T>,andDictionary<K,V>.WhenanArray<T>orDictionary<K,V>iswrittentothefilesystemwiththesemethods,anXMLpropertylistiscreated.AnXMLpropertylistisacollectionoftaggedvalues:<?xmlversion="1.0"encoding="UTF-8"?><!DOCTYPEplistPUBLIC"-//Apple//DTDPLIST1.0//EN""http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plistversion="1.0"><array><dict>

WOW! eBook www.wowebook.org

Page 455: iOS Programming The Big Nerd Ranch Guide, 5th

<key>firstName</key><string>Christian</string><key>lastName</key><string>Keur</string></dict><dict><key>firstName</key><string>Aaron</string><key>lastName</key><string>Hillegass</string></dict></array></plist>

XMLpropertylistsareaconvenientwaytostoredatabecausetheycanbereadonnearlyanysystem.Manywebserviceapplicationsusepropertylistsasinputandoutput.Thecodeforwritingandreadingapropertylistlookslikethis:letauthors:AnyObject=[["firstName":"Christian","lastName":"Keur"],["firstName":"Aaron","lastName":"Hillegass"]]

//WritearraytodiskifNSPropertyListSerialization.propertyList(authors,isValidForFormat:.XMLFormat_v1_0){do{letdata=tryNSPropertyListSerialization.dataWithPropertyList(authors,format:.XMLFormat_v1_0,options:0)data.writeToURL(url,atomically:true)}catch{print("Errorwritingplist:\(error)")}}

//Readarrayfromdiskdo{letdata=tryNSData(contentsOfURL:url,options:[])letauthors=tryNSPropertyListSerialization.propertyListWithData(data,options:[],format:nil)print("Readinauthors:\(authors)")}catch{print("Errorreadingplist:\(error)")}

WOW! eBook www.wowebook.org

Page 456: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 457: iOS Programming The Big Nerd Ranch Guide, 5th

FortheMoreCurious:TheApplicationBundleWhenyoubuildaniOSapplicationprojectinXcode,youcreateanapplicationbundle.Theapplicationbundlecontainstheapplicationexecutableandanyresourcesyouhavebundledwithyourapplication.Resourcesarethingslikestoryboardfiles,images,andaudiofiles–anyfilesthatwillbeusedatruntime.Whenyouaddaresourcefiletoaproject,Xcodeissmartenoughtorealizethatitshouldbebundledwithyourapplication.

Howcanyoutellwhichfilesarebeingbundledwithyourapplication?SelecttheHomepwnerprojectfromtheprojectnavigator.CheckouttheBuildPhasespaneintheHomepwnertarget.EverythingunderCopyBundleResourceswillbeaddedtotheapplicationbundlewhenitisbuilt.

EachitemintheHomepwnertargetgroupisoneofthephasesthatoccurswhenyoubuildaproject.TheCopyBundleResourcesphaseiswherealloftheresourcesinyourprojectgetcopiedintotheapplicationbundle.

Youcancheckoutwhatanapplicationbundlelookslikeonthefilesystemafteryouinstallanapplicationonthesimulator.Printtheapplicationbundlepathtotheconsoleandthennavigatetothatdirectory.print(NSBundle.mainBundle().bundlePath)

Control-clicktheapplicationbundleandchooseShowPackageContentsfromthecontextualmenu(Figure15.10).

Figure15.10Viewinganapplicationbundle

AFinderwindowwillappearshowingyouthecontentsoftheapplicationbundle

WOW! eBook www.wowebook.org

Page 458: iOS Programming The Big Nerd Ranch Guide, 5th

(Figure15.11).WhenusersdownloadyourapplicationfromtheAppStore,thesefilesarecopiedtotheirdevices.

Figure15.11Theapplicationbundle

Youcanloadfilesfromtheapplication’sbundleatruntime.TogetthefullURLforfilesintheapplicationbundle,youneedtogetareferencetotheapplicationbundleandthenaskitfortheURLofaresource.//GetareferencetotheapplicationbundleletapplicationBundle=NSBundle.mainBundle()

//AskfortheURLtoaresourcenamedmyImage.pnginthebundleifleturl=applicationBundle.URLForResource("myImage",ofType:"png"){//DosomethingwithURL}

IfyouaskfortheURLtoafilethatisnotintheapplication’sbundle,thismethodwillreturnnil.Ifthefiledoesexist,thenthefullURLisreturned,andyoucanusethisURLtoloadthefilewiththeappropriateclass.

Bearinmindthatfileswithintheapplicationbundleareread-only.Youcannotmodifythem,norcanyoudynamicallyaddfilestotheapplicationbundleatruntime.Filesintheapplicationbundlearetypicallythingslikebuttonimages,interfacesoundeffects,ortheinitialstateofadatabaseyoushipwithyourapplication.Youwillusethismethodinlaterchapterstoloadthesetypesofresourcesatruntime.

WOW! eBook www.wowebook.org

Page 459: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 460: iOS Programming The Big Nerd Ranch Guide, 5th

16SizeClasses

Often,youwantanapplication’sinterfacetohaveadifferentlayoutdependingonthedimensionsandorientationofthescreen.Inthischapter,youwillmodifytheinterfaceforDetailViewControllerinHomepwnersothatwhenitappearsonascreenthathasarelativelysmallheight,thesetoftextfieldsandtheimageviewwillbesidebysideinsteadofstackedontopofoneanother(Figure16.1).

Figure16.1TwopossiblelayoutsforHomepwner’sDetailViewController

Therelativesizesofscreensaredefinedinsizeclasses.Asizeclassrepresentsarelativeamountofscreenspaceinagivendimension.Eachdimension(widthandheight)caneitherbecompactorregular,sotherearefourpossiblecombinationsofsizeclasses:

CompactWidth|CompactHeight

iPhoneswith3.5-,4-,or4.7-inchscreensinlandscapeorientationCompactWidth|RegularHeight

iPhonesofallsizesinportraitorientationRegularWidth|CompactHeight

iPhoneswith5.5-inchscreensinlandscapeorientation

WOW! eBook www.wowebook.org

Page 461: iOS Programming The Big Nerd Ranch Guide, 5th

RegularWidth|RegularHeight

iPadsofallsizesinallorientations

Noticethatthesizeclassescoverbothscreensizesandorientations.Insteadofthinkingaboutinterfacesintermsoforientationordevice,itisbettertothinkintermsofsizeclasses.

Ifyouwantalayouttocovermultiplesizeclasses,thenyoucanusethewildcardAny.AnyWidth|AnyHeightisthedefaultandthemostgeneric.WhenyoueditintheAnyWidth|AnyHeightconfiguration,youareprovidinganinterfacethatiscommonacrossallsizeclasses.ForDetailViewController,youwantalayoutspecifictoalliPhonesinlandscapeorientation,soyouaregoingtodefineitforAnyWidth|CompactHeight.

OpenHomepwner.xcodeprojandMain.storyboard.Selectthe tabtoopenthefileinspector(Command-Option-1)andconfirmthatthecheckboxforUseSizeClassesischecked(Figure16.2).

Figure16.2Sizeclassesenabled

WOW! eBook www.wowebook.org

Page 462: iOS Programming The Big Nerd Ranch Guide, 5th

AnotherSizeClassWheneditingtheinterfaceforaspecificsizeclasscombination,youareabletochange:

propertiesformanyviews

whetheraspecificsubviewisinstalled

whetheraspecificconstraintisinstalled

theconstantofaconstraint

thefontforsubviewsthatdisplaytext

InHomepwner,youaregoingtofocusonthefirstiteminthatlist–adjustingviewpropertiesdependingonthecurrentsizeclassconfiguration.Thegoalistohavetheimageviewbeontherightsideofthelabelsandtextfieldsinacompactheightenvironment.Inaregularheightenvironment,theimageviewwillbebelowthelabelsandtextfields(asitcurrentlyis).Stackviewswillmakethisremarkablyeasy.

Tobegin,youaregoingtoembedtheexistingverticalstackviewwithinanotherstackview.Thiswillmakeiteasytoaddanimageviewtotherightsideofthelabelsandtextfields.Selecttheverticalstackviewandclickthe icontoembedthisstackviewwithinanotherstackview.Withthisnewstackviewselected,opentheAutoLayoutPinmenuandconfigureitasseeninFigure16.3.

Figure16.3Stackviewconstraints

Next,openthenewstackview’sattributesinspector.IncreasetheSpacingtobe8.

Nowyouaregoingtomovetheimageviewfromtheinnerstackviewtotheouterstack

WOW! eBook www.wowebook.org

Page 463: iOS Programming The Big Nerd Ranch Guide, 5th

viewthatyoujustcreated.Thisishowyouwillbeabletohavetheimageviewontherightsideoftherestoftheinterface:inacompactheightenvironment,thestackviewwillsettobehorizontalandtheimageviewwilltakeuptherightsideoftheinterface.

Movingtheimageviewfromonestackviewtotheothercanbealittletricky,soyouaregoingtodoitinafewsteps.

OpenthedocumentoutlineandexpandthesectionfortheDetailViewControllerScene.ExpandtheoutertwostackviewsasseeninFigure16.4.

Figure16.4Expandingthedocumentoutline

DragtheImageViewrightabovethestackviewthatitiscurrentlycontainedwithin(Figure16.5).Thiswillmoveitfromtheinnerstackviewtotheouterstackview.

WOW! eBook www.wowebook.org

Page 464: iOS Programming The Big Nerd Ranch Guide, 5th

Figure16.5Movingtheimageviewtotheouterstackview

Finally,collapsetheinnerstackviewanddragtheImageViewtobebelowitinthestack(Figure16.6).MakesuretheImageViewisindentedatthesamelevelastheinnerstackview.Youmayneedtoupdateframesatthispointtogetridofanywarnings.

Figure16.6Movingtheimageviewbelowtheinnerstackview

Buildandruntheapplication.Confirmthatthebehaviorofthestackviewisunchanged.

Atthispoint,youhaveupdatedeverythingthatiscommontoallsizeclasses.Nextyouwillmodifyspecificsizeclassestochangethelayoutofthecontent.

AtthebottomofInterfaceBuilder,clickonwAnyhAny.Thepop-upthatappearsallowsyoutoselectasizeclass.Theinterfacecanbeconfusing.Thetrickisthatthebottom-rightsquarecoveredbytheblueareatellsyouthesizeclass.Currently,theblueareastretchesrightanddowntodeadcenter,andthelabelatthetopreadsAny(Figure16.7).

WOW! eBook www.wowebook.org

Page 465: iOS Programming The Big Nerd Ranch Guide, 5th

Figure16.7Choosingasizeclasstoedit

Dragtoresizetheblueareatoseetheothersizeclasses.FindandselectCompactHeight(thetop-centerboxinthegrid).Noticethattheviewcontrollerisshorter(Figure16.8).Additionally,thebottomofInterfaceBuilderhasabluebartoremindyouthatyouarenolongereditingthelayoutforallsizeclasses.

WOW! eBook www.wowebook.org

Page 466: iOS Programming The Big Nerd Ranch Guide, 5th

Figure16.8DetailViewControllerintheAny|Compactenvironment

Whatyouwillnowdoisupdatethepropertiesfortheouterstackviewfortheimageviewtobeontherightsideoftherestoftheinterface.

Selecttheouterstackviewandopenitsattributesinspector.UndertheStackViewheading,findtheAxispropertyandclickthe+buttononitsleftside.Fromthepop-upmenu,chooseAnyWidth,thenCompactHeight(Figure16.9).Thiswillallowyoutocustomizethispropertyforjustthecurrentsizeclassconfiguration.

Figure16.9Addingasize-class-specificoption

Forthenewoption(wAnyhC),chooseHorizontal(Figure16.10).Now,whenevertheinterfacehasacompactheight,theouterstackviewwillhaveahorizontalconfiguration.Whentheinterfacehasaregularheight,theouterstackviewwillhaveaverticalconfiguration.

WOW! eBook www.wowebook.org

Page 467: iOS Programming The Big Nerd Ranch Guide, 5th

Figure16.10Customizingtheaxis

Thelastchangeyouwanttomakeisfortheinnerstackviewandtheimageviewtofilltheouterstackviewequally.Todothis,youwillcustomizetheouterstackview’sdistribution.

Withtheattributesinspectorstillopenfortheouterstackview,clickonthe+nexttoDistributionandselectAnyWidth|CompactHeight(current)fromthepop-upmenu.ChangethedistributionforthissizeclasstobeFillEqually(Figure16.11).

Figure16.11Customizingthedistribution

BuildandruntheapplicationoniPhone.Createanitemanddrilldowntoitsdetailstoaddaphoto.Rotatebetweenportraitandlandscapeandnoticehowtheinterfaceislaidoutasyouspecifiedforbothregularandcompactheight.

Withthat,yourHomepwnerapplicationiscomplete.Youhavebuiltanappwithaflexibleinterfacethatcantakephotosandstoredata,andwehopeyouareproudofyouraccomplishment!Takesometimetocelebrate.

WOW! eBook www.wowebook.org

Page 468: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 469: iOS Programming The Big Nerd Ranch Guide, 5th

BronzeChallenge:StackedTextFieldandLabelsInacompactheightenvironment,makeitsothetextfieldsandlabelsarestackedverticallyinsteadofhorizontally(Figure16.12).

Figure16.12Textfieldsandlabelsstacked

WOW! eBook www.wowebook.org

Page 470: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 471: iOS Programming The Big Nerd Ranch Guide, 5th

17TouchEventsandUIResponder

Inthenexttwochapters,youwillcreateTouchTracker,anappthatletstheuserdrawbytouchingthescreen.Inthischapter,youwillcreateaviewthatdrawslinesinresponsetotheuserdraggingacrossit(Figure17.1).Usingmultitouch,theuserwillbeabletodrawmorethanonelineatatime.

Figure17.1TouchTracker

WOW! eBook www.wowebook.org

Page 472: iOS Programming The Big Nerd Ranch Guide, 5th

TouchEventsAsasubclassofUIResponder,aUIViewcanoverridefourmethodstohandlethefourdistincttouchevents:

afingerorfingerstouchesthescreenfunctouchesBegan(touches:Set<UITouch>,withEventevent:UIEvent?)

afingerorfingersmovesacrossthescreen(thismessageissentrepeatedlyasafingermoves)functouchesMoved(touches:Set<UITouch>,withEventevent:UIEvent?)

afingerorfingersisremovedfromthescreenfunctouchesEnded(touches:Set<UITouch>,withEventevent:UIEvent?)

asystemevent,likeanincomingphonecall,interruptsatouchbeforeitendsfunctouchesCancelled(touches:Set<UITouch>?,withEventevent:UIEvent?)

Let’swalkthroughthetypicallifecycleofatouch.Whentheuser’sfingertouchesthescreen,aninstanceofUITouchiscreated.ThetouchesBegan(_:withEvent:)methodiscalledontheUIViewthatthefingertouched,andtheUITouchispassedinthroughtheSetoftouches.

Asthefingermovesaroundthescreen,thetouchobjectisupdatedtocontainthecurrentlocationofthefingeronthescreen.Then,thesameUIViewthatthetouchbeganonissentthemessagetouchesMoved(_:withEvent:).TheSetthatispassedasanargumenttothismethodcontainsthesameUITouchthatoriginallywascreatedwhenthefingeritrepresentstouchedthescreen.

Whenthefingerisremovedfromthescreen,thetouchobjectisupdatedonelasttimetocontainthecurrentlocationofthefingerandtheviewthatthetouchbeganonissentthemessagetouchesEnded(_:withEvent:).Afterthatmethodfinishesexecuting,theUITouchobjectisdestroyed.

Fromthisinformation,youcandrawafewconclusionsabouthowtouchobjectswork:

OneUITouchcorrespondstoonefingeronthescreen.Thistouchobjectlivesaslongasthefingerisonthescreenandalwayscontainsthecurrentpositionofthefingeronthescreen.

Theviewthatthefingerstartedonwillreceiveeverytoucheventmessageforthatfinger.EvenifthefingermovesbeyondtheframeoftheUIViewthatthetouchbeganon,thetouchesMoved(_:withEvent:)andtouchesEnded(_:withEvent:)methodswillstillbecalledonthatview.Thus,ifatouchbeginsonaview,thenthatviewownsthetouchforthelifeofthetouch.

Youdonothaveto–norshouldyouever–keepareferencetoaUITouchobject.Theapplicationwillgiveyouaccesstoatouchobjectviathe

WOW! eBook www.wowebook.org

Page 473: iOS Programming The Big Nerd Ranch Guide, 5th

UIRespondermethodscalledatthedistinctpointsinthetouch’slifecycle.

Everytimeatouchdoessomething–likebegins,moves,orends–atoucheventisaddedtoaqueueofeventsthattheUIApplicationobjectmanages.Inpractice,thequeuerarelyfillsup,andeventsaredeliveredimmediately.ThedeliveryofthesetoucheventsinvolvessendingoneoftheUIRespondermessagestotheviewthatownsthetouch.

Whataboutmultipletouches?Ifmultiplefingersdothesamethingattheexactsametimetothesameview,allofthesetoucheventsaredeliveredatonce.Eachtouchobject–oneforeachfinger–isincludedintheSetpassedasanargumentintheUIRespondermessages.However,thewindowofopportunityforthe“exactsametime”isfairlyshort.So,insteadofonerespondermessagewithallofthetouches,thereareusuallymultiplerespondermessageswithoneormoreofthetouches.

WOW! eBook www.wowebook.org

Page 474: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 475: iOS Programming The Big Nerd Ranch Guide, 5th

CreatingtheTouchTrackerApplicationNowlet’sgetstartedwithyourapplication.InXcode,createanewSingleViewUniversalprojectandnameitTouchTracker(Figure17.2).

Figure17.2CreatingTouchTracker

InbuildingTouchTracker,youaregoingtousethedefaultviewcontrollerandthestoryboardthatthetemplatecreated.Foritsviewandmodellayers,youaregoingtocreateacustomviewclassandacustomstructure.Figure17.3showsthemajorpiecesofTouchTracker.

WOW! eBook www.wowebook.org

Page 476: iOS Programming The Big Nerd Ranch Guide, 5th

Figure17.3ObjectdiagramforTouchTracker

Let’sbeginwithyourcustomstruct.

WOW! eBook www.wowebook.org

Page 477: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 478: iOS Programming The Big Nerd Ranch Guide, 5th

CreatingtheLineStructYouaregoingtocreatethecustomLinetype.Sofar,allofthetypesthatyouhavecreatedhavebeenclasses.Infact,theyhavebeenCocoaTouchsubclasses;forexample,youhavecreatedsubclassesofNSObject,UIViewController,andUIView.

Linewillbeastruct.Youhaveusedstructsthroughoutthisbook–CGRect,CGSize,andCGPointareallstructs.SotooareString,Int,Array,andDictionary.Nowyouaregoingtocreateoneofyourown.

CreateanewSwiftfilenamedLine.

InLine.swift,importCoreGraphicsanddeclaretheLinestruct.DeclaretwoCGPointpropertiesthatwilldeterminethebeginningandendingpointfortheline.importFoundationimportCoreGraphics

structLine{

varbegin=CGPoint.zerovarend=CGPoint.zero}

Structs

Structsdifferfromclassesinanumberofways:

Structsdonotsupportinheritance.

Structsgetamember-wiseinitializerifnootherinitializersaredeclared.Themember-wiseinitializertakesinanargumentforeachpropertywithinthetype.TheLinestruct,forexample,hasthemember-wiseinitializerinit(begin:CGPoint,end:CGPoint).

Ifallpropertieshavedefaultvaluesandnootherinitializersaredeclared,structsalsogainanemptyinitializer(init())thatcreatesaninstanceandsetsallofthepropertiestotheirdefaultvalue.

Perhapsmostimportantly,structs(andenums)arevaluetypes–asopposedtoclasses,whicharereferencetypes.

Valuetypesvs.referencetypes

Valuetypesaretypeswhosevaluesarecopiedwhentheyareassignedtoanotherinstanceorpassedintheargumentofafunction.Thismeansthatassigninganinstanceofavaluetypetoanotheractuallyassignsacopyofthefirstinstancetothesecondinstance.ValuetypesplayanimportantroleinSwift.Forexample,arraysanddictionariesarebothvaluetypes.Allenumsandstructsyouwritearevaluetypesaswell.

Referencetypesarenotcopiedwhentheyareassignedtoaninstanceorpassedintoanargumentofafunction.Instead,areferencetothesameinstanceispassed.Classesand

WOW! eBook www.wowebook.org

Page 479: iOS Programming The Big Nerd Ranch Guide, 5th

closuresarereferencetypes.

Sowhichdoyouchoose?Ingeneral,wesuggeststartingoutwithavaluetypeunlessyouabsolutelyknowyouneedthebenefitsofareferencetype.Valuetypesareeasiertoreasonaboutbecauseyoudonotneedtoworryaboutwhathappenstoaninstancewhenyouchangevaluesonacopy.Ifyouwouldlikeadeeperdiscussiononthistopic,youshouldcheckoutSwiftProgramming:TheBigNerdRanchGuide.

WOW! eBook www.wowebook.org

Page 480: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 481: iOS Programming The Big Nerd Ranch Guide, 5th

CreatingDrawViewInadditiontoacustomstruct,TouchTrackerneedsacustomview.

CreateanewSwiftfilenamedDrawView.InDrawView.swift,definetheDrawViewclass.Addtwoproperties:anoptionalLinetokeeptrackofalinethatispossiblybeingdrawnandanarrayofLinestokeeptrackoflinesthathavebeendrawn.importFoundationimportUIKit

classDrawView:UIView{

varcurrentLine:Line?varfinishedLines=[Line]()

}

AninstanceofDrawViewwillbetheviewoftheapplication’srootViewController,thedefaultViewControllerincludedintheproject.TheviewcontrollerneedstoknowthatitsviewwillbeaninstanceofDrawView.

OpenMain.storyboard.SelecttheViewandopentheidentityinspector(Command-Option-3).UnderCustomClass,changetheClasstoDrawView(Figure17.4).

Figure17.4Changingtheviewclass

WOW! eBook www.wowebook.org

Page 482: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 483: iOS Programming The Big Nerd Ranch Guide, 5th

DrawingwithDrawViewAninstanceofDrawViewneedstobeabledrawlines.YouaregoingtowriteamethodthatusesUIBezierPathtocreateandstrokeapathbasedonthepropertiesofagivenLine.ThenyouwilloverridedrawRect(_:)todrawthelinesinthearrayoffinishedlinesaswellasthecurrentline,ifany.

InDrawView.swift,implementthemethodforstrokinglinesandoverridedrawRect(_:).varcurrentLine:Line?varfinishedLines=[Line]()

funcstrokeLine(line:Line){letpath=UIBezierPath()path.lineWidth=10path.lineCapStyle=CGLineCap.Round

path.moveToPoint(line.begin)path.addLineToPoint(line.end)path.stroke()}

overridefuncdrawRect(rect:CGRect){//DrawfinishedlinesinblackUIColor.blackColor().setStroke()forlineinfinishedLines{strokeLine(line)}

ifletline=currentLine{//Ifthereisalinecurrentlybeingdrawn,doitinredUIColor.redColor().setStroke()strokeLine(line)}}

WOW! eBook www.wowebook.org

Page 484: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 485: iOS Programming The Big Nerd Ranch Guide, 5th

TurningTouchesintoLinesAlineisdefinedbytwopoints.YourLinestoresthesepointsaspropertiesnamedbeginandend.Whenatouchbegins,youwillcreateaLineandsetbothofitspropertiestothepointwherethetouchbegan.Whenthetouchmoves,youwillupdatetheLine’send.Whenthetouchends,youwillhaveyourcompleteLine.

InDrawView.swift,implementtouchesBegan(_:withEvent:)tocreateanewline.overridefunctouchesBegan(touches:Set<UITouch>,withEventevent:UIEvent?){lettouch=touches.first!

//Getlocationofthetouchinview'scoordinatesystemletlocation=touch.locationInView(self)

currentLine=Line(begin:location,end:location)

setNeedsDisplay()}

Thiscodefirstfiguresoutthelocationofthetouchwithintheview’scoordinatesystem.ThenitcallssetNeedsDisplay(),whichflagstheviewtoberedrawnattheendoftherunloop.

Next,stillinDrawView.swift,implementtouchesMoved(_:withEvent:)sothatitupdatestheendofthecurrentLine.overridefunctouchesMoved(touches:Set<UITouch>,withEventevent:UIEvent?){lettouch=touches.first!letlocation=touch.locationInView(self)

currentLine?.end=location

setNeedsDisplay()}

Finally,inDrawView.swift,updatetheendlocationofthecurrentLineandaddittothefinishedLinesarraywhenthetouchends.overridefunctouchesEnded(touches:Set<UITouch>,withEventevent:UIEvent?){ifvarline=currentLine{lettouch=touches.first!letlocation=touch.locationInView(self)line.end=location

finishedLines.append(line)}currentLine=nil

setNeedsDisplay()}

Buildandruntheapplicationanddrawsomelinesonthescreen.Whileyouaredrawing,thelineswillappearinred.Oncefinished,theywillappearinblack.

Handlingmultipletouches

Whendrawinglines,youmayhavenoticedthathavingmorethanonefingeronthescreendoesnotdoanything–thatis,youcanonlydrawonelineatatime.Let’supdate

WOW! eBook www.wowebook.org

Page 486: iOS Programming The Big Nerd Ranch Guide, 5th

DrawViewsothatyoucandrawasmanylinesasyoucanfitfingersonthescreen.

Bydefault,aviewwillonlyacceptonetouchatatime.IfonefingerhasalreadytriggeredtouchesBegan(_:withEvent:)buthasnotfinished–andthereforehasnottriggeredtouchesEnded(_:withEvent:)–subsequenttouchesareignored.Inthiscontext,“ignored”meansthatneithertouchesBegan(_:withEvent:)noranyotherUIRespondermethodrelatedtotheextratoucheswillbecalledontheDrawView.

InMain.storyboard,selecttheDrawViewandopentheattributesinspector.ChecktheboxlabeledMultipleTouch(Figure17.5),whichwillsettheDrawViewinstance’smultipleTouchesEnabledpropertytotrue.

Figure17.5MultipleTouchenabled

NowthatDrawViewwillacceptmultipletouches,eachtimeafingertouchesthescreen,moves,orisremovedfromthescreen,theappropriateUIResponderwillbecalledontheview.However,younowhaveaproblem:yourUIRespondercodeassumestherewillonlybeonetouchactiveandonelinebeingdrawnatatime.

Forexample,eachtouch-handlingmethodasksforthefirstelementinthesetoftouchesitreceives.Inasingle-touchview,therewillonlyeverbeoneobjectintheset,soaskingforanyobjectalwaysreturnsthetouchthattriggeredtheevent.Inamultiple-touchview,thesetcancontainmorethanonetouch.Also,DrawViewhasonlyoneproperty(currentLine)thathangsontoalineinprogress.Obviously,youwillneedtoholdasmanylinesastherearetouchescurrentlyonthescreen.Whileyoucouldcreateafewmoreproperties,likecurrentLine1andcurrentLine2,itwouldbeahassletomanagewhichpropertycorrespondstowhichtouch.

Insteadofaddingmoreproperties,youaregoingreplacethesingleLinewithadictionarycontaininginstancesofLine.InDrawView.swift,addanewpropertytoreplacecurrentLine.classDrawView:UIView{

varcurrentLine:Line?varcurrentLines=[NSValue:Line]()

WOW! eBook www.wowebook.org

Page 487: iOS Programming The Big Nerd Ranch Guide, 5th

ThekeytostorethelineinthedictionarywillbederivedfromtheUITouchobjectthatthelinecorrespondsto.Asmoretoucheventsoccur,youcanusethesamealgorithmtoderivethekeyfromtheUITouchthattriggeredtheeventanduseittolookuptheappropriateLineinthedictionary.

NowyouneedtoupdatetheUIRespondermethodstoaddlinesthatarecurrentlybeingdrawntothisdictionary.InDrawView.swift,updatethecodeintouchesBegan(_:withEvent:).overridefunctouchesBegan(touches:Set<UITouch>,withEventevent:UIEvent?){

lettouch=touches.first!

//Getlocationofthetouchinview'scoordinatesystemletlocation=touch.locationInView(self)currentLine=Line(begin:location,end:location)

//Let'sputinalogstatementtoseetheorderofeventsprint(__FUNCTION__)

fortouchintouches{letlocation=touch.locationInView(self)

letnewLine=Line(begin:location,end:location)

letkey=NSValue(nonretainedObject:touch)currentLines[key]=newLine}

setNeedsDisplay()}

Inthiscode,youfirstprintoutthenameofthemethod,whichisrepresentedbythe__FUNCTION__expression.Atcompiletime,thecompilerwillevaluatethisexpressionandreturnaStringthatrepresentsthenameofthemethod.

Second,youenumerateoverallofthetouchesthatbegan,becauseitispossiblethatmorethanonetouchcanbeginatthesametime.(Typically,touchesbeginatdifferenttimesandtouchesBegan(_:withEvent:)getscalledmultipletimesontheDrawViewforeachtouch.Butyouhavetopreparefortheimprobable,ifnottheimpossible.)

Next,noticetheuseofNSValue(nonretainedObject:)toderivethekeytostoretheLine.ThismethodcreatesanNSValueinstancethatholdsontotheaddressoftheUITouchobjectthatwillbeassociatedwiththisline.SinceaUITouchiscreatedwhenatouchbegins,updatedthroughoutitslifetime,anddestroyedwhenthetouchends,theaddressofthatobjectwillbeconstantthrougheachtouch-event-handlingmethod.Figure17.6showsthenewstateofaffairs.

Youmaybewondering:WhynotusetheUITouchitselfasthekey?WhygothroughthehoopofcreatinganNSValue?ThedocumentationforUITouchsaysthatyoushouldneverretain(inotherwords,keepastrongreferenceto)aUITouchobject.Therefore,youwrapthememoryaddressoftheUITouchinaninstanceofNSValue,usingitsinit(nonretainedObject:)initializer.Thedocumentationforthismethodstates:“Thismethodisusefulifyouwanttoaddanobjecttoacollectionbutdon’twantthecollectiontocreateastrongreferencetoit,”whichisexactlywhatyouwant.SincethesameUITouchobjectisreusedfortheentiretyofthattouch’slifecycle,youcanrecreate

WOW! eBook www.wowebook.org

Page 488: iOS Programming The Big Nerd Ranch Guide, 5th

thesameNSValueusingthesameUITouch.

Figure17.6ObjectdiagramformultitouchTouchTracker

UpdatetouchesMoved(_:withEvent:)inDrawView.swiftsothatitcanlookuptherightLine.overridefunctouchesMoved(touches:Set<NSObject>,withEventevent:UIEvent){

lettouch=touches.first!letlocation=touch.locationInView(self)

currentLine?.end=location

//Let'sputinalogstatementtoseetheorderofeventsprint(__FUNCTION__)

fortouchintouches{letkey=NSValue(nonretainedObject:touch)currentLines[key]?.end=touch.locationInView(self)}

setNeedsDisplay()}

Now,updatetouchesEnded(_:withEvent:)tomoveanyfinishedlinesintothefinishedLinesarray.overridefunctouchesEnded(touches:Set<NSObject>,withEventevent:UIEvent){

ifvarline=currentLine{lettouch=touches.first!letlocation=touch.locationInView(self)line.end=location

finishedLines.append(line)}currentLine=nil

//Let'sputinalogstatementtoseetheorderofeventsprint(__FUNCTION__)

fortouchintouches{letkey=NSValue(nonretainedObject:touch)ifvarline=currentLines[key]{

WOW! eBook www.wowebook.org

Page 489: iOS Programming The Big Nerd Ranch Guide, 5th

line.end=touch.locationInView(self)

finishedLines.append(line)currentLines.removeValueForKey(key)}}

setNeedsDisplay()}

Finally,updatedrawRect(_:)todraweachlineincurrentLines.overridefuncdrawRect(rect:CGRect){

//DrawfinishedlinesinblackUIColor.blackColor().setStroke()forlineinfinishedLines{strokeLine(line)}

ifletline=currentLine{//Ifthereisalinecurrentlybeingdrawn,doitinredUIColor.redColor().setStroke()strokeLine(line)}

//DrawcurrentlinesinredUIColor.redColor().setStroke()for(_,line)incurrentLines{strokeLine(line)}}

Buildandruntheapplicationandstartdrawinglineswithmultiplefingers.(Onthesimulator,holddowntheOptionkeywhileyoudragtosimulatemultiplefingers.)Noticetheorderingofthelogmessagesintheconsole.

YoushouldknowthatwhenaUIRespondermethodliketouchesMoved(_:withEvent:)iscalledonaview,onlythetouchesthathavemovedwillbeinthesetoftouches.Thus,itispossibleforthreetouchestobeonaview,butonlyonetouchtobeinthesetoftouchespassedintooneofthesemethods.Additionally,onceaUITouchbeginsonaview,alltoucheventmethodsarecalledonthatsameviewoverthetouch’slifetime,evenifthattouchmovesoffoftheviewitbeganon.

ThelastthingleftforthebasicsofTouchTrackeristohandlewhathappenswhenatouchiscanceled.AtouchcanbecanceledwhentheapplicationisinterruptedbytheOS(forexample,whenaphonecallcomesin)whileatouchiscurrentlyonthescreen.Whenatouchiscanceled,anystateitsetupshouldbereverted.Inthiscase,youshouldremoveanylinesinprogress.

InDrawView.swift,implementtouchesCancelled(_:withEvent:).overridefunctouchesCancelled(touches:Set<UITouch>?,withEventevent:UIEvent?){

//Let'sputinalogstatementtoseetheorderofeventsprint(__FUNCTION__)

currentLines.removeAll()

setNeedsDisplay()}

WOW! eBook www.wowebook.org

Page 490: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 491: iOS Programming The Big Nerd Ranch Guide, 5th

@IBInspectableWhenworkinginInterfaceBuilder,youareabletomodifyattributesfortheviewsthatyouaddtothecanvas.Forexample,youcansetthebackgroundcoloronaview,thetextonalabel,andthecurrentprogressonaslider.YoucanaddthissamebehaviortoyourowncustomUIViewsubclassesforcertaintypes.Let’saddintheabilityfortheDrawView’scurrentlinecolor,finishedlinecolor,andlinethicknesstobecustomizedthroughInterfaceBuilder.

InDrawView.swift,declarethreepropertiestoreferencethesevalues.Givethemdefaultvaluesandhavetheviewflagitselfforredrawingwheneverthesepropertieschange.varcurrentLines=[NSValue:Line]()varfinishedLines=[Line]()

@IBInspectablevarfinishedLineColor:UIColor=UIColor.blackColor(){didSet{setNeedsDisplay()}}

@IBInspectablevarcurrentLineColor:UIColor=UIColor.redColor(){didSet{setNeedsDisplay()}}

@IBInspectablevarlineThickness:CGFloat=10{didSet{setNeedsDisplay()}}

The@IBInspectablekeywordletsInterfaceBuilderknowthatthisisapropertythatyouwanttocustomizethroughtheattributesinspector.Manyofthecommontypesaresupportedby@IBInspectable:Booleans,strings,numbers,CGPoint,CGSize,CGRect,UIColor,UIImage,andafewmoreareallcandidates.

NowupdatestrokeLine(_:)anddrawView(_:)tousethesenewproperties.funcstrokeLine(line:Line){letpath=UIBezierPath()path.lineWidth=10path.lineWidth=lineThicknesspath.lineCapStyle=CGLineCap.Round

path.moveToPoint(line.begin)path.addLineToPoint(line.end)path.stroke()}

overridefuncdrawRect(rect:CGRect){//DrawfinishedlinesinblackUIColor.blackColor().setStroke()finishedLineColor.setStroke()forlineinfinishedLines{strokeLine(line)}

//DrawcurrentlinesinredUIColor.redColor().setStroke()currentLineColor.setStroke()for(_,line)incurrentLines{strokeLine(line)

WOW! eBook www.wowebook.org

Page 492: iOS Programming The Big Nerd Ranch Guide, 5th

}}

NowwhenyouaddaDrawViewtothecanvasinInterfaceBuilder,youcancustomizethesethreepropertiesintheattributesinspectortobedifferentfordifferentinstances(Figure17.7).

Figure17.7CustomizingDrawView

WOW! eBook www.wowebook.org

Page 493: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 494: iOS Programming The Big Nerd Ranch Guide, 5th

SilverChallenge:ColorsMakeitsotheangleatwhichalineisdrawndictatesitscoloronceithasbeenaddedtocurrentLines.

WOW! eBook www.wowebook.org

Page 495: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 496: iOS Programming The Big Nerd Ranch Guide, 5th

GoldChallenge:CirclesUsetwofingerstodrawcircles.Tryhavingeachfingerrepresentonecorneroftheboundingboxaroundthecircle.RecallthatyoucansimulatetwofingersonthesimulatorbyholdingdowntheOptionkey.(Hint:thisismucheasierifyoutracktouchesthatareworkingonacircleinaseparatedictionary.)

WOW! eBook www.wowebook.org

Page 497: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 498: iOS Programming The Big Nerd Ranch Guide, 5th

FortheMoreCurious:TheResponderChainInChapter13,wetalkedbrieflyaboutthefirstresponder.AUIRespondercanbeafirstresponderandreceivetouchevents.UIViewisoneexampleofaUIRespondersubclass,buttherearemanyothers,includingUIViewController,UIApplication,andUIWindow.Youareprobablythinking,“Butyoucan’ttouchaUIViewController.It’snotanonscreenobject.”Youareright–youcannotsendatoucheventdirectlytoaUIViewController,butviewcontrollerscanreceiveeventsthroughtheresponderchain.

EveryUIResponderhasamethodcallednextResponder(),andtogethertheseobjectsmakeuptheresponderchain(Figure17.8).Atoucheventstartsattheviewthatwastouched.ThenextResponderofaviewistypicallyitsUIViewController(ifithasone)oritssuperview(ifitdoesnot).ThenextResponder()ofaviewcontrolleristypicallyitsview’ssuperview.Thetop-mostsuperviewisthewindow.Thewindow’snextResponder()isthesingletoninstanceofUIApplication.

Figure17.8Responderchain

HowdoesaUIRespondernothandleanevent?ItforwardsthesamemessagetoitsnextResponder().ThatiswhatthedefaultimplementationofmethodsliketouchesBegan(_:withEvent:)do.Soifamethodisnotoverridden,itsnextresponderwillattempttohandlethetouchevent.Iftheapplication(thelastobjectintheresponderchain)doesnothandletheevent,thenitisdiscarded.

Youcanexplicitlysendamessagetoanextresponder,too.Let’ssaythereisaviewthattrackstouches,butifadouble-tapoccurs,itsnextrespondershouldhandleit.Thecodewouldlooklikethis:overridefunctouchesBegan(touches:Set<UITouch>,withEventevent:UIEvent?){lettouch=touches.first!iftouch.tapCount==2{nextResponder()?.touchesBegan(touches,withEvent:event)}

WOW! eBook www.wowebook.org

Page 499: iOS Programming The Big Nerd Ranch Guide, 5th

else{//Goontohandletouchesthatarenotdouble-taps}}

WOW! eBook www.wowebook.org

Page 500: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 501: iOS Programming The Big Nerd Ranch Guide, 5th

FortheMoreCurious:UIControlTheclassUIControlisthesuperclassforseveralclassesinCocoaTouch,includingUIButtonandUISlider.Youhaveseenhowtosetthetargetsandactionsforthesecontrols.NowwecantakeacloserlookathowUIControloverridesthesameUIRespondermethodsyouimplementedinthischapter.

InUIControl,eachpossiblecontroleventisassociatedwithaconstant.Buttons,forexample,typicallysendactionmessagesontheUIControlEvents.TouchUpInsidecontrolevent.Atargetregisteredforthiscontroleventwillonlyreceiveitsactionmessageiftheusertouchesthecontrolandthenliftsthefingeroffthescreeninsidetheframeofthecontrol.

Forabutton,however,youcanhaveactionsonothereventtypes.Forexample,youmighttriggeramethodiftheuserremovesthefingerinsideoroutsidetheframe.Assigningthetargetandactionprogrammaticallywouldlooklikethis:button.addTarget(self,action:"resetTemperature:",forControlEvents:[.TouchUpInside,.TouchUpOutside])

NowconsiderhowUIControlhandlesUIControlEvents.TouchUpInside.//Nottheexactcode.Thereisabitmoregoingon!overridefunctouchesEnded(touches:Set<UITouch>,withEventevent:UIEvent?){

//Referencetothetouchthatisendinglettouch=touches.first!

//Locationofthatpointinthiscontrol'scoordinatesystemlettouchLocation=touch.locationInView(self)

//Isthatpointstillinmyviewingbounds?ifself.bounds.contains(touchLocation){//Sendoutactionmessagestoalltargetsregisteredforthisevent!sendActionsForControlEvents(.TouchUpInside)}else{//Thetouchendedoutsidethebounds:differentcontroleventsendActionsForControlEvents(.TouchUpOutside)}}

Sohowdotheseactionsgetsenttotherighttarget?AttheendoftheUIRespondermethodimplementations,thecontrolsendsthemessagesendActionsForControlEvents(_:)toitself.Thismethodlooksatallofthetarget-actionpairsthecontrolhas.Ifanyofthemareregisteredforthecontroleventpassedastheargument,thosetargetsaresentanactionmessage.

However,acontrolneversendsamessagedirectlytoitstargets.Instead,itroutesthesemessagesthroughtheUIApplicationobject.Whynothavecontrolssendtheactionmessagesdirectlytothetargets?Controlscanalsohavenil-targetedactions.IfaUIControl’stargetisnil,theUIApplicationfindsthefirstresponderofitsUIWindowandsendstheactionmessagetoit.

WOW! eBook www.wowebook.org

Page 502: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 503: iOS Programming The Big Nerd Ranch Guide, 5th

18UIGestureRecognizerand

UIMenuControllerInChapter17,youhandledrawtouchesbyimplementingmethodsfromUIResponder.Sometimesyouwanttodetectaspecificpatternoftouchesthatmakeagesture,likeapinchoraswipe.Insteadofwritingcodetodetectcommongesturesyourself,youcanuseinstancesofUIGestureRecognizer.

AUIGestureRecognizerinterceptstouchesthatareontheirwaytobeinghandledbyaview.Whenitrecognizesaparticulargesture,itcallsamethodontheobjectofyourchoice.ThereareseveraltypesofgesturerecognizersbuiltintotheSDK.Inthischapter,youwillusethreeofthemtoallowTouchTrackeruserstoselect,move,anddeletelines(Figure18.1).YouwillalsoseehowtouseanotherinterestingiOSclass,UIMenuController.

WOW! eBook www.wowebook.org

Page 504: iOS Programming The Big Nerd Ranch Guide, 5th

Figure18.1TouchTrackerbytheendofthechapter

WOW! eBook www.wowebook.org

Page 505: iOS Programming The Big Nerd Ranch Guide, 5th

UIGestureRecognizerSubclassesYoudonotinstantiateUIGestureRecognizeritself.Instead,thereareanumberofsubclassesofUIGestureRecognizer,andeachoneisresponsibleforrecognizingaparticulargesture.

TouseaninstanceofaUIGestureRecognizersubclass,yougiveitatarget-actionpairandattachittoaview.Wheneverthegesturerecognizerrecognizesitsgestureontheview,itwillsendtheactionmessagetoitstarget.AllUIGestureRecognizeractionmessageshavethesameform:funcaction(gestureRecognizer:UIGestureRecognizer){}

Whenrecognizingagesture,thegesturerecognizerinterceptsthetouchesdestinedfortheview(Figure18.2).Thus,thetypicalUIRespondermethodsliketouchesBegan(_:withEvent:)maynotbecalledonaviewwithgesturerecognizers.

Figure18.2Gesturerecognizersintercepttouches

WOW! eBook www.wowebook.org

Page 506: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 507: iOS Programming The Big Nerd Ranch Guide, 5th

DetectingTapswithUITapGestureRecognizerThefirstUIGestureRecognizersubclassyouwilluseisUITapGestureRecognizer.Whentheusertapsthescreentwice,allofthelinesonthescreenwillbecleared.

OpenTouchTracker.xcodeprojfromChapter17andDrawView.swift.Addaninit?(coder:)methodandinstantiateaUITapGestureRecognizerthatrequirestwotapstofireandcalltheactionmethodonitstarget.requiredinit?(coderaDecoder:NSCoder){super.init(coder:aDecoder)

letdoubleTapRecognizer=UITapGestureRecognizer(target:self,action:"doubleTap:")doubleTapRecognizer.numberOfTapsRequired=2addGestureRecognizer(doubleTapRecognizer)}

Nowwhenadouble-tapoccursonaninstanceofDrawView,themethoddoubleTap(_:)willbecalledonthatinstance.ImplementthismethodinDrawView.swift.funcdoubleTap(gestureRecognizer:UIGestureRecognizer){print("Recognizedadoubletap")

currentLines.removeAll(keepCapacity:false)finishedLines.removeAll(keepCapacity:false)setNeedsDisplay()}

NoticethattheargumenttotheactionmethodforagesturerecognizeristheinstanceofUIGestureRecognizerthatcalledthemethod.Inthecaseofadouble-tap,youdonotneedanyinformationfromtherecognizer,butyouwillneedinformationfromtheotherrecognizersyouinstalllaterinthechapter.

Buildandruntheapplication,drawafewlines,anddouble-tapthescreentoclearthem.

Youmayhavenoticed(especiallyonthesimulator)thatthefirsttapofadouble-tapresultsinasmallreddotbeingdrawn.ThisdotappearsbecausetouchesBegan(_:withEvent:)iscalledontheDrawViewonthefirsttap,creatingaveryshortline.Checktheconsoleandyouwillseethefollowingsequenceofevents:touchesBegan(_:withEvent:)RecognizedadoubletaptouchesCancelled(_:withEvent:)

Gesturerecognizersworkbyinspectingtoucheventstodetermineiftheirparticulargesturehasoccurred.Beforeagestureisrecognized,thegesturerecognizerinterceptsalltheUIRespondermethodcalls.Ifithasnotrecognizeditsgesture,eachcallisforwardedontotheview.

Recognizingataprequiresthatatouchbeginandend.ThismeansthattheUITapGestureRecognizercannotknowwhetherthetouchisatapwhentouchesBegan(_:withEvent:)isoriginallycalled,sothemethodiscalledontheviewaswell.Whenthetouchends,thetapisrecognizedandthegesturerecognizerclaims

WOW! eBook www.wowebook.org

Page 508: iOS Programming The Big Nerd Ranch Guide, 5th

thetouchforitself.ItdoessobycallingtouchesCancelled(_:withEvent:)ontheview.Afterthat,nomoreUIRespondermethodswillbecalledontheviewforthatparticulartouch.

Topreventthisreddotfromappearingtemporarily,youmustpreventtouchesBegan(_:withEvent:)frombeingcalledontheview.YoucantellaUIGestureRecognizertodelaycallingtouchesBegan(_:withEvent:)onitsviewifitisstillpossiblethatitsgesturemightberecognizedforthattouch.

InDrawView.swift,modifyinit?(coder:)todojustthis.requiredinit?(coderaDecoder:NSCoder){super.init(coder:aDecoder)

letdoubleTapRecognizer=UITapGestureRecognizer(target:self,action:"doubleTap:")doubleTapRecognizer.numberOfTapsRequired=2doubleTapRecognizer.delaysTouchesBegan=trueaddGestureRecognizer(doubleTapRecognizer)}

Buildandruntheapplication,drawsomelines,andthendouble-taptoclearthem.Youwillnolongerseethereddotwhiledouble-tapping.

WOW! eBook www.wowebook.org

Page 509: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 510: iOS Programming The Big Nerd Ranch Guide, 5th

MultipleGestureRecognizersThenextstepistoaddanothergesturerecognizerthatallowstheusertoselectaline.(Later,auserwillbeabletodeletetheselectedline.)YouwillinstallanotherUITapGestureRecognizerontheDrawViewthatonlyrequiresonetap.

InDrawView.swift,modifyinit?(coder:)toaddthisgesturerecognizer.requiredinit?(coderaDecoder:NSCoder){super.init(coder:aDecoder)

letdoubleTapRecognizer=UITapGestureRecognizer(target:self,action:"doubleTap:")doubleTapRecognizer.numberOfTapsRequired=2doubleTapRecognizer.delaysTouchesBegan=trueaddGestureRecognizer(doubleTapRecognizer)

lettapRecognizer=UITapGestureRecognizer(target:self,action:"tap:")tapRecognizer.delaysTouchesBegan=trueaddGestureRecognizer(tapRecognizer)}

Now,implementtap(_:)inDrawView.swifttologthetaptotheconsole.functap(gestureRecognizer:UIGestureRecognizer){print("Recognizedatap")}

Buildandruntheapplication.Trytappinganddouble-tapping.Tappingoncelogstheappropriatemessagetotheconsole.Double-tapping,however,triggersbothtap(_:)anddoubleTap(_:).

Insituationswhereyouhavemultiplegesturerecognizers,itisnotuncommontohaveonegesturerecognizerfireandclaimatouchwhenyoureallywantedanothergesturerecognizertohandleit.Inthesecases,yousetupdependenciesbetweenrecognizersthatsay,“Waitamomentbeforeyoufire,becausethistouchmightbemine!”

Ininit?(coder:),makeitsothetapRecognizerwaitsuntilthedoubleTapRecognizerfailstorecognizeadouble-tapbeforeclaimingthesingletapforitself.requiredinit?(coderaDecoder:NSCoder){super.init(coder:aDecoder)

letdoubleTapRecognizer=UITapGestureRecognizer(target:self,action:"doubleTap:")doubleTapRecognizer.numberOfTapsRequired=2doubleTapRecognizer.delaysTouchesBegan=trueaddGestureRecognizer(doubleTapRecognizer)

lettapRecognizer=UITapGestureRecognizer(target:self,action:"tap:")tapRecognizer.delaysTouchesBegan=truetapRecognizer.requireGestureRecognizerToFail(doubleTapRecognizer)addGestureRecognizer(tapRecognizer)}

Buildandruntheapplicationagainandtryoutsometaps.Asingletapnowtakesasmallamountoftimetofireafterthetapoccurs,butdouble-tappingnolongertriggersthetap(_:)message.

Next,let’sbuildontheDrawViewsothattheusercanselectalinewhenitistapped.First,addapropertyatthetopofDrawView.swifttoholdontotheindexofaselected

WOW! eBook www.wowebook.org

Page 511: iOS Programming The Big Nerd Ranch Guide, 5th

line.classDrawView:UIView{

varcurrentLines=[NSValue:Line]()varfinishedLines=[Line]()varselectedLineIndex:Int?

NowmodifydrawRect(_:)todrawtheselectedlineingreen.overridefuncdrawRect(rect:CGRect){finishedLineColor.setStroke()forlineinfinishedLines{strokeLine(line)}

currentLineColor.setStroke()for(_,line)incurrentLines{strokeLine(line)}

ifletindex=selectedLineIndex{UIColor.greenColor().setStroke()letselectedLine=finishedLines[index]strokeLine(selectedLine)}}

InDrawView.swift,addanindexOfLineAtPoint(_:)methodthatreturnstheindexoftheLineclosesttoagivenpoint.funcindexOfLineAtPoint(point:CGPoint)->Int?{

//Findalineclosetopointfor(index,line)infinishedLines.enumerate(){letbegin=line.beginletend=line.end

//CheckafewpointsonthelinefortinCGFloat(0).stride(to:1.0,by:0.05){letx=begin.x+((end.x-begin.x)*t)lety=begin.y+((end.y-begin.y)*t)

//Ifthetappedpointiswithin20points,let'sreturnthislineifhypot(x-point.x,y-point.y)<20.0{returnindex}}}

//Ifnothingiscloseenoughtothetappedpoint,thenwedidnotselectalinereturnnil}

Thestridemethodwillallowttostartat0andgoupto(butnotreach)thetovalue,incrementingthevalueoftbythebyvalue.Itisanalogoustothefollowingcode:forvart:CGFloat=0;t<1.0;t+=0.05{...}

Thereareother,betterwaystodeterminetheclosestlinetoapoint,butthissimpleimplementationwillworkforyourpurposes.

Thepointtobepassedinisthepointwherethetapoccurred.Youcaneasilygetthisinformation.EveryUIGestureRecognizerhasalocationInView(_:)method.Callingthismethodonthegesturerecognizerwillgiveyouthecoordinatewherethegestureoccurredinthecoordinatesystemoftheviewthatispassedastheargument.

WOW! eBook www.wowebook.org

Page 512: iOS Programming The Big Nerd Ranch Guide, 5th

InDrawView.swift,updatetap(_:)tocalllocationInView(_:)onthegesturerecognizer,passtheresulttoindexOfLineAtPoint(_:),andmakethereturnedindextheselectedLineIndex.functap(gestureRecognizer:UIGestureRecognizer){print("Recognizedatap")

letpoint=gestureRecognizer.locationInView(self)selectedLineIndex=indexOfLineAtPoint(point)

setNeedsDisplay()}

Iftheuserdouble-tapstoclearalllineswhilealineisselected,theapplicationwilltrap.Toaddressthis,updatedoubleTap(_:)tosettheselectedLineIndextonil.funcdoubleTap(gestureRecognizer:UIGestureRecognizer){print("Recognizedadoubletap")

selectedLineIndex=nilcurrentLines.removeAll(keepCapacity:false)finishedLines.removeAll(keepCapacity:false)setNeedsDisplay()}

Buildandruntheapplication.Drawafewlinesandthentapone.Thetappedlineshouldappearingreen,butrememberthatittakesamomentbeforethetapisknownnottobeadouble-tap.

WOW! eBook www.wowebook.org

Page 513: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 514: iOS Programming The Big Nerd Ranch Guide, 5th

UIMenuControllerNextyouaregoingtomakeitsothatwhentheuserselectsaline,amenuwiththeoptiontodeletethatlineappearswheretheusertapped.Thereisabuilt-inclassforprovidingthissortofmenucalledUIMenuController(Figure18.3).AmenucontrollerhasalistofUIMenuItemobjectsandispresentedinanexistingview.Eachitemhasatitle(whatshowsupinthemenu)andanaction(themessageitsendsthefirstresponderofthewindow).

Figure18.3AUIMenuController

ThereisonlyoneUIMenuControllerperapplication.Whenyouwishtopresentthisinstance,youfillitwithmenuitems,giveitarectangletopresentfrom,andsetittobevisible.

DothisinDrawView.swift’stap(_:)methodiftheuserhastappedonaline.Iftheusertappedsomewherethatisnotnearaline,thecurrentlyselectedlinewillbedeselectedandthemenucontrollerwillhide.functap(gestureRecognizer:UIGestureRecognizer){print("Recognizedatap")

letpoint=gestureRecognizer.locationInView(self)selectedLineIndex=indexOfLineAtPoint(point)

//Grabthemenucontrollerletmenu=UIMenuController.sharedMenuController()

ifselectedLineIndex!=nil{

//MakeDrawViewthetargetofmenuitemactionmessagesbecomeFirstResponder()

//Createanew"Delete"UIMenuItemletdeleteItem=UIMenuItem(title:"Delete",action:"deleteLine:")menu.menuItems=[deleteItem]

//Tellthemenuwhereitshouldcomefromandshowitmenu.setTargetRect(CGRect(x:point.x,y:point.y,width:2,height:2),inView:self)menu.setMenuVisible(true,animated:true)}else{//Hidethemenuifnolineisselectedmenu.setMenuVisible(false,animated:true)}

setNeedsDisplay()}

Foramenucontrollertoappear,aviewthatrespondstoatleastoneactionmessageintheUIMenuController’smenuitemsmustbethefirstresponderofthewindow–thisiswhyyoucalledthemethodbecomeFirstResponder()ontheDrawViewbeforesettingupthemenucontroller.

WOW! eBook www.wowebook.org

Page 515: iOS Programming The Big Nerd Ranch Guide, 5th

Ifyouhaveacustomviewclassthatneedstobecomethefirstresponder,youmustalsooverridecanBecomeFirstResponder().InDrawView.swift,overridethismethodtoreturntrue.overridefunccanBecomeFirstResponder()->Bool{returntrue}

Youcanbuildandruntheapplicationnow,butwhenyouselectaline,themenuwillnotappear.Whenbeingpresented,themenucontrollergoesthrougheachmenuitemandasksthefirstresponderifitimplementstheactionmessageforthatitem.Ifthefirstresponderdoesnotimplementthatmethod,thenthemenucontrollerwillnotshowtheassociatedmenuitem.Ifnomenuitemshavetheiractionmessagesimplementedbythefirstresponder,themenuisnotshownatall.

TogettheDeletemenuitem(andthemenuitself)toappear,implementdeleteLine(_:)inDrawView.swift.funcdeleteLine(sender:AnyObject){//RemovetheselectedlinefromthelistoffinishedLinesifletindex=selectedLineIndex{finishedLines.removeAtIndex(index)selectedLineIndex=nil

//RedraweverythingsetNeedsDisplay()}}

Buildandruntheapplication.Drawaline,taponit,andthenselectDeletefromthemenuitem.

Ifyouselectalineandthendouble-taptoclearalllines,themenucontrollerwillstillbevisible.IftheselectedLineIndexeverbecomesnil,themenucontrollershouldnotbevisible.

AddapropertyobservertoselectedLineIndexthatsetsthemenucontrollertobenotvisibleiftheindexissettonil.varselectedLineIndex:Int?{didSet{ifselectedLineIndex==nil{letmenu=UIMenuController.sharedMenuController()menu.setMenuVisible(false,animated:true)}}}

Buildandruntheapplication.Drawaline,selectit,andthendouble-tapthebackground.Thelinesandthemenucontrollerwillnolongerbevisible.

WOW! eBook www.wowebook.org

Page 516: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 517: iOS Programming The Big Nerd Ranch Guide, 5th

MoreGestureRecognizersInthissection,youaregoingtoaddtheabilityforausertoselectalinebypressingandholding(alongpress)andthenmovetheselectedlinebydraggingthefinger(apan).ThiswillrequiretwomoresubclassesofUIGestureRecognizer:UILongPressGestureRecognizerandUIPanGestureRecognizer.

UILongPressGestureRecognizer

InDrawView.swift,instantiateaUILongPressGestureRecognizerininit?(coder:)andaddittotheDrawView....

addGestureRecognizer(tapRecognizer)

letlongPressRecognizer=UILongPressGestureRecognizer(target:self,action:"longPress:")addGestureRecognizer(longPressRecognizer)}

NowwhentheuserholdsdownontheDrawView,themethodlongPress(_:)willbecalledonit.Bydefault,atouchmustbeheld0.5secondstobecomealongpress,butyoucanchangetheminimumPressDurationofthegesturerecognizerifyoulike.

Sofar,youhaveworkedwithtapgestures.Atapisadiscretegesture.Bythetimeitisrecognized,thegestureisover,andtheactionmessagehasbeendelivered.Alongpress,ontheotherhand,isacontinuousgesture.Continuousgesturesoccurovertime.Tokeeptrackofwhatisgoingonwithacontinuousgesture,youcancheckarecognizer’sstateproperty.

Forexample,consideratypicallongpress:

Whentheusertouchesaview,thelongpressrecognizernoticesapossiblelongpressbutmustwaittoseewhetherthetouchisheldlongenoughtobecomealongpressgesture.Therecognizer’sstateisUIGestureRecognizerState.Possible.

Oncetheuserholdsthetouchlongenough,thelongpressisrecognizedandthegesturehasbegun.Therecognizer’sstateisUIGestureRecognizerState.Began.

Whentheuserremovesthefinger,thegesturehasended.Therecognizer’sstateisUIGestureRecognizerState.Ended.

Whenthelong-pressgesturerecognizertransitionsfrompossibletobeganandfrombegantoended,itsendsitsactionmessagetoitstarget.Todeterminewhichtransitiontriggeredtheaction,youcheckthegesturerecognizer’sstate.

Rememberthatthelongpressispartofalargerfeature.Inthenextsection,youwillenabletheusertomovetheselectedlinebydraggingitwiththesamefingerthatbeganthelongpress.SohereistheplanforimplementingthelongPress(_:)actionmethod:

WOW! eBook www.wowebook.org

Page 518: iOS Programming The Big Nerd Ranch Guide, 5th

Whentherecognizerisinthebeganstate,youwillselecttheclosestlinetowherethegestureoccurred.Whentherecognizerisintheendedstate,youwilldeselecttheline.

InDrawView.swift,implementlongPress(_:).funclongPress(gestureRecognizer:UIGestureRecognizer){print("Recognizedalongpress")

ifgestureRecognizer.state==.Began{letpoint=gestureRecognizer.locationInView(self)selectedLineIndex=indexOfLineAtPoint(point)

ifselectedLineIndex!=nil{currentLines.removeAll(keepCapacity:false)}}elseifgestureRecognizer.state==.Ended{selectedLineIndex=nil}

setNeedsDisplay()}

Buildandruntheapplication.Drawalineandthenpressandholdit;thelinewillturngreenandbecometheselectedline.Whenyouletgo,thelinewillreverttoitsformercolorandwillnolongerbetheselectedline.

UIPanGestureRecognizerandsimultaneousrecognizers

InDrawView.swift,declareaUIPanGestureRecognizerasapropertysothatyouhaveaccesstoitinallofyourmethods.classDrawView:UIView{

varcurrentLines=[NSValue:Line]()varfinishedLines=[Line]()varselectedLineIndex:Int?varmoveRecognizer:UIPanGestureRecognizer!

Next,inDrawView.swift,addcodetoinit?(coder:)toinstantiateaUIPanGestureRecognizer,setoneofitsproperties,andaddittotheDrawView.letlongPressRecognizer=UILongPressGestureRecognizer(target:self,action:"longPress:")addGestureRecognizer(longPressRecognizer)

moveRecognizer=UIPanGestureRecognizer(target:self,action:"moveLine:")moveRecognizer.cancelsTouchesInView=falseaddGestureRecognizer(moveRecognizer)}

WhatiscancelsTouchesInView?EveryUIGestureRecognizerhasthisproperty,whichdefaultstotrue.WhencancelsTouchesInViewistrue,thegesturerecognizerwill“eat”anytouchitrecognizes,andtheviewwillnotgetachancetohandlethetouchviathetraditionalUIRespondermethods,liketouchesBegan(_:withEvent:).

Usually,thisiswhatyouwant,butnotalways.Inthiscase,ifthepangesturerecognizerweretoeatitstouches,thenuserswouldnotbeabletodrawlines.WhenyousetcancelsTouchesInViewtofalse,youensurethatanytouchrecognizedbythegesturerecognizerwillalsobedeliveredtotheviewviatheUIRespondermethods.

WOW! eBook www.wowebook.org

Page 519: iOS Programming The Big Nerd Ranch Guide, 5th

InDrawView.swift,addasimpleimplementationfortheactionmethod:funcmoveLine(gestureRecognizer:UIPanGestureRecognizer){print("Recognizedapan")}

Buildandruntheappanddrawsomelines.BecausecancelsTouchesInViewisfalse,thepangestureisrecognized,butlinescanstillbedrawn.YoucancommentoutthelinethatsetscancelsTouchesInViewandrunagaintoseethedifference.

Soon,youwillupdatemoveLine(_:)toredrawtheselectedlineastheuser’sfingermovesacrossthescreen.Butfirstyouneedtwogesturerecognizerstobeabletohandlethesametouch.Normally,whenagesturerecognizerrecognizesitsgesture,iteatsitandnootherrecognizergetsachancetohandlethattouch.Tryit:runtheapp,drawaline,pressandholdtoselecttheline,andthenmoveyourfingeraround.Theconsolereportsthelongpressbutnotthepan.

Inyourcase,thedefaultbehaviorisproblematic:youruserswillpressandholdtoselectalineandthenpantomovetheline-withoutliftingthefingerinbetween.Thus,thetwogestureswilloccursimultaneously,andthepangesturerecognizermustbeallowedtorecognizeapaneventhoughthelongpressgesturehasalreadyrecognizedalongpress.

Toallowagesturerecognizertorecognizeitsgesturesimultaneouslywithanothergesturerecognizer,youimplementamethodfromtheUIGestureRecognizerDelegateprotocol:optionalfuncgestureRecognizer(_gestureRecognizer:UIGestureRecognizer,shouldRecognizeSimultaneouslyWithGestureRecognizerotherGestureRecognizer:UIGestureRecognizer)->Bool

Thefirstparameteristhegesturerecognizerthatisaskingforguidance.Itsaystoitsdelegate,“Sothere’smeandthisotherrecognizer,andoneofusjustrecognizedagesture.Shouldtheonewhodidnotrecognizeitstayinthepossiblestateandcontinuetotrackthistouch?”

Notethatthecallitselfdoesnottellyouwhichofthetworecognizershasrecognizeditsgesture-and,thus,whichofthemwillpotentiallybedeprivedofthechancetorecognizeitsgesture.

Bydefault,themethodreturnsfalse,andthegesturerecognizerstillinthepossiblestateleavesthetouchinthehandsofthegesturealreadyintherecognizedstate.Youcanimplementthemethodtoreturntruetoallowbothrecognizerstorecognizetheirgesturesinthesametouch.(Ifyouneedtodeterminewhichofthetworecognizershasrecognizeditsgesture,youcanchecktherecognizers’stateproperties.)

Toenablepanningwhilelongpressing,youaregoingtogivethepangesturerecognizeradelegate(theDrawView).Then,whenthelongpressrecognizerrecognizesitsgesture,thepangesturerecognizerwillcallthesimultaneousrecognitionmethodonitsdelegate.YouwillimplementthismethodinDrawViewtoreturntrue.Thiswillallowthepangesturerecognizertorecognizeanypanningthatoccurswhilealongpressisinprogress.

First,inDrawView.swift,declarethatDrawViewconformstotheUIGestureRecognizerDelegateprotocol.classDrawView:UIView,UIGestureRecognizerDelegate{

WOW! eBook www.wowebook.org

Page 520: iOS Programming The Big Nerd Ranch Guide, 5th

varcurrentLines=[NSValue:Line]()varfinishedLines=[Line]()varselectedLineIndex:Int?varmoveRecognizer:UIPanGestureRecognizer!

Next,ininit?(coder:),settheDrawViewtobethedelegateoftheUIPanGestureRecognizer.letlongPressRecognizer=UILongPressGestureRecognizer(target:self,action:"longPress:")addGestureRecognizer(longPressRecognizer)

moveRecognizer=UIPanGestureRecognizer(target:self,action:"moveLine:")moveRecognizer.delegate=selfmoveRecognizer.cancelsTouchesInView=falseaddGestureRecognizer(moveRecognizer)}

Finally,inDrawView.swift,implementthedelegatemethodtoreturntrue.funcgestureRecognizer(gestureRecognizer:UIGestureRecognizer,shouldRecognizeSimultaneouslyWithGestureRecognizerotherGestureRecognizer:UIGestureRecognizer)->Bool{returntrue}

Forthissituation,whereonlyyourpangesturerecognizerhasadelegate,thereisnoneedtodomorethanreturntrue.Inmorecomplicatedscenarios,youwouldusethepassed-ingesturerecognizerstomorecarefullycontrolsimultaneousrecognition.

Now,whenalongpressbegins,theUIPanGestureRecognizerwillcontinuetokeeptrackofthetouch,andiftheuser’sfingerbeginstomove,thepanrecognizerwillrecognizethepan.Toseethedifference,runtheapp,drawaline,selectit,andthenpan.Theconsolewillreportbothgestures.

TheUIGestureRecognizerDelegateprotocolincludesothermethodstohelpyoutweakthebehaviorofyourgesturerecognizers.Visittheprotocolreferencepageformoreinformation.

Inadditiontothestatesyouhavealreadyseen,apangesturerecognizersupportsthechangedstate.Whenafingerstartstomove,thepanrecognizerentersthebeganstateandcallsamethodonitstarget.Whilethefingermovesaroundthescreen,therecognizertransitionstothechangedstateandcallstheactionmethodonitstargetrepeatedly.Whenthefingerleavesthescreen,therecognizer’sstateissettoended,andthemethodiscalledonthetargetforthefinaltime.

ThenextstepistoimplementthemoveLine(_:)methodthatthepanrecognizercallsonitstarget.Inthisimplementation,youwillcallthemethodtranslationInView(_:)onthepanrecognizer.ThisUIPanGestureRecognizermethodreturnshowfarthepanhasmovedasaCGPointinthecoordinatesystemoftheviewpassedastheargument.Whenthepangesturebegins,thispropertyissettothezeropoint(wherebothxandyequalzero).Asthepanmoves,thisvalueisupdated–ifthepangoesveryfartotheright,ithasahighxvalue;ifthepanreturnstowhereitbegan,itstranslationgoesbacktothezeropoint.

InDrawView.swift,implementmoveLine(_:).NoticethatbecauseyouwillsendthegesturerecognizeramethodfromtheUIPanGestureRecognizerclass,the

WOW! eBook www.wowebook.org

Page 521: iOS Programming The Big Nerd Ranch Guide, 5th

parameterofthismethodmustbeareferencetoaninstanceofUIPanGestureRecognizerratherthanUIGestureRecognizer.funcmoveLine(gestureRecognizer:UIPanGestureRecognizer){print("Recognizedapan")

//Ifalineisselected...ifletindex=selectedLineIndex{//Whenthepanrecognizerchangesitsposition...ifgestureRecognizer.state==.Changed{//Howfarhasthepanmoved?lettranslation=gestureRecognizer.translationInView(self)

//Addthetranslationtothecurrentbeginningandendpointsoftheline//Makesuretherearenocopyandpastetypos!finishedLines[index].begin.x+=translation.xfinishedLines[index].begin.y+=translation.yfinishedLines[index].end.x+=translation.xfinishedLines[index].end.y+=translation.y

//RedrawthescreensetNeedsDisplay()}}else{//Ifnolineisselected,donotdoanythingreturn}}

Buildandruntheapplication.Touchandholdonalineandbegindragging–andyouwillimmediatelynoticethatthelineandyourfingerarewayoutofsync.Thismakessensebecauseyouareaddingthecurrenttranslationoverandoveragaintotheline’soriginalendpoints.Youreallyneedthegesturerecognizertoreportthechangeintranslationsincethelasttimethismethodwascalledinstead.Fortunately,youcandothis.Youcansetthetranslationofapangesturerecognizerbacktothezeropointeverytimeitreportsachange.Then,thenexttimeitreportsachange,itwillhavethetranslationsincethelastevent.

NearthebottomofmoveLine(_:)inDrawView.swift,addthefollowinglineofcode.finishedLines[index].end.x+=translation.xfinishedLines[index].end.y+=translation.y

gestureRecognizer.setTranslation(CGPoint.zero,inView:self)

//RedrawthescreensetNeedsDisplay()

Buildandruntheapplicationandmovealinearound.Worksgreat!

WOW! eBook www.wowebook.org

Page 522: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 523: iOS Programming The Big Nerd Ranch Guide, 5th

MoreonUIGestureRecognizerYouhaveonlyscratchedthesurfaceofUIGestureRecognizer.Therearemoresubclasses,moreproperties,andmoredelegatemethods,andyoucanevencreaterecognizersofyourown.ThissectionwillgiveyouanideaofwhatUIGestureRecognizeriscapableof.Youcanstudythedocumentationtolearnevenmore.

Whenagesturerecognizerisonaview,itisreallyhandlingalloftheUIRespondermethods,liketouchesBegan(_:withEvent:),foryou.Gesturerecognizersareprettygreedy,sotheytypicallydonotletaviewreceivetoucheventsortheyatleastdelaythedeliveryofthoseevents.Youcansetpropertiesontherecognizer,likedelaysTouchesBegan,delaysTouchesEnded,andcancelsTouchesInView,tochangethisbehavior.Ifyouneedfinercontrolthanthisall-or-nothingapproach,youcanimplementdelegatemethodsfortherecognizer.

Attimes,youmayhavetwogesturerecognizerslookingforverysimilargestures.YoucanchainrecognizerstogethersothatoneisrequiredtofailforthenextonetostartusingthemethodrequireGestureRecognizerToFail(_:).

Onethingyoumustunderstandtomastergesturerecognizersishowtheyinterprettheirstate.Overall,therearesevenstatesarecognizercanenter:

UIGestureRecognizerState.Possible

UIGestureRecognizerState.Failed

UIGestureRecognizerState.Began

UIGestureRecognizerState.Cancelled

UIGestureRecognizerState.Changed

UIGestureRecognizerState.Recognized

UIGestureRecognizerState.Ended

Thepossiblestateiswhererecognizersspendmostoftheirtime.Whenagesturetransitionstoanystateotherthanthepossiblestateorthefailedstate,theactionmessageoftherecognizerissentanditsstatepropertycanbecheckedtoseewhy.

Thefailedstateisusedbyrecognizerswatchingforamultitouchgesture.Atsomepoint,theuser’sfingersmayachieveapositionfromwhichtheycannolongermakethatrecognizer’sgesture.Atthatpoint,thegesturerecognizerfails.Arecognizerentersthecanceledstatewhenitisinterrupted,suchasbyanincomingphonecall.

Ifagestureiscontinuous,likeapan,thegesturerecognizerwillenterthebeganstateandthengointothechangedstateuntilthegestureends.Whenthegestureends(oriscanceled),therecognizerenterstheended(orcanceled)stateandsendsitsactionmessageafinaltimebeforereturningtothepossiblestate.

Forgesturerecognizersthatpickuponadiscretegesturelikeatap,youwillonlyseetheWOW! eBook

www.wowebook.org

Page 524: iOS Programming The Big Nerd Ranch Guide, 5th

recognizedstate(whichhasthesamevalueastheendedstate).

Thefourbuilt-inrecognizersthatyoudidnotimplementinthischapterareUIPinchGestureRecognizer,UISwipeGestureRecognizer,UIScreenEdgePanGestureRecognizer,andUIRotationGestureRecognizer.Eachhaspropertiesthatallowyoutofine-tuneitsbehavior.Thedocumentationwillshowyouhow.

Finally,ifthereisagesturethatyouwanttorecognizethatisnotimplementedbythebuilt-insubclassesofUIGestureRecognizer,youcansubclassUIGestureRecognizeryourself.Thisisanintenseundertakingandoutsidethescopeofthisbook.YoucanreadtheMethodsforSubclassingsectionoftheUIGestureRecognizerdocumentationtolearnwhatisrequired.

WOW! eBook www.wowebook.org

Page 525: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 526: iOS Programming The Big Nerd Ranch Guide, 5th

SilverChallenge:MysteriousLinesThereisabugintheapplication.Ifyoutaponalineandthenstartdrawinganewonewhilethemenuisvisible,youwilldragtheselectedlineanddrawanewlineatthesametime.Fixthisbug.

WOW! eBook www.wowebook.org

Page 527: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 528: iOS Programming The Big Nerd Ranch Guide, 5th

GoldChallenge:SpeedandSizePiggy-backoffofthepangesturerecognizertorecordthevelocityofthepanwhenyouaredrawingaline.Adjustthethicknessofthelinebeingdrawnbasedonthisspeed.Makenoassumptionsabouthowsmallorlargethevelocityvalueofthepanrecognizercanbe.(Inotherwords,logavarietyofvelocitiestotheconsolefirst.)

WOW! eBook www.wowebook.org

Page 529: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 530: iOS Programming The Big Nerd Ranch Guide, 5th

PlatinumChallenge:ColorsHaveathree-fingerswipeupwardbringupapanelthatshowscolors.Selectingoneofthosecolorsshouldmakeanylinesyoudrawafterwardappearinthatcolor.Noextralinesshouldbedrawnbyputtingupthatpanel–oranylinesdrawnshouldbeimmediatelydeletedwhentheapplicationrealizesthatitisdealingwithathree-fingerswipe.

WOW! eBook www.wowebook.org

Page 531: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 532: iOS Programming The Big Nerd Ranch Guide, 5th

FortheMoreCurious:UIMenuControllerandUIResponderStandardEditActionsTheUIMenuControlleristypicallyresponsibleforshowingtheuseran“edit”menuwhenitisdisplayed.(Thinkofatextfieldortextviewwhenyoupressandhold.)Therefore,anunmodifiedmenucontroller(onethatyoudonotsetthemenuitemsfor)alreadyhasdefaultmenuitemsthatitpresents,likeCut,Copy,andotherfamiliaroptions.Eachitemhasanactionmessagewiredup.Forexample,cut:issenttotheviewpresentingthemenucontrollerwhentheCutmenuitemistapped.

AllinstancesofUIResponderimplementthesemethods,but,bydefault,thesemethodsdonotdoanything.SubclasseslikeUITextFieldoverridethesemethodstodosomethingappropriatefortheircontext,likecutthecurrentlyselectedtext.ThemethodsarealldeclaredintheUIResponderStandardEditActionsprotocol.

IfyouoverrideamethodfromUIResponderStandardEditActionsinaview,itsmenuitemwillautomaticallyappearinanymenuyoushowforthatview.ThisworksbecausethemenucontrollercallsthemethodcanPerformAction(_:withSender:)onitsview,whichreturnstrueorfalsedependingonwhethertheviewimplementsthismethod.

Ifyouwanttoimplementoneofthesemethodsbutdonotwantittoappearinthemenu,youcanoverridecanPerformAction(_:withSender:)toreturnfalse.overridefunccanPerformAction(action:Selector,withSendersender:AnyObject?)->Bool{

ifaction=="copy:"{returnfalse}else{//Elsereturnthedefaultbehaviorreturnsuper.canPerformAction(action,withSender:sender)}}

WOW! eBook www.wowebook.org

Page 533: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 534: iOS Programming The Big Nerd Ranch Guide, 5th

19WebServices

Inthenextfourchapters,youwillcreateanapplicationnamedPhotoramathatreadsinalistofrecentphotosfromFlickr.Thischapterwilllaythefoundationandfocusonimplementingthewebservicerequestsresponsibleforfetchingthemetadataforrecentphotosaswellasdownloadingtheimagedataforaspecificphoto.InChapter20,youwilldisplayalloftherecentphotosinagridlayout.Figure19.1showsPhotoramaattheendofthischapter.

Figure19.1Photorama

YourwebbrowserusestheHTTPprotocoltocommunicatewithawebserver.Inthesimplestinteraction,thebrowsersendsarequesttotheserverspecifyingaURL.Theserverrespondsbysendingbacktherequestedpage(typicallyHTMLandimages),whichthebrowserformatsanddisplays.

Inmorecomplexinteractions,browserrequestsincludeotherparameters,suchasformdata.Theserverprocessestheseparametersandreturnsacustomized,ordynamic,webpage.

Webbrowsersarewidelyusedandhavebeenaroundforalongtime,sothetechnologiessurroundingHTTParestableandwelldeveloped:HTTPtrafficpassesneatlythroughmostfirewalls,webserversareverysecureandhavegreatperformance,andwebapplicationdevelopmenttoolshavebecomeeasytouse.

YoucanwriteaclientapplicationforiOSthatleveragestheHTTPinfrastructuretotalktoaweb-enabledserver.Theserversideofthisapplicationisawebservice.YourclientapplicationandthewebservicecanexchangerequestsandresponsesviaHTTP.

WOW! eBook www.wowebook.org

Page 535: iOS Programming The Big Nerd Ranch Guide, 5th

BecausetheHTTPprotocoldoesnotcarewhatdataittransports,theseexchangescancontaincomplexdata.ThisdataistypicallyinJSON(JavaScriptObjectNotation)orXML(ExtensibleMarkupLanguage)format.Ifyoucontrolthewebserveraswellastheclient,youcanuseanyformatyoulike.Ifnot,youhavetobuildyourapplicationtousewhatevertheserversupports.

PhotoramawillmakeawebservicerequesttogetrecentphotosfromFlickr.Thewebserviceishostedathttps://api.flickr.com/services/rest.ThedatathatisreturnedwillbeJSONthatdescribesthephotos.

WOW! eBook www.wowebook.org

Page 536: iOS Programming The Big Nerd Ranch Guide, 5th

StartingthePhotoramaApplicationCreateanewSingleViewApplicationfortheUniversaldevicefamily.NamethisapplicationPhotorama,asshowninFigure19.2.

Figure19.2Creatingasingleviewapplication

Let’sknockoutthebasicUIbeforefocusingonwebservices.CreateanewSwiftfilenamedPhotosViewController.InPhotosViewController.swift,definethePhotosViewControllerclassandgiveitanimageViewproperty.importFoundationimportUIKit

classPhotosViewController:UIViewController{

@IBOutletvarimageView:UIImageView!

}

Intheprojectnavigator,deletetheexistingViewController.swift.

OpenMain.storyboardandselecttheViewController.OpenitsidentityinspectorandchangetheClasstoPhotosViewController.WiththePhotosViewControllerstillselected,selecttheEditormenuandchooseEmbedIn→NavigationController.

SelecttheNavigationControllerandopenitsattributesinspector.UndertheViewControllerheading,makesuretheboxforIsInitialViewControllerischecked.

DraganImageViewontothecanvasforPhotosViewControllerandaddconstraintstopinittoalledgesofthesuperview.ConnecttheimageviewtotheimageViewoutletonPhotosViewController.OpentheattributesinspectorfortheimageviewandchangetheModetoAspectFill.

Finally,double-clickonthecenterofthenavigationbarforthePhotosViewControllerand

WOW! eBook www.wowebook.org

Page 537: iOS Programming The Big Nerd Ranch Guide, 5th

giveitatitleofPhotorama.YourinterfacewilllooklikeFigure19.3.

Figure19.3InitialPhotoramainterface

Buildandruntheapplicationtomakesuretherearenoerrors.

WOW! eBook www.wowebook.org

Page 538: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 539: iOS Programming The Big Nerd Ranch Guide, 5th

BuildingtheURLCommunicationwithserversisdoneviarequests.Arequestencapsulatesinformationabouttheinteractionbetweentheapplicationandtheserver,anditsmostimportantpieceofinformationisthedestinationURL.

Inthissection,youwillbuilduptheURLforretrievingrecentphotosfromtheFlickrwebservice.Thearchitectureoftheapplicationwillreflectbestpractices.Forexample,eachtypethatyoucreatewillencapsulateasingleresponsibility.Thiswillmakeyourtypesrobustandflexibleandyourapplicationeasiertoreasonabout.TobeagoodiOSdeveloper,younotonlyneedtogetthejobdone,youalsoneedtogetitdonethoughtfullyandwithforesight.

FormattingURLsandrequests

Theformatofawebservicerequestvariesdependingontheserverthattherequestisreachingoutto.Therearenoset-in-stoneruleswhenitcomestowebservices.Youwillneedtofindthedocumentationforthewebservicetoknowhowtoformatarequest.Aslongasaclientapplicationsendstheserverwhatitwants,youhaveaworkingexchange.

Flickr’srecentphotoswebservicewantsaURLthatlookslikethis:https://api.flickr.com/services/rest/?method=flickr.photos.getRecent&api_key=a6d819499131071f158fd740860a5a88&extras=url_h,date_taken&format=json&nojsoncallback=1

Webservicerequestscomeinallsortsofformats,dependingonwhatthecreatorofthatwebserviceistryingtoaccomplish.Therecentphotoswebservice,wherepiecesofinformationarebrokenupintokey-valuepairs,isprettycommon.

Thekey-valuepairsthataresuppliedaspartoftheURLarecalledqueryitems.EachofthequeryitemsfortherecentphotosrequestisdefinedbyandisuniquetotheFlickrAPI.

ThemethoddetermineswhichendpointyouwanttohitontheFlickrAPI.Fortherecentphotos,thisisthestring“flickr.photos.getRecent”.

Theapi_keyisakeythatFlickrgeneratestoauthorizeanapplicationtousetheFlickrAPI.

Theextrasareattributespassedintocustomizetheresponse.Here,theurl_h,date_takenvaluetellstheFlickrserverthatyouwantthephotoURLstoalsocomebackintheresponsealongwiththedatethephotowastaken.

TheformatitemspecifiesthatyouwantthepayloadcomingbacktobeJSON.

ThenojsoncallbackitemspecifiesthatyouwantJSONbackinitsrawformat.

NSURLComponents

Youwillcreatetwotypestodealwithallofthewebserviceinformation.The

WOW! eBook www.wowebook.org

Page 540: iOS Programming The Big Nerd Ranch Guide, 5th

FlickrAPIstructwillberesponsibleforknowingandhandlingallFlickr-relatedinformation.ThisincludesknowinghowtogeneratetheURLsthattheFlickrAPIexpectsaswellasknowingtheformatoftheincomingJSONandhowtoparsethatJSONintotherelevantmodelobjects.ThePhotoStoreclasswillhandletheactualwebservicecalls.Let’sstartbycreatingtheFlickrAPIstruct.

CreateanewSwiftfilenamedFlickrAPI.ThisfilewilldefineaFlickrAPIstructthatwillcontainalloftheknowledgethatisspecifictotheFlickrAPI.

InFlickrAPI.swift,declaretheFlickrAPIstruct.importFoundation

structFlickrAPI{

}

AnenumerationwillbeusedtospecifywhichendpointontheFlickrservertohit.Forthisapplication,youwillonlybeworkingwiththeendpointtogetrecentphotos.However,FlickrsupportsmanyadditionalAPIs,suchassearchingforimagesbasedonastring.Usinganenumnowwillmakeiteasiertoaddendpointsinthefuture.

InFlickrAPI.swift,createtheMethodenumeration.EachcaseofMethodhasarawvaluethatmatchesthecorrespondingFlickrendpoint.Also,defineaconstantforthebaseURLstring.importFoundation

enumMethod:String{caseRecentPhotos="flickr.photos.getRecent"}

structFlickrAPI{

}

InChapter2,youlearnedthatenumerationscanhaverawvaluesassociatedwiththem.AlthoughtherawvaluesareoftenInts,youcanseehereagreatuseofStringastherawvaluefortheMethodenumeration.

Nowdeclareatype-levelpropertytoreferencethebaseURLstringforthewebservicerequests.enumMethod:String{caseRecentPhotos="flickr.photos.getRecent"}

structFlickrAPI{

staticletbaseURLString="https://api.flickr.com/services/rest"}

Atype-levelproperty(ormethod)isonethatisaccessedonthetypeitself–inthiscase,theFlickrAPItype.Forstructs,typepropertiesandmethodsaredeclaredwiththestatickeyword;classesusetheclasskeyword.YouusedatypemethodontheUIViewclassinChapter8whenyoucalledtheanimateWithDuration(_:animations:)method.YoualsousedatypemethodonUIImagePickerControllerinChapter14whenyoucalledtheisSourceTypeAvailable(_:)method.Here,youaredeclaringatype-levelpropertyonFlickrAPI.

WOW! eBook www.wowebook.org

Page 541: iOS Programming The Big Nerd Ranch Guide, 5th

ThebaseURLStringisanimplementationdetailoftheFlickrAPItype,andnootherfileneedstoknowaboutit.Instead,theywillaskforacompletedURLfromFlickrAPI.TokeepotherfilesfrombeingabletoaccessbaseURLString,youwillmarkthepropertyasprivate.structFlickrAPI{

privatestaticletbaseURLString="https://api.flickr.com/services/rest"}

Thisiscalledaccesscontrol.Youcancontrolwhatcanaccessthepropertiesandmethodsonyourowntypes.Therearethreelevelsofaccesscontrolthatcanbeappliedtotypes,properties,andmethods:

public–Anythingcanaccessthistype,property,ormethod.Thisincludeseverythinginyourproject(whichisonemodule)aswellascodefromothermodules(suchasthird-partyframeworks).

internal–Thisisthedefault.Anythinginthecurrentmodulecanaccessthistype,property,ormethod.Foranapp,onlyfileswithinyourprojectcanaccessthese.Ifyouwriteathird-partylibrary,thenonlyfileswithinthatthird-partylibrarycanaccessthem–appsthatuseyourthird-partylibrarycannot.

private–Anythinginthesamesourcefilecanseethistype,property,ormethod.Thisisdifferentthanotherlanguageswhereonlythetypecanseeit.InSwift,thevisibilityforthingsmarkedasprivateisthesourcefile(the.swiftfile).

NowyouaregoingtocreateatypemethodthatbuildsuptheFlickrURLforaspecificendpoint.Thismethodwillaccepttwoarguments:thefirstwillspecifywhichendpointtohit,usingtheMethodenumeration,andthesecondwillbeanoptionaldictionaryofqueryitemparametersassociatedwiththerequest.

ImplementthismethodinyourFlickrAPIstructinFlickrAPI.swift.Fornow,thismethodwillreturnanemptyURL.privatestaticfuncflickrURL(methodmethod:Method,parameters:[String:String]?)->NSURL{

returnNSURL()}

NoticethattheflickrURL(method:parameters:)methodisprivate.ItisanimplementationdetailoftheFlickrAPIstruct.AninternaltypemethodwillbeexposedtotherestoftheprojectforeachofthespecificendpointURLs(whichiscurrentlyjusttherecentphotosendpoint).TheseinternaltypemethodswillcallthroughtotheflickrURL(method:parameters:)method.

InFlickrAPI.swift,defineandimplementtherecentPhotosURL()method.staticfuncrecentPhotosURL()->NSURL{returnflickrURL(method:.RecentPhotos,parameters:["extras":"url_h,date_taken"])}

TimetoconstructthefullURL.YouhavethebaseURLdefinedasaconstant,andthe

WOW! eBook www.wowebook.org

Page 542: iOS Programming The Big Nerd Ranch Guide, 5th

queryitemsarebeingpassedintotheflickrURL(method:parameters:)methodviatheparametersargument.YouwillbuilduptheURLusingtheNSURLComponentsclass,whichisdesignedtotakeinthesevariouscomponentsandconstructanNSURLfromthem.

UpdatetheflickrURL(method:parameters:)methodtoconstructaninstanceofNSURLComponentsfromthebaseURL.Then,loopovertheincomingparametersandcreatetheassociatedNSURLQueryIteminstances.privatestaticfuncflickrURL(methodmethod:Method,parameters:[String:String]?)->NSURL?{

returnNSURL()

letcomponents=NSURLComponents(string:baseURLString)!

varqueryItems=[NSURLQueryItem]()

ifletadditionalParams=parameters{for(key,value)inadditionalParams{letitem=NSURLQueryItem(name:key,value:value)queryItems.append(item)}}components.queryItems=queryItems

returncomponents.URL!}

ThelaststepinsettinguptheURListopassintheparametersthatarecommontoallrequests:method,api_key,format,andnojsoncallback.

TheAPIkeyisatokengeneratedbyFlickrtoidentifyyourapplicationandauthenticateitwiththewebservice.WehavegeneratedanAPIkeyforthisapplicationbycreatingaFlickraccountandregisteringthisapplication.(IfyouwouldlikeyourownAPIkey,youwillneedtoregisteranapplicationathttps://www.flickr.com/services/apps/create/.)

InFlickrAPI.swift,createaconstantthatreferencesthistoken.structFlickrAPI{

privatestaticletbaseURLString="https://api.flickr.com/services/rest"privatestaticletAPIKey="a6d819499131071f158fd740860a5a88"

Double-checktomakesureyouhavetypedintheAPIkeyexactlyaspresentedhere.Ithastomatchexactlyortheserverwillrejectyourrequests.IfyourAPIkeyisnotworkingorifyouhaveanyproblemswiththerequests,checkouttheforumsathttp://forums.bignerdranch.comforhelp.

FinishimplementingflickrURL(method:parameters:)toaddthecommonqueryitemstotheNSURLComponents.privatestaticfuncflickrURL(methodmethod:Method,parameters:[String:String]?)->NSURL{

letcomponents=NSURLComponents(string:baseURLString)!

varqueryItems=[NSURLQueryItem]()

letbaseParams=["method":method.rawValue,"format":"json",

WOW! eBook www.wowebook.org

Page 543: iOS Programming The Big Nerd Ranch Guide, 5th

"nojsoncallback":"1","api_key":APIKey]

for(key,value)inbaseParams{letitem=NSURLQueryItem(name:key,value:value)queryItems.append(item)}

ifletadditionalParams=parameters{for(key,value)inadditionalParams{letitem=NSURLQueryItem(name:key,value:value)queryItems.append(item)}}components.queryItems=queryItems

returncomponents.URL!}

WOW! eBook www.wowebook.org

Page 544: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 545: iOS Programming The Big Nerd Ranch Guide, 5th

SendingtheRequestAURLrequestencapsulatesinformationaboutthecommunicationfromtheapplicationtotheserver.Mostimportantly,itspecifiestheURLoftheserverfortherequest,butitalsohasatimeoutinterval,acachepolicy,andothermetadataabouttherequest.ArequestisrepresentedbytheNSURLRequestclass.CheckouttheFortheMoreCurioussectionattheendofthischapterformoreinformation.

TheNSURLSessionAPIisacollectionofclassesthatusearequesttocommunicatewithaserverinanumberofways.TheNSURLSessionTaskclassisresponsibleforcommunicatingwithaserver.TheNSURLSessionclassisresponsibleforcreatingtasksthatmatchagivenconfiguration.

Anewclass,PhotoStore,willberesponsibleforinitiatingthewebservicerequests.ItwillusetheNSURLSessionAPIandtheFlickrAPIstructtofetchalistofrecentphotosanddownloadtheimagedataforeachphoto.

CreateanewSwiftfilenamedPhotoStore.

OpenPhotoStore.swiftanddeclarethePhotoStoreclass.importFoundation

classPhotoStore{

}

NSURLSession

Let’slookatafewofthepropertiesonNSURLRequest:

allHTTPHeaderFields–ThisisadictionaryofmetadataabouttheHTTPtransaction,includingcharacterencodingandhowtheservershouldhandlecaching.

allowsCellularAccess–ThisBooleanrepresentswhetherarequestisallowedtousecellulardata.

cachePolicy–Thisdeterminesifandhowthelocalcacheshouldbeused.

HTTPMethod–Thisistherequestmethod.ThedefaultisGET;othervaluesarePOST,PUT,andDELETE.

timeoutInterval–Thisisthemaximumdurationaconnectiontotheserverwillbeattemptedfor.

TheclassthatcommunicateswiththewebserviceisaninstanceofNSURLSessionTask.Therearethreekindsoftasks:datatasks,downloadtasks,anduploadtasks.NSURLSessionDataTaskretrievesdatafromtheserverandreturnsitasNSDatainmemory.NSURLSessionDownloadTaskretrievesdatafromtheserverandreturnsitasafilesavedtothefilesystem.NSURLSessionUploadTasksendsdatatotheserver.

WOW! eBook www.wowebook.org

Page 546: iOS Programming The Big Nerd Ranch Guide, 5th

Often,youwillhaveagroupofrequeststhathavemanypropertiesincommon.Forexample,maybesomedownloadsshouldneverhappenovercellulardata,ormaybecertainrequestsshouldbecacheddifferentlythanothers.Itcanbecometedioustoconfigurerelatedrequeststhesameway.

ThisiswhereNSURLSessioncomesinhandy.NSURLSessionactsasafactoryforNSURLSessionTaskinstances.Thesessioniscreatedwithaconfigurationthatspecifiespropertiesthatarecommonacrossallofthetasksthatitcreates.AlthoughmanyapplicationsmightonlyneedtouseasingleinstanceofNSURLSession,havingthepowerandflexibilityofmultiplesessionsisagreattooltohaveatyourdisposal.

InPhotoStore.swift,addapropertytoholdontoaninstanceofNSURLSession.classPhotoStore{

letsession:NSURLSession={letconfig=NSURLSessionConfiguration.defaultSessionConfiguration()returnNSURLSession(configuration:config)}()

}

InPhotoStore.swift,implementthefetchRecentPhotos()methodtocreateanNSURLRequestthatconnectstoapi.flickr.comandasksforthelistofrecentphotos.Then,usetheNSURLSessiontocreateanNSURLSessionDataTaskthattransfersthisrequesttotheserver.funcfetchRecentPhotos(){

leturl=FlickrAPI.recentPhotosURL()letrequest=NSURLRequest(URL:url)lettask=session.dataTaskWithRequest(request){(data,response,error)->Voidin

ifletjsonData=data{ifletjsonString=NSString(data:jsonData,encoding:NSUTF8StringEncoding){print(jsonString)}}elseifletrequestError=error{print("Errorfetchingrecentphotos:\(requestError)")}else{print("Unexpectederrorwiththerequest")}}task.resume()}

CreatingtheNSURLRequestisfairlystraightforward:youcreateanNSURLinstanceusingtheFlickrAPIstructandinstantiatearequestobjectwithit.

Bygivingthesessionarequestandacompletionclosuretocallwhentherequestfinishes,thesessionwillreturnaninstanceofNSURLSessionTask.SincePhotoramaisrequestingdatafromawebservice,thetypeoftaskwillbeaninstanceofNSURLSessionDataTask.Tasksarealwayscreatedinthesuspendedstate,socallingresume()onthetaskwillstartthewebservicerequest.Fornow,thecompletionblockwilljustprintouttheJSONdatareturnedfromtherequest.

Tomakearequest,PhotosViewControllerwillcalltheappropriatemethodson

WOW! eBook www.wowebook.org

Page 547: iOS Programming The Big Nerd Ranch Guide, 5th

PhotoStore.Todothis,PhotosViewControllerneedsareferencetoaninstanceofPhotoStore.

AtthetopofPhotosViewController.swift,addapropertytohangontoaninstanceofPhotoStore.classPhotosViewController:UIViewController{

@IBOutletvarimageView:UIImageView!varstore:PhotoStore!

ThestoreisadependencyofthePhotosViewController.YouwillusepropertyinjectiontogivethePhotosViewControlleritsstoredependency,justasyoudidwiththeviewcontrollersinHomepwner.

OpenAppDelegate.swiftandusepropertyinjectiontogivethePhotosViewControlleraninstanceofPhotoStore.funcapplication(application:UIApplication,didFinishLaunchingWithOptionslaunchOptions:[NSObject:AnyObject]?)->Bool{//Overridepointforcustomizationafterapplicationlaunch.

letrootViewController=window!.rootViewControlleras!UINavigationControllerletphotosViewController=rootViewController.topViewControlleras!PhotosViewControllerphotosViewController.store=PhotoStore()

returntrue}

NowthatthePhotosViewControllercaninteractwiththePhotoStore,kickoffthewebserviceexchangewhentheviewcontrolleriscomingonscreenforthefirsttime.

InPhotosViewController.swift,overrideviewDidLoad()andfetchtherecentphotos.overridefuncviewDidLoad(){super.viewDidLoad()

store.fetchRecentPhotos()}

Buildandruntheapplication.AstringrepresentationoftheJSONdatacomingbackfromthewebservicewillprinttotheconsole.(Ifyoudonotseeanythingprinttotheconsole,makesureyoutypedtheURLandAPIkeycorrectly.)

TheresponsewilllooksomethinglikeFigure19.4.

WOW! eBook www.wowebook.org

Page 548: iOS Programming The Big Nerd Ranch Guide, 5th

Figure19.4Webserviceconsoleoutput

WOW! eBook www.wowebook.org

Page 549: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 550: iOS Programming The Big Nerd Ranch Guide, 5th

ModelingthePhotoNext,youwillcreateaPhotoclasstorepresenteachphotothatisreturnedfromthewebservicerequest.Therelevantpiecesofinformationthatyouwillneedforthisapplicationaretheid,thetitle,theurl_h,andthedatetaken.

CreateanewSwiftfileandnameitPhoto.OpenthisfileanddeclarethePhotoclasswithpropertiesforthephotoID,thetitle,andtheremoteURL.Finally,addadesignatedinitializerthatsetsuptheinstance.importFoundation

classPhoto{

lettitle:StringletremoteURL:NSURLletphotoID:StringletdateTaken:NSDate

init(title:String,photoID:String,remoteURL:NSURL,dateTaken:NSDate){self.title=titleself.photoID=photoIDself.remoteURL=remoteURLself.dateTaken=dateTaken}}

YouwillusethisclassshortlyonceyouareparsingtheJSONdata.

WOW! eBook www.wowebook.org

Page 551: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 552: iOS Programming The Big Nerd Ranch Guide, 5th

JSONDataJSONdata,especiallywhenitiscondensedlikeitisinyourconsole,mayseemdaunting.However,itisactuallyaverysimplesyntax.JSONcancontainthemostbasictypesusedtorepresentmodelobjects:arrays,dictionaries,strings,andnumbers.AJSONdictionarycontainsoneormorekey-valuepairs,wherethekeyisastringandthevaluecanbeanotherdictionaryorastring,number,orarray.Anarraycanconsistofstrings,numbers,dictionaries,andotherarrays.Thus,aJSONdocumentisanestedsetofthesetypesofvalues.

HereisanexampleofsomereallysimpleJSON:{"name":"Christian","friends":["Stacy","Mikey"],"job":{"company":"BigNerdRanch","title":"SeniorNerd"}}

ThisJSONdocumentbeginsandendswithcurlybraces({and}),whichinJSONdelimitadictionary.Withinthecurlybracesarethekey-valuepairsthatbelongtothedictionary.Thisdictionarycontainsthreekey-valuepairs(name,friends,andjob).

Astringisrepresentedbytextwithinquotationmarks.Stringsareusedasthekeyswithinadictionaryandcanbeusedasvalues,too.Thus,thevalueassociatedwiththenamekeyinthetop-leveldictionaryisthestringChristian.

Arraysarerepresentedwithsquarebrackets([and]).AnarraycancontainanyotherJSONinformation.Inthiscase,thefriendskeyholdsanarrayofstrings(StacyandMikey).

Adictionarycancontainotherdictionaries,andthefinalkeyinthetop-leveldictionary,job,isassociatedwithadictionarythathastwokey-valuepairs(companyandtitle).

PhotoramawillparseouttheusefulinformationfromtheJSONdataandstoreitinaPhotoinstance.

NSJSONSerialization

Applehasabuilt-inclassforparsingJSONdata,NSJSONSerialization.YoucanhandthisclassabunchofJSONdata,anditwillcreateadictionaryforeveryJSONdictionary(theJSONspecificationcallsthese“objects”),anarrayforeveryJSONarray,aStringforeveryJSONstring,andanNSNumberforeveryJSONnumber.Let’sseehowthisclasshelpsyou.

OpenPhotoStore.swiftandupdatefetchRecentPhotos()toprinttheJSONobjecttotheconsole.funcfetchRecentPhotos(){

leturl=FlickrAPI.recentPhotosURL()letrequest=NSURLRequest(URL:url)

WOW! eBook www.wowebook.org

Page 553: iOS Programming The Big Nerd Ranch Guide, 5th

lettask=session.dataTaskWithRequest(request){(data,response,error)->Voidin

ifletjsonData=data{ifletjsonString=NSString(data:jsonData,encoding:NSUTF8StringEncoding){print(jsonString)}do{letjsonObject:AnyObject=tryNSJSONSerialization.JSONObjectWithData(jsonData,options:[])print(jsonObject)}catchleterror{print("ErrorcreatingJSONobject:\(error)")}}elseifletrequestError=error{print("Errorfetchingrecentphotos:\(requestError)")}else{print("Unexpectederrorwiththerequest")}}task.resume()}

Buildandrun,thenchecktheconsole.YouwillseetheJSONdataagain,butnowitwillbeformatteddifferentlybecauseprint()doesagoodjobformattingdictionariesandarrays.

TheformatoftheJSONdataisdictatedbytheAPI,soyouwilladdthecodetoparsetheJSONtotheFlickrAPIstruct.

Parsingthedatathatcomesbackfromtheservercouldgowronginanumberofways:ThedatamightnotcontainJSON.Thedatacouldbecorrupt.ThedatamightcontainJSONbutnotmatchtheformatthatyouexpect.Tomanagethepossibilityoffailure,youwilluseanenumerationwithassociatedvaluestorepresentthesuccessorfailureoftheparsing.

Enumerationsandassociatedvalues

YoulearnedaboutthebasicsofenumerationsinChapter2,andyouhavebeenusingthemthroughoutthisbook–includingtheMethodenumusedearlierinthischapter.Associatedvaluesareausefulfeatureofenumerations.Let’stakeamomenttolookatasimpleexamplebeforeyouusethisfeatureinPhotorama.

Enumerationsareaconvenientwayofdefiningandrestrictingthepossiblevaluesforavariable.Forexample,let’ssayyouareworkingonahomeautomationapp.Youcoulddefineanenumerationtospecifytheovenstate,likethis:enumOvenState{caseOncaseOff}

Iftheovenison,youalsoneedtoknowwhattemperatureitissetto.Associatedvaluesareaperfectsolutiontothissituation.enumOvenState{caseOn(Double)

WOW! eBook www.wowebook.org

Page 554: iOS Programming The Big Nerd Ranch Guide, 5th

caseOff}

varovenState=OvenState.On(450)

Eachcaseofanenumerationcanhavedataofanytypeassociatedwithit.ForOvenState,its.OncasehasanassociatedDoublethatrepresentstheoven’stemperature.Noticethatnotallcasesneedtohaveassociatedvalues.

Retrievingtheassociatedvaluefromanenumisoftendoneusingaswitchstatement.switchovenState{caselet.On(temperature):print("Theovenisonandsetto\(temperature)degrees.")case.Off:print("Theovenisoff.")}

Notethatthe.Oncaseusesaletkeywordtostoretheassociatedvalueinthetemperatureconstant,whichcanbeusedwithinthecaseclause.(Itcanusethevarkeywordinsteadiftemperatureneedstobeavariable.)ConsideringthevaluegiventoovenState,theswitchstatementabovewouldresultintheline“Theovenisonandsetto450degrees.”printedtotheconsole.

Inthenextsection,youwilluseanenumerationwithassociatedvaluestotieasuccessfulresultstatusofarequesttotheFlickrwebservicewiththedatacontainingrecentphotosortieafailureresultstatuswitherrorinformation.

ParsingJSONdata

InFlickrAPI.swift,addanenumerationnamedPhotosResulttothetopofthefilethathasacaseforbothsuccessandfailure.enumMethod:String{caseRecentPhotos="flickr.photos.getRecent"}

enumPhotosResult{caseSuccess([Photo])caseFailure(ErrorType)}

IfthedataisvalidJSONandcontainsanarrayofphotos,thosephotoswillbeassociatedwiththeSuccesscase.Ifthereareanyerrorsduringtheparsingprocess,therelevantErrorTypewillbepassedalongwiththeFailurecase.

ErrorTypeisaprotocolthatallerrorsconformto.NSErroristheerrorthatmanyiOSframeworksthrow,anditconformstoErrorType.YouwillcreateyourownErrorTypeshortly.

InFlickrAPI.swift,implementamethodthattakesinaninstanceofNSDataandusestheNSJSONSerializationclasstoconvertthedataintothebasicfoundationobjects.staticfuncphotosFromJSONData(data:NSData)->PhotosResult{do{letjsonObject:AnyObject=tryNSJSONSerialization.JSONObjectWithData(data,options:[])

varfinalPhotos=[Photo]()

WOW! eBook www.wowebook.org

Page 555: iOS Programming The Big Nerd Ranch Guide, 5th

return.Success(finalPhotos)}catchleterror{return.Failure(error)}}

(Thiscodewillgeneratesomewarnings.Youwillresolvethemshortly.)

IftheincomingdataisvalidJSONdata,thenthejsonObjectinstancewillreferencetheappropriatemodelobject.Ifnot,thentherewasaproblemwiththedataandyoupassalongtheerror.YounowneedtogetthephotoinformationoutoftheJSONobjectandintoinstancesofPhoto.

WhentheNSURLSessionDataTaskfinishes,youwilluseNSJSONSerializationtoconverttheJSONdataintoadictionary.Figure19.5showshowthedatawillbestructured.

AtthetopleveloftheincomingJSONdataisadictionary.Thevalueassociatedwiththe“photos”keycontainstheimportantinformation,andthemostimportantisthearrayofdictionaries.

Figure19.5JSONobjects

WOW! eBook www.wowebook.org

Page 556: iOS Programming The Big Nerd Ranch Guide, 5th

Asyoucansee,youhavetodigprettydeeplytogettheinformationthatyouneed.

IfthestructureoftheJSONdoesnotmatchyourexpectation,youwillreturnacustomerror.

AtthetopofFlickrAPI.swift,declareacustomenumtorepresentpossibleerrorsfortheFlickrAPI.enumPhotosResult{caseSuccess([Photo])caseFailure(ErrorType)}

enumFlickrError:ErrorType{caseInvalidJSONData}

InphotosFromJSONData(_:),digdownthroughtheJSONtogettothearrayofdictionariesrepresentingtheindividualphotos.staticfuncphotosFromJSONData(data:NSData)->PhotosResult{do{letjsonObject:AnyObject=tryNSJSONSerialization.JSONObjectWithData(data,options:[])

guardletjsonDictionary=jsonObjectas?[NSObject:AnyObject],photos=jsonDictionary["photos"]as?[String:AnyObject],photosArray=photos["photo"]as?[[String:AnyObject]]else{

//TheJSONstructuredoesn'tmatchourexpectationsreturn.Failure(FlickrError.InvalidJSONData)}

varfinalPhotos=[Photo]()return.Success(finalPhotos)}catchleterror{return.Failure(error)}}

ThenextstepistogetthephotoinformationoutofthedictionaryandintoPhotomodelobjects.

YouwillneedaninstanceofNSDateFormattertoconvertthedatetakenstringintoaninstanceofNSDate.

InFlickrAPI.swift,addaconstantinstanceofNSDateFormatter.privatestaticletbaseURLString="https://api.flickr.com/services/rest"privatestaticletAPIKey="a6d819499131071f158fd740860a5a88"

privatestaticletdateFormatter:NSDateFormatter={letformatter=NSDateFormatter()formatter.dateFormat="yyyy-MM-ddHH:mm:ss"returnformatter}()

StillinFlickrAPI.swift,writeanewmethodtoparseaJSONdictionaryintoaPhotoinstance.privatestaticfuncphotoFromJSONObject(json:[String:AnyObject])->Photo?{guardletphotoID=json["id"]as?String,title=json["title"]as?String,dateString=json["datetaken"]as?String,photoURLString=json["url_h"]as?String,url=NSURL(string:photoURLString),

WOW! eBook www.wowebook.org

Page 557: iOS Programming The Big Nerd Ranch Guide, 5th

dateTaken=dateFormatter.dateFromString(dateString)else{

//Don'thaveenoughinformationtoconstructaPhotoreturnnil}

returnPhoto(title:title,photoID:photoID,remoteURL:url,dateTaken:dateTaken)}

NowupdatephotosFromJSONData(_:)toparsethedictionariesintoPhotoobjectsandthenreturntheseaspartoftheSuccessenumerator.AlsohandlethepossibilitythattheJSONformathaschanged,sonophotoswereabletobefound.staticfuncphotosFromJSONData(data:NSData)->PhotosResult{

do{letjsonObject:AnyObject=tryNSJSONSerialization.JSONObjectWithData(data,options:[])

guardletjsonDictionary=jsonObjectas?[NSObject:AnyObject],photos=jsonDictionary["photos"]as?[String:AnyObject],photosArray=photos["photo"]as?[[String:AnyObject]]else{

//TheJSONstructuredoesn'tmatchourexpectationsreturn.Failure(FlickrError.InvalidJSONData)}

varfinalPhotos=[Photo]()forphotoJSONinphotosArray{ifletphoto=photoFromJSONObject(photoJSON){finalPhotos.append(photo)}}

iffinalPhotos.count==0&&photosArray.count>0{//Weweren'tabletoparseanyofthephotos//MaybetheJSONformatforphotoshaschangedreturn.Failure(FlickrError.InvalidJSONData)}return.Success(finalPhotos)}catchleterror{return.Failure(error)}}

Next,inPhotoStore.swift,writeanewmethodthatwillprocesstheJSONdatathatisreturnedfromthewebservicerequest.funcprocessRecentPhotosRequest(datadata:NSData?,error:NSError?)->PhotosResult{guardletjsonData=dataelse{return.Failure(error!)}

returnFlickrAPI.photosFromJSONData(jsonData)}

NowupdatefetchRecentPhotos()tousethemethodyoujustcreated.funcfetchRecentPhotos(){leturl=FlickrAPI.recentPhotosURL()letrequest=NSURLRequest(URL:url)lettask=session.dataTaskWithRequest(request){(data,response,error)->Voidin

ifletjsonData=data{do{letjsonObject:AnyObject=tryNSJSONSerialization.JSONObjectWithData(jsonData,options:[])print(jsonObject)}

WOW! eBook www.wowebook.org

Page 558: iOS Programming The Big Nerd Ranch Guide, 5th

catchleterror{print("ErrorcreatingJSONobject:\(error)")}}elseifletrequestError=error{print("Errorfetchingrecentphotos:\(requestError)")}else{print("Unexpectederrorwiththerequest")}

letresult=self.processRecentPhotosRequest(data:data,error:error)}task.resume()}

NowupdatethemethodsignatureforfetchRecentPhotos()totakeinacompletionclosurethatwillbecalledoncethewebservicerequestiscompleted.funcfetchRecentPhotos(){funcfetchRecentPhotos(completioncompletion:(PhotosResult)->Void){leturl=FlickrAPI.recentPhotosURL()letrequest=NSURLRequest(URL:url)lettask=session.dataTaskWithRequest(request){(data,response,error)->Voidin

letresult=self.processRecentPhotosRequest(data:data,error:error)completion(result)}task.resume()}

Fetchingdatafromawebserviceisanasynchronousprocess.Oncetherequeststarts,itmaytakeanontrivialamountoftimeforaresponsetocomebackfromtheserver.Becauseofthis,thefetchRecentPhotos(_:)methodcannotdirectlyreturnaninstanceofPhotosResult.Instead,thecallerofthismethodwillsupplyacompletionclosureforthePhotoStoretocalloncetherequestiscomplete.

ThisfollowsthesamepatternthatNSURLSessionTaskuseswithitscompletionhandler:thetaskiscreatedwithaclosureforittocalloncethewebservicerequestcompletes.Figure19.6describestheflowofdatawiththewebservicerequest.

WOW! eBook www.wowebook.org

Page 559: iOS Programming The Big Nerd Ranch Guide, 5th

Figure19.6Webservicerequestdataflow

InPhotosViewController.swift,updatetheimplementationoftheviewDidLoad()toprintouttheresultofthewebservicerequest.overridefuncviewDidLoad()super.viewDidLoad()

store.fetchRecentPhotos(){(photosResult)->Voidin

switchphotosResult{caselet.Success(photos):print("Successfullyfound\(photos.count)recentphotos.")caselet.Failure(error):print("Errorfetchingrecentphotos:\(error)")}

}}

Buildandruntheapplication.Oncethewebservicerequestcompletes,youshouldseethenumberofphotosfoundprintedtotheconsole.

WOW! eBook www.wowebook.org

Page 560: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 561: iOS Programming The Big Nerd Ranch Guide, 5th

DownloadingandDisplayingtheImageDataYouhavedonealotalreadyinthischapter:youhavesuccessfullyinteractedwiththeFlickrAPIviaawebservicerequest,andyouhaveparsedtheincomingJSONdataintoPhotomodelobjects.Unfortunately,youhavenothingtoshowforitexceptsomelogmessagesintheconsole.

Inthissection,youwillusetheURLreturnedfromthewebservicerequesttodownloadtheimagedata.ThenyouwillcreateaninstanceofUIImagefromthatdataand,finally,youwilldisplaythefirstimagereturnedfromtherequestinaUIImageView.(Inthenextchapter,youwilldisplayalloftheimagesthatarereturnedinagridlayoutdrivenbyaUICollectionView.)

Thefirststepisdownloadingtheimagedata.Thisprocesswillbeverysimilartothewebservicerequesttodownloadthephotos’JSONdata.

OpenPhoto.swiftandgiveitanoptionalUIImageproperty.ImporttheUIKitframeworkinsteadofFoundation.importFoundationimportUIKit

classPhoto{

lettitle:StringletremoteURL:NSURLletphotoID:StringletdateTaken:NSDatevarimage:UIImage?

OpenPhotoStore.swift,importUIKit,andaddanenumerationtothetopofthefilethatrepresentstheresultofdownloadingtheimage.ThisenumerationwillfollowthesamepatternasthePhotosResultenumeration,takingadvantageofassociatedvalues.YouwillalsocreateanErrorTypetorepresentphotoerrors.importFoundationimportUIKit

enumImageResult{caseSuccess(UIImage)caseFailure(ErrorType)}

enumPhotoError:ErrorType{caseImageCreationError}

Ifthedownloadissuccessful,theSuccesscasewillhavetheUIImageassociatedwithit.Ifthereisanerror,theFailurecasewillhavetheErrorTypeassociatedwithit.

Now,inthesamefile,implementamethodtodownloadtheimagedata.LikethefetchRecentPhotos(_:)method,thisnewmethodwilltakeinacompletionclosurethatwillreturnaninstanceofImageResult.funcfetchImageForPhoto(photo:Photo,completion:(ImageResult)->Void){

letphotoURL=photo.remoteURLletrequest=NSURLRequest(URL:photoURL)

lettask=session.dataTaskWithRequest(request){

WOW! eBook www.wowebook.org

Page 562: iOS Programming The Big Nerd Ranch Guide, 5th

(data,response,error)->Voidin

}task.resume()}

Nowimplementamethodthatprocessesthedatafromthewebservicerequestintoanimage,ifpossible.funcprocessImageRequest(datadata:NSData?,error:NSError?)->ImageResult{

guardletimageData=data,image=UIImage(data:imageData)else{

//Couldn'tcreateanimageifdata==nil{return.Failure(error!)}else{return.Failure(PhotoError.ImageCreationError)}}

return.Success(image)}

StillinPhotoStore.swift,updatefetchImageForPhoto(_:completion:)tousethisnewmethod.funcfetchImageForPhoto(photo:Photo,completion:(ImageResult)->Void){

letphotoURL=photo.remoteURLletrequest=NSURLRequest(URL:photoURL)

lettask=session.dataTaskWithRequest(request){(data,response,error)->Voidin

letresult=self.processImageRequest(data:data,error:error)

ifcaselet.Success(image)=result{photo.image=image}

completion(result)}task.resume()}

Sinceyouonlyneedtohandlethe.Successcase,youuseanifcasestatementtocheckwhetherresulthasavalueof.Success.Thisisidenticaltoonlyhandlingthatcaseinaswitchstatement://Thiscodeifcaselet.Success(image)=result{photo.image=image}

//Behavesjustlikethiscodeswitchresult{caselet.Success(image):photo.image=imagecase.Failure(_):break}

Totestthiscode,youwilldownloadtheimagedataforthefirstphotothatisreturnedfromtherecentphotosrequestanddisplayitontheimageview.

OpenPhotosViewController.swiftandupdateviewDidLoad().

WOW! eBook www.wowebook.org

Page 563: iOS Programming The Big Nerd Ranch Guide, 5th

overridefuncviewDidLoad()super.viewDidLoad()

store.fetchRecentPhotos(){(photosResult)->Voidin

switchphotosResult{caselet.Success(photos):print("Successfullyfound\(photos.count)recentphotos.")

ifletfirstPhoto=photos.first{self.store.fetchImageForPhoto(firstPhoto){(imageResult)->Voidin

switchimageResult{caselet.Success(image):self.imageView.image=imagecaselet.Failure(error):print("Errordownloadingimage:\(error)")}}}caselet.Failure(error):print("Errorfetchingrecentphotos:\(error)")}}}

Althoughyoucouldbuildandruntheapplicationatthispoint,theimagemayormaynotappearintheimageviewwhenthewebservicerequestfinishes.Why?Thecodethatupdatestheimageviewisnotbeingrunonthemainthread.

WOW! eBook www.wowebook.org

Page 564: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 565: iOS Programming The Big Nerd Ranch Guide, 5th

TheMainThreadModerniOSdeviceshavemulticoreprocessorsthatenablethemtorunmultiplechunksofcodesimultaneously.Thesecomputationsproceedinparallel,sothisisreferredtoasparallelcomputing.Whendifferentcomputationsareinflightatthesametime,thisisknownasconcurrency,andthecomputationsaresaidtobehappeningconcurrently.Acommonwaytoexpressthisisbyrepresentingeachcomputationwithadifferentthreadofcontrol.

Sofarinthisbook,allofyourcodehasbeenrunningonthemainthread.ThemainthreadissometimesreferredtoastheUI(userinterface)thread,becauseanycodethatmodifiestheUImustrunonthemainthread.

Whenthewebservicecompletes,youwantittoupdatetheimageview.Butbydefault,NSURLSessionDataTaskrunsthecompletionhandleronabackgroundthread.Youneedawaytoforcecodetorunonthemainthreadinordertoupdatetheimageview.YoucandothateasilyusingtheNSOperationQueueclass.

InPhotosViewController.swift,updateviewDidLoad()tocallthecompletionclosureonthemainthread.overridefuncviewDidLoad()super.viewDidLoad()

store.fetchRecentPhotos(){(photosResult)->Voidin

switchphotosResult{caselet.Success(photos):print("Successfullyfound\(photos.count)recentphotos.")

ifletfirstPhoto=photos.first{self.store.fetchImageForPhoto(firstPhoto){(imageResult)->Voidin

switchimageResult{caselet.Success(image):self.imageView.image=imageNSOperationQueue.mainQueue().addOperationWithBlock{self.imageView.image=image}caselet.Failure(error):print("Errordownloadingimage:\(error)")}}}caselet.Failure(error):print("Errorfetchingrecentphotos:\(error)")}}}

Buildandruntheapplication.Nowthattheimageviewisbeingupdatedonthemainthread,youwillhavesomethingtoshowforallyourhardwork:theimagewillappearwhenthewebservicerequestfinishes.(Itmighttakealittletimetoshowtheimageifthewebservicerequesttakesawhiletofinish.)

WOW! eBook www.wowebook.org

Page 566: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 567: iOS Programming The Big Nerd Ranch Guide, 5th

BronzeChallenge:PrintingtheResponseInformationThecompletionhandlerfordataTaskWithRequest(_:completionHandler:)providesaninstanceofNSURLResponse.WhenmakingHTTPrequests,thisresponseisoftypeNSURLHTTPResponse(asubclassofNSURLResponse).PrintthestatusCodeandheaderFieldstotheconsole.Thesepropertiesareveryusefulwhendebuggingwebservicecalls.

WOW! eBook www.wowebook.org

Page 568: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 569: iOS Programming The Big Nerd Ranch Guide, 5th

FortheMoreCurious:HTTPWhenNSURLSessionTaskinteractswithawebserver,itdoessoaccordingtotherulesoutlinedintheHTTPspecification.Thespecificationisveryclearabouttheexactformatoftherequest/responseexchangebetweentheclientandtheserver.AnexampleofasimpleHTTPrequestisshowninFigure19.7.

Figure19.7HTTPrequestformat

AnHTTPrequesthasthreeparts:arequestline,requestheaders,andanoptionalrequestbody.Therequestlineisthefirstlineoftherequestandtellstheserverwhattheclientistryingtodo.Inthisrequest,theclientistryingtoGETtheresourceat/index.html.(ItalsospecifiestheHTTPversionthattherequestwillbeconformingto.)

ThewordGETisanHTTPmethod.WhilethereareanumberofsupportedHTTPmethods,youwillseeGETandPOSTmostoften.ThedefaultofNSURLRequest,GET,indicatesthattheclientwantsaresourcefromtheserver.Theresourcerequestedmightbeanactualfileonthewebserver’sfilesystem,oritcouldbegenerateddynamicallyatthemomenttherequestisreceived.Asaclient,youshouldnotcareaboutthisdetail,butmorethanlikelytheJSONresourcesyourequestedinthischapterwerecreateddynamically.

Inadditiontogettingthingsfromaserver,youcansenditinformation.Forexample,manywebserversallowyoutouploadphotos.AclientapplicationwouldpasstheimagedatatotheserverthroughanHTTPrequest.Inthissituation,youwouldusetheHTTPmethodPOST,andyouwouldincludearequestbody.Thebodyofarequestisthepayloadyouaresendingtotheserver–typicallyJSON,XML,orbinarydata.

Whentherequesthasabody,itmustalsohavetheContent-Lengthheader.Handily,NSURLRequestwillcomputethesizeofthebodyandaddthisheaderforyou.

HereisanexampleofhowtoPOSTanimagetoanimaginarysiteusinganNSMutableURLRequest.ifletsomeURL=NSURL(string:"http://www.photos.example.com/upload"){letimage=profileImage()

WOW! eBook www.wowebook.org

Page 570: iOS Programming The Big Nerd Ranch Guide, 5th

letdata=UIImagePNGRepresentation(image)

letreq=NSMutableURLRequest(URL:someURL)

//ThisaddstheHTTPbodydataandautomaticallysetsthecontent-lengthheaderreq.HTTPBody=data

//ThischangestheHTTPmethodintherequestlinereq.HTTPMethod="POST"

//Ifyouwantedtosetarequestheader,suchastheAcceptheaderreq.setValue("text/json",forHTTPHeaderField:"Accept")}

Figure19.8showswhatasimpleHTTPresponsemightlooklike.WhileyouwillnotbemodifyingthecorrespondingNSHTTPURLResponseinstance,itisnicetounderstandwhatitismodeling.

Figure19.8HTTPresponseformat

Asyoucansee,theformatoftheresponseisnottoodifferentfromtherequest.Itincludesastatusline,responseheaders,and,ofcourse,theresponsebody.Yes,thisiswherethatpesky“404NotFound”comesfrom!

WOW! eBook www.wowebook.org

Page 571: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 572: iOS Programming The Big Nerd Ranch Guide, 5th

20CollectionViews

Inthischapter,youwillcontinueworkingonthePhotoramaapplicationbydisplayingtherecentFlickrphotosinagridusingtheUICollectionViewclass.Thischapterwillalsoreinforcethedatasourcedesignpatternthatyouusedinpreviouschapters.Figure20.1showsyouwhattheapplicationwilllooklikeattheendofthischapter.

Figure20.1Photoramawithacollectionview

InChapter9,youworkedwithUITableView.Tableviewsareagreatwaytodisplayandeditacolumnofinformationinahierarchicallist.Likeatableview,acollectionviewalsodisplaysanorderedcollectionofitems,butinsteadofdisplayingtheinformationinahierarchicallist,thecollectionviewhasalayoutobjectthatdrivesthedisplayofinformation.Youwilluseabuilt-inlayoutobject,theUICollectionViewFlowLayout,topresenttherecentphotosinascrollablegrid.

WOW! eBook www.wowebook.org

Page 573: iOS Programming The Big Nerd Ranch Guide, 5th

DisplayingtheGridLet’stackletheinterfacefirst.YouaregoingtochangetheuserinterfaceforPhotosViewControllertodisplayacollectionviewinsteadofdisplayingtheimageview.

OpenMain.storyboardandlocatethePhotoramaimageview.DeletetheimageviewfromthecanvasanddragaCollectionViewontothecanvas.Selectboththecollectionviewanditssuperview.(Theeasiestwaytodothisisusingthedocumentoutline.)OpentheAutoLayoutAlignmenu,configureitlikeFigure20.2,andclickAdd4Constraints.

Figure20.2Collectionviewconstraints

BecauseyouusedtheAlignmenutopintheedges,thecollectionviewwillbepinnedtothetopoftheentireviewinsteadoftothetoplayoutguide.Thisisusefulforscrollviews(andtheirsubclasses,likeUITableViewandUICollectionView)sothatthecontentwillscrollunderneaththenavigationbar.Thescrollviewwillautomaticallyupdateitsinsetstomakethecontentvisible,asyousawinChapter9.ThecanvaswillnowlooklikeFigure20.3.

WOW! eBook www.wowebook.org

Page 574: iOS Programming The Big Nerd Ranch Guide, 5th

Figure20.3Storyboardcanvas

Currently,thecollectionviewandthecollectionviewcellsbothhaveaclearbackgroundcolor.Opentheattributesinspectorforthecollectionviewandchangeitsbackgroundcolortowhite.Thenselectthecollectionviewcell–thesmallrectangleintheupper-leftcornerofthecollectionview–andgiveitablackbackgroundcolor.

Selecttheblackcollectionviewcellandopenitsattributesinspector.SettheIdentifiertoUICollectionViewCell(Figure20.4).

Figure20.4Settingthereuseidentifier

Thecollectionviewisnowonthecanvas,butyouneedawaytopopulatethecellswithdata.Todothis,youwillcreateanewclasstoactasthedatasourceofthecollectionview.

WOW! eBook www.wowebook.org

Page 575: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 576: iOS Programming The Big Nerd Ranch Guide, 5th

CollectionViewDataSourceApplicationsareconstantlychanging,sopartofbeingagoodiOSdeveloperisbuildingapplicationsinawaythatallowsthemtoadapttochangingrequirements.

ThePhotoramaapplicationwilldisplayasinglecollectionviewofphotos.YoucoulddosomethingsimilartowhatyoudidinHomepwnerandmakethePhotosViewControllerbethedatasourceofthecollectionview.Theviewcontrollerwouldimplementtherequireddatasourcemethods,andeverythingwouldworkjustfine.

Atleast,itwouldworkfornow.Whatif,sometimeinthefuture,youdecidedtohaveadifferentscreenthatalsodisplayedacollectionviewofphotos?Maybeinsteadofdisplayingtherecentphotos,itwoulduseadifferentwebservicetodisplayallthephotosmatchingasearchterm.Inthiscase,youwouldneedtoreimplementthesamedatasourcemethodswithinthenewviewcontrollerwithessentiallythesamecode.Thatwouldnotbeideal.

Instead,youwillabstractoutthecollectionviewdatasourcecodeintoanewclass.Thisclasswillberesponsibleforrespondingtodatasourcequestions–anditwillbereusableasnecessary.

CreateanewSwiftfilenamedPhotoDataSource.OpenthisfileanddeclarethePhotoDataSourceclass.importFoundationimportUIKit

classPhotoDataSource:NSObject,UICollectionViewDataSource{

varphotos=[Photo]()

}

ToconformtotheUICollectionViewDataSourceprotocol,atypealsoneedstoconformtotheNSObjectProtocol.TheeasiestandmostcommonwaytoconformtothisprotocolistosubclassfromNSObject,asyoudidabove.

TheUICollectionViewDataSourceprotocoldeclarestworequiredmethodstoimplement:funccollectionView(collectionView:UICollectionView,numberOfItemsInSectionsection:Int)->IntfunccollectionView(collectionView:UICollectionView,cellForItemAtIndexPathindexPath:NSIndexPath)->UICollectionViewCell

YoumightnoticethatthesetwomethodslookverysimilartothetworequiredmethodsofUITableViewDataSourcethatyousawinChapter9.Thefirstdatasourcecallbackaskshowmanycellstodisplay,andthesecondasksfortheUICollectionViewCelltodisplayforagivenindexpath.

ImplementthesetwomethodsinPhotoDataSource.swift.classPhotoDataSource:NSObject,UICollectionViewDataSource{

varphotos=[Photo]()

WOW! eBook www.wowebook.org

Page 577: iOS Programming The Big Nerd Ranch Guide, 5th

funccollectionView(collectionView:UICollectionView,numberOfItemsInSectionsection:Int)->Int{returnphotos.count}

funccollectionView(collectionView:UICollectionView,cellForItemAtIndexPathindexPath:NSIndexPath)->UICollectionViewCell{

letidentifier="UICollectionViewCell"letcell=collectionView.dequeueReusableCellWithReuseIdentifier(identifier,forIndexPath:indexPath)

returncell}}

Next,thecollectionviewneedstoknowthataninstanceofPhotoDataSourceisthedatasourceobject.

InPhotosViewController.swift,addapropertytoreferenceaninstanceofPhotoDataSourceandanoutletforaUICollectionViewinstance.Also,youwillnotneedtheimageViewanymore,sodeleteit.classPhotosViewController:UIViewController{

@IBOutletvarimageView:UIImageView!@IBOutletvarcollectionView:UICollectionView!

varstore:PhotoStore!letphotoDataSource=PhotoDataSource()

UpdateviewDidLoad()tosetthedatasourceonthecollectionview.overridefuncviewDidLoad(){super.viewDidLoad()

collectionView.dataSource=photoDataSource

...

Finally,updatethephotoDataSourceobjectwiththeresultofthewebservicerequestandreloadthecollectionview.overridefuncviewDidLoad()super.viewDidLoad()

collectionView.dataSource=photoDataSource

store.fetchRecentPhotos(){(photosResult)->Voidin

switchphotosResult{caselet.Success(photos):print("Successfullyfound\(photos.count)recentphotos.")ifletfirstPhoto=photos.first{self.store.fetchImageForPhoto(firstPhoto){(imageResult)->VoidinswitchimageResult{caselet.Success(image):NSOperationQueue.mainQueue().addOperationWithBlock(){self.imageView.image=image}caselet.Failure(error):print("Errordownloadingimage:\(error)")}}}caselet.Failure(error):print("Errorfetchingrecentphotos:\(error)")}

WOW! eBook www.wowebook.org

Page 578: iOS Programming The Big Nerd Ranch Guide, 5th

NSOperationQueue.mainQueue().addOperationWithBlock(){switchphotosResult{caselet.Success(photos):print("Successfullyfound\(photos.count)recentphotos.")self.photoDataSource.photos=photoscaselet.Failure(error):self.photoDataSource.photos.removeAll()print("Errorfetchingrecentphotos:\(error)")}self.collectionView.reloadSections(NSIndexSet(index:0))}}}

ThelastthingyouneedtodoismakethecollectionViewoutletconnection.

OpenMain.storyboardandnavigatetothecollectionview.Control-dragfromthePhotosViewControllertothecollectionviewandconnectittothecollectionViewoutlet.

Buildandruntheapplication.Afterthewebservicerequestcompletes,checktheconsoletoconfirmthatphotoswerefound.OntheiOSdevice,therewillbeagridofblacksquarescorrespondingtothenumberofphotosfound(Figure20.5).Thesecellsarearrangedinaflowlayout.Aflowlayoutfitsasmanycellsonarowaspossiblebeforeflowingdowntothenextrow.IfyourotatetheiOSdevice,youwillseethecellsfillthegivenarea.

Figure20.5Initialflowlayout

WOW! eBook www.wowebook.org

Page 579: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 580: iOS Programming The Big Nerd Ranch Guide, 5th

CustomizingtheLayoutThedisplayofcellsisnotdrivenbythecollectionviewitselfbutbythecollectionview’slayout.Thelayoutobjectisresponsiblefortheplacementofcellsonscreen.Layouts,inturn,aredrivenbyasubclassofUICollectionViewLayout.

TheflowlayoutthatPhotoramaiscurrentlyusingisUICollectionViewFlowLayout,whichistheonlyconcreteUICollectionViewLayoutsubclassprovidedbytheUIKitframework.

SomeofthepropertiesyoucancustomizeonUICollectionViewFlowLayoutare:

scrollDirection–Doyouwanttoscrollverticallyorhorizontally?

minimumLineSpacing–Whatistheminimumspacingbetweenlines?

minimumInteritemSpacing–Whatistheminimumspacingbetweenitemsinarow(orcolumn,ifscrollinghorizontally)?

itemSize–Whatisthesizeofeachitem?

sectionInset–Whatarethemarginsusedtolayoutcontentforeachsection?

Figure20.6showshowthesepropertiesaffectthepresentationofcellsusingUICollectionViewFlowLayout.

WOW! eBook www.wowebook.org

Page 581: iOS Programming The Big Nerd Ranch Guide, 5th

Figure20.6UICollectionViewFlowLayoutproperties

OpenMain.storyboardandselectthecollectionview.OpenthesizeinspectorandconfiguretheCellSize,MinimumSpacing,andSectionInsetsasshowninFigure20.7.

WOW! eBook www.wowebook.org

Page 582: iOS Programming The Big Nerd Ranch Guide, 5th

Figure20.7Collectionviewsizeinspector

Buildandruntheapplicationtoseehowthelayouthaschanged.

WOW! eBook www.wowebook.org

Page 583: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 584: iOS Programming The Big Nerd Ranch Guide, 5th

CreatingaCustomUICollectionViewCellNextyouaregoingtocreateacustomUICollectionViewCellsubclasstodisplaythephotos.Whiletheimagedataisdownloading,thecollectionviewcellwilldisplayaspinningactivityindicatorusingtheUIActivityIndicatorViewclass.

CreateanewSwiftfilenamedPhotoCollectionViewCell.InPhotoCollectionViewCell.swift,definePhotoCollectionViewCellasasubclassofUICollectionViewCell.Thenaddoutletstoreferencetheimageviewandtheactivityindicatorview.importFoundationimportUIKit

classPhotoCollectionViewCell:UICollectionViewCell{

@IBOutletvarimageView:UIImageView!@IBOutletvarspinner:UIActivityIndicatorView!

}

Theactivityindicatorviewshouldonlyspinwhenthecellisnotdisplayinganimage.InsteadofalwaysupdatingthespinnerwhentheimageViewisupdated,orviceversa,youwillwriteahelpermethodtotakecareofitforyou.

CreatethishelpermethodinPhotoCollectionViewCell.swift.funcupdateWithImage(image:UIImage?){ifletimageToDisplay=image{spinner.stopAnimating()imageView.image=imageToDisplay}else{spinner.startAnimating()imageView.image=nil}}

Itwouldbenicetoreseteachcelltothespinningstatebothwhenthecellisfirstcreatedandwhenthecellisgettingreused.ThemethodawakeFromNib()iscalledaftertheinterfacefileisloadedinandtheoutletconnectionsaremade.Thisisagoodopportunitytodoanyuserinterfacecustomization.ThemethodprepareForReuse()iscalledwhenacellisabouttobereused.

ImplementthesetwomethodsinPhotoCollectionViewCell.swifttoresetthecellbacktothespinningstate.overridefuncawakeFromNib(){super.awakeFromNib()

updateWithImage(nil)}

overridefuncprepareForReuse(){super.prepareForReuse()

updateWithImage(nil)}

Youwilluseaprototypecelltosetuptheinterfaceforthecollectionviewcellinthestoryboard,justasyoudidinChapter11forItemCell.Ifyourecall,eachprototypecell

WOW! eBook www.wowebook.org

Page 585: iOS Programming The Big Nerd Ranch Guide, 5th

correspondstoavisuallyuniquecellwithauniquereuseidentifier.Mostofthetime,theprototypecellswillbeassociatedwithdifferentUICollectionViewCellsubclassestoprovidebehaviorspecifictothatkindofcell.

Inthecollectionview’sattributesinspector,youcanadjustthenumberofItemsthatthecollectionviewdisplays,andeachitemcorrespondstoaprototypecellinthecanvas.ForPhotorama,youonlyneedonekindofcell:thePhotoCollectionViewCellthatdisplaysaphoto.

OpenMain.storyboardandselectthecollectionviewcell.Intheidentityinspector,changetheClasstoPhotoCollectionViewCell(Figure20.8).

Figure20.8Changingthecellclass

DraganimageviewontotheUICollectionViewCell.Addconstraintstopintheimageviewtotheedgesofthecell.OpentheattributesinspectorfortheimageviewandsettheModetoAspectFill.Thiswillcutoffpartsofthephotos,butitwillallowthephotostocompletelyfillinthecollectionviewcell.

Next,draganactivityindicatorviewontopoftheimageview.Addconstraintstocentertheactivityindicatorviewbothhorizontallyandverticallywiththeimageview.OpenitsattributesinspectorandselectHidesWhenStopped(Figure20.9).

Figure20.9Configuringtheactivityindicator

Selectthecollectionviewcellagain.Thiscanbeabittrickytodoonthecanvassincethenewlyaddedsubviewscompletelycoverthecellitself.AhelpfulInterfaceBuildertipistoholdControlandShifttogetherandthenclickontopoftheviewyouwanttoselect.Youwillbepresentedwithalistofalloftheviewsandcontrollersunderthepointyouclicked(Figure20.10).

WOW! eBook www.wowebook.org

Page 586: iOS Programming The Big Nerd Ranch Guide, 5th

Figure20.10Selectingthecellonthecanvas

Withthecellselected,opentheconnectionsinspectorandconnecttheimageViewandspinnerpropertiestotheimageviewandactivityindicatorviewonthecanvas(Figure20.11).

WOW! eBook www.wowebook.org

Page 587: iOS Programming The Big Nerd Ranch Guide, 5th

Figure20.11ConnectingPhotoCollectionViewCelloutlets

OpenPhotoDataSource.swiftandupdatethedatasourcemethodtousethePhotoCollectionViewCell.funccollectionView(collectionView:UICollectionView,cellForItemAtIndexPathindexPath:NSIndexPath)->UICollectionViewCell{

letidentifier="UICollectionViewCell"letcell=collectionView.dequeueReusableCellWithReuseIdentifier(identifier,forIndexPath:indexPath)as!PhotoCollectionViewCell

letphoto=photos[indexPath.row]cell.updateWithImage(photo.image)

returncell}

Buildandruntheapplication.Whentherecentphotosrequestcompletes,youwillseetheactivityindicatorviewsallspinning(Figure20.12).

WOW! eBook www.wowebook.org

Page 588: iOS Programming The Big Nerd Ranch Guide, 5th

Figure20.12Customcollectionviewsubclass

WOW! eBook www.wowebook.org

Page 589: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 590: iOS Programming The Big Nerd Ranch Guide, 5th

DownloadingtheImageDataNowallthatisleftisdownloadingtheimagedataforthephotosthatcomebackintherequest.Thistaskisnotverydifficult,butitrequiressomethought.Imagesarelargefiles,anddownloadingthemcouldeatupyourusers’cellulardataallowance.AsaconsiderateiOSdeveloper,youwanttomakeyourapp’sdatausageisonlywhatitneedstobe.

Consideryouroptions.YoucoulddownloadtheimagedatainviewDidLoad()whenthefetchRecentPhotos(_:)methodcallsitscompletionclosure.Atthatpoint,youalreadyassigntheincomingphotostothephotosproperty,soyoucoulditerateoverallofthosephotosanddownloadtheirimagedatarightatthatpoint.

Althoughthiswouldwork,itwouldbeverycostly.Therecouldbealargenumberofphotoscomingbackintheinitialrequest,andtheusermayneverevenscrolldownintheapplicationfarenoughtoseesomeofthem.Ontopofthat,ifyouinitializetoomanyrequestssimultaneously,someoftherequestsmaytimeoutwhilewaitingforotherrequeststofinish.Sothisisprobablynotthebestsolution.

Instead,itmakessensetodownloadtheimagedataforonlythecellsthattheuserisattemptingtoview.UICollectionViewhasamechanismtosupportthisthroughitsUICollectionViewDelegatemethodcollectionView(_:willDisplayCell:forItemAtIndexPath:).Thisdelegatemethodwillbecalledeverytimeacellisgettingdisplayedonscreenandisagreatopportunitytodownloadtheimagedata.

RecallthatthedataforthecollectionviewisdrivenbyaninstanceofPhotoDataSource,areusableclasswiththesingleresponsibilityofdisplayingphotosinacollectionview.Collectionviewsalsohaveadelegate,whichisresponsibleforhandlinguserinteractionwiththecollectionview.Thisincludestaskssuchasmanagingcellselectionandtrackingcellscomingintoandoutofview.Thisresponsibilityismoretightlycoupledwiththeviewcontrolleritself,sowhereasthedatasourceisaninstanceofPhotoDataSource,thecollectionview’sdelegatewillbethePhotosViewController.

InPhotosViewController.swift,havetheclassconformtotheUICollectionViewDelegateprotocol.classPhotosViewController:UIViewController,UICollectionViewDelegate{

(SincetheUICollectionViewDelegateprotocolonlydefinesoptionalmethods,Xcodedoesnotreportanyerrorswhenyouaddthisdeclaration.)

UpdateviewDidLoad()tosetthePhotosViewControllerasthedelegateofthecollectionview.overridefuncviewDidLoad(){super.viewDidLoad()

collectionView.dataSource=photoDataSourcecollectionView.delegate=self

...

WOW! eBook www.wowebook.org

Page 591: iOS Programming The Big Nerd Ranch Guide, 5th

Finally,implementthedelegatemethodinPhotosViewController.swift.funccollectionView(collectionView:UICollectionView,willDisplayCellcell:UICollectionViewCell,forItemAtIndexPathindexPath:NSIndexPath){

letphoto=photoDataSource.photos[indexPath.row]

//Downloadtheimagedata,whichcouldtakesometimestore.fetchImageForPhoto(photo){(result)->Voidin

NSOperationQueue.mainQueue().addOperationWithBlock(){

//Theindexpathforthephotomighthavechangedbetweenthe//timetherequeststartedandfinished,sofindthemost//recentindexpath

//(Note:Youwillhaveanerroronthenextline;youwillfixitsoon)letphotoIndex=self.photoDataSource.photos.indexOf(photo)!letphotoIndexPath=NSIndexPath(forRow:photoIndex,inSection:0)

//Whentherequestfinishes,onlyupdatethecellifit'sstillvisibleifletcell=self.collectionView.cellForItemAtIndexPath(photoIndexPath)as?PhotoCollectionViewCell{cell.updateWithImage(photo.image)}}}}

IftheimagealreadyexistsonthePhoto,theimagedatashouldnotbedownloadedfromthewebserviceagain.

OpenPhotoStore.swiftandupdatefetchImageForPhoto(_:completion:)toreturnthephoto’simageifithasalreadybeendownloaded.funcfetchImageForPhoto(photo:Photo,completion:(ImageResult)->Void){

ifletimage=photo.image{completion(.Success(image))return}

letphotoURL=photo.remoteURL...

Let’sfixtheerroryousawwhenfindingtheindexofphotointhephotosarray.TheindexOf(_:)methodworksbycomparingtheitemthatyouarelookingfortoeachoftheitemsinthecollection.Itdoesthisusingthe==operator.TypesthatconformtotheEquatableprotocolmustimplementthisoperator,andPhotodoesnotyetconformtoEquatable.

InPhoto.swift,declarethatPhotoconformstotheEquatableprotocolandimplementtherequiredoverloadingofthe==operator.classPhoto:Equatable{...}

func==(lhs:Photo,rhs:Photo)->Bool{//TwoPhotosarethesameiftheyhavethesamephotoIDreturnlhs.photoID==rhs.photoID}

Customoperatorshavetobedeclaredoutsideofthetypedeclaration.Thecompilerrecognizesthatthe==operatoroccursbetweentwoinstancesofPhoto(basedonthe

WOW! eBook www.wowebook.org

Page 592: iOS Programming The Big Nerd Ranch Guide, 5th

argumentstothefunction)andthereforevalidatesthattherequirementfortheEquatableprotocolhasbeensatisfied.

InSwift,itiscommontogrouprelatedchunksoffunctionalityintoanextension.Let’stakeashortdetourtolearnaboutextensionsandthenusethisknowledgetoseehowconformingtotheEquatableprotocolisoftendoneinpractice.

Extensions

Extensionsserveacoupleofpurposes:theyallowyoutogroupchunksoffunctionalityintoalogicalunit,andtheyalsoallowyoutoaddfunctionalitytoyourowntypesaswellastypesprovidedbythesystemorotherframeworks.Beingabletoaddfunctionalitytoatypewhosesourcecodeyoudonothaveaccesstoisaverypowerfulandflexibletool.Extensionscanbeaddedtoclasses,structs,andenums.Let’stakealookatanexample.

SayyouwantedtoaddfunctionalitytotheInttypetoprovideadoubledvalueofthatInt.Forexample:letfourteen=7.doubled//Thevalueoffourteenis'14'

YoucanaddthisfunctionalitybyextendingtheInttype:extensionInt{vardoubled:Int{returnself*2}}

Withextensions,youcanaddcomputedproperties,addmethods,andconformtoprotocols.However,youcannotaddstoredpropertiestoanextension.

Extensionsprovideagreatmechanismforgroupingrelatedpiecesoffunctionality.Theycanmakethecodemorereadableandhelpwithlong-termmaintainabilityofyourcodebase.Onecommonchunkoffunctionalitythatisoftengroupedintoanextensionisconformancetoaprotocolalongwiththemethodsofthatprotocol.

UpdatePhoto.swifttouseanextensiontoconformtotheEquatableprotocol.classPhoto:Equatable{...}

extensionPhoto:Equatable{}

func==(lhs:Photo,rhs:Photo)->Bool{//TwoPhotosarethesameiftheyhavethesamephotoIDreturnlhs.photoID==rhs.photoID}

TheextensiondeclaresthatPhotoconformstotheEquatableprotocol.Whileitmightmakesenseforthe==operatortobeincludedinthisPhotoextension,aswementionedoperatorsmustbedeclaredoutsidethescopeofaspecifictype.Therefore,topreservethegroupingasmuchaspossible,theoperatorisdefinedimmediatelyafterthe(empty)extension.

Thisisasimplifiedexample,butextensionsareverypowerfulforbothextendingexistingtypesandgroupingrelatedfunctionality.Infact,theSwiftstandardlibrarymakes

WOW! eBook www.wowebook.org

Page 593: iOS Programming The Big Nerd Ranch Guide, 5th

extensiveuseofextensions–andyouwill,too.

Buildandruntheapplication.Theimagedatawilldownloadforthecellsvisibleonscreen(Figure20.13).Scrolldowntomakemorecellsvisible.Atfirst,youwillseetheactivityindicatorviewsspinning,butsoontheimagedataforthosecellswillload.Ifyouscrollbackup,therewillbenodelayinloadingimagedata.Becausetheimageswerealreadydownloadedforthosecells,youwillnotseeanyactivityindicatorviewsandtheimageswillbeimmediatelyvisible.

Figure20.13Imagedownloadsinprogress

WOW! eBook www.wowebook.org

Page 594: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 595: iOS Programming The Big Nerd Ranch Guide, 5th

NavigatingtoaPhotoInthissection,youaregoingtoaddfunctionalitytoallowausertonavigatetoanddisplayasinglephoto.

CreateanewSwiftfilenamedPhotoInfoViewController.Inthisnewfile,declarethePhotoInfoViewControllerclassandaddanimageViewoutlet.importFoundationimportUIKit

classPhotoInfoViewController:UIViewController{

@IBOutletvarimageView:UIImageView!}

Nowsetuptheinterfaceforthisviewcontroller.OpenMain.storyboardanddraganewViewControllerontothecanvasfromtheobjectlibrary.Withthisviewcontrollerselected,openitsidentityinspectorandchangetheClasstoPhotoInfoViewController.

Whentheusertapsononeofthecollectionviewcells,theapplicationwillnavigatetothisnewviewcontroller.Control-dragfromtheUICollectionViewCelltothePhotoInfoViewControllerandselecttheShowsegue.Withthenewsegueselected,openitsattributesinspectorandgivethesegueanIdentifierofShowPhoto(Figure20.14).

Figure20.14Navigationtoaphoto

AddanimageviewtothePhotoInfoViewController’sview.SetupitsAutoLayoutconstraintstopintheimageviewtoallfoursides.OpentheattributesinspectorfortheimageviewandsetitsModetoAspectFit.

WOW! eBook www.wowebook.org

Page 596: iOS Programming The Big Nerd Ranch Guide, 5th

Finally,connecttheimageviewtotheimageViewoutlet.

Whentheusertapsacell,theShowPhotoseguewillbetriggered.Atthispoint,thePhotosViewControllerwillneedtopassboththePhotoandthePhotoStoretothePhotoInfoViewController.

OpenPhotoInfoViewController.swiftandaddtwoproperties.classPhotoInfoViewController:UIViewController{

@IBOutletvarimageView:UIImageView!

varphoto:Photo!{didSet{navigationItem.title=photo.title}}varstore:PhotoStore!}

Whenphotoissetonthisviewcontroller,thenavigationitemwillbeupdatedtodisplaythenameofthephoto.

NowoverrideviewDidLoad()tosettheimageontheimageViewwhentheviewisloaded.overridefuncviewDidLoad(){super.viewDidLoad()

store.fetchImageForPhoto(photo){(result)->Voidinswitchresult{caselet.Success(image):NSOperationQueue.mainQueue().addOperationWithBlock(){self.imageView.image=image}caselet.Failure(error):print("Errorfetchingimageforphoto:\(error)")}}}

InPhotosViewController.swift,implementprepareForSegue(_:sender:)topassalongthephotoandthestore.overridefuncprepareForSegue(segue:UIStoryboardSegue,sender:AnyObject?){ifsegue.identifier=="ShowPhoto"{ifletselectedIndexPath=collectionView.indexPathsForSelectedItems()?.first{

letphoto=photoDataSource.photos[selectedIndexPath.row]

letdestinationVC=segue.destinationViewControlleras!PhotoInfoViewControllerdestinationVC.photo=photodestinationVC.store=store}}}

Buildandruntheapplication.Afterthewebservicerequesthasfinished,tapononeofthephotostoseeitinthenewviewcontroller(Figure20.15).

WOW! eBook www.wowebook.org

Page 597: iOS Programming The Big Nerd Ranch Guide, 5th

Figure20.15Displayingaphoto

Collectionviewsareapowerfulwaytodisplaydatausingaflexiblelayout.Youhavejustbarelytappedintothepowerofcollectionviewsinthischapter.

WOW! eBook www.wowebook.org

Page 598: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 599: iOS Programming The Big Nerd Ranch Guide, 5th

SilverChallenge:UpdatedItemSizesHavethecollectionviewalwaysdisplayfouritemsperrow,takingupasmuchasthescreenwidthaspossible.Thisshouldworkinbothportraitandlandscapeorientations.

WOW! eBook www.wowebook.org

Page 600: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 601: iOS Programming The Big Nerd Ranch Guide, 5th

GoldChallenge:CreatingaCustomLayoutCreateacustomlayoutthatdisplaysthephotosinaflipbook.Youwillneedtousethetransformpropertyonthecelllayertogetanappropriate3-Deffect.YoucansubclassUICollectionViewLayoutforthischallenge,butalsoconsidersubclassingUICollectionViewFlowLayout.CheckouttheclassreferenceforUICollectionViewLayoutformoreinformation.

WOW! eBook www.wowebook.org

Page 602: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 603: iOS Programming The Big Nerd Ranch Guide, 5th

21CoreData

WhendecidingbetweenapproachestosavingandloadingforiOSapplications,thefirstquestionis“Localorremote?”Ifyouwanttosavedatatoaremoteserver,youwilllikelyuseawebservice.Ifyouwanttostoredatalocally,youhavetoaskanotherquestion:“ArchivingorCoreData?”

YourHomepwnerapplicationuseskeyedarchivingtosaveitemdatatothefilesystem.Thebiggestdrawbacktoarchivingisitsall-or-nothingnature:toaccessanythinginthearchive,youmustunarchivetheentirefile,andtosaveanychanges,youmustrewritetheentirefile.CoreData,ontheotherhand,canfetchasubsetofthestoredobjects.Andifyouchangeanyofthoseobjects,youcanupdatejustthatpartofthefile.Thisincrementalfetching,updating,deleting,andinsertingcanradicallyimprovetheperformanceofyourapplicationwhenyouhavealotofmodelobjectsbeingshuttledbetweenthefilesystemandRAM.

WOW! eBook www.wowebook.org

Page 604: iOS Programming The Big Nerd Ranch Guide, 5th

ObjectGraphsCoreDataisaframeworkthatletsyouexpresswhatyourmodelobjectsareandhowtheyarerelatedtooneanother.Itthentakescontrolofthelifetimesoftheseobjects,makingsuretherelationshipsarekeptuptodate.Whenyousaveandloadtheobjects,CoreDatamakessureeverythingisconsistent.Thiscollectionofmodelobjectsisoftencalledanobjectgraph,astheobjectscanbethoughtofasnodesandtherelationshipsasverticesinamathematicalgraph.

OftenyouwillhaveCoreDatasaveyourobjectgraphtoaSQLitedatabase.DeveloperswhoareusedtootherSQLtechnologiesmightexpecttotreatCoreDatalikeanobject-relationalmappingsystem,butthismindsetwillleadtoconfusion.UnlikeanORM,CoreDatatakescompletecontrolofthestorage,whichjusthappenstobearelationaldatabase.Youdonothavetodescribethingslikethedatabaseschemaandforeignkeys–CoreDatadoesthat.YoujusttellCoreDatawhatneedsstoringandletitworkouthowtostoreit.

CoreDatagivesyoutheabilitytofetchandstoredatainarelationaldatabasewithouthavingtoknowthedetailsoftheunderlyingstoragemechanism.ThischapterwillgiveyouanunderstandingofCoreDataasyouaddpersistencetothePhotoramaapplication.

WOW! eBook www.wowebook.org

Page 605: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 606: iOS Programming The Big Nerd Ranch Guide, 5th

EntitiesArelationaldatabasehassomethingcalledatable.Atablerepresentsatype:youcanhaveatableofpeople,atableofacreditcardpurchases,oratableofrealestatelistings.Eachtablehasanumberofcolumnstoholdpiecesofinformationaboutthetype.Atablethatrepresentspeoplemighthavecolumnsforlastname,dateofbirth,andheight.Everyrowinthetablerepresentsanexampleofthetype–e.g.,asingleperson.

ThisorganizationtranslateswelltoSwift.EverytableislikeaSwifttype.Everycolumnisoneofthetype’sproperties.Everyrowisaninstanceofthattype.Thus,CoreData’sjobistomovedatatoandfromthesetworepresentations(Figure21.1).

Figure21.1RoleofCoreData

CoreDatausesdifferentterminologytodescribetheseideas:atable/typeiscalledanentity,andthecolumns/propertiesarecalledattributes.ACoreDatamodelfileisthedescriptionofeveryentityalongwithitsattributesinyourapplication.InPhotorama,youaregoingtodescribeaPhotoentityinamodelfileandgiveitattributesliketitle,remoteURL,anddateTaken.

Modelingentities

OpenPhotorama.xcodeproj.Createanewfile,butdonotmakeitaSwiftfileliketheonesyouhavecreatedbefore.Instead,selectCoreDataintheiOSsectionandcreateanewDataModel(Figure21.2).NameitPhotorama.

WOW! eBook www.wowebook.org

Page 607: iOS Programming The Big Nerd Ranch Guide, 5th

Figure21.2Creatingthemodelfile

ThiswillcreatethePhotorama.xcdatamodeldfileandaddittoyourproject.SelectthisfilefromtheprojectnavigatorandtheeditorareawillrevealtheuserinterfaceformanipulatingaCoreDatamodelfile.

FindtheAddEntitybuttonatthebottomleftofthewindowandclickit.Anewentitywillappearinthelistofentitiesinthelefthandtable.Double-clickthisentityandchangeitsnametoPhoto(Figure21.3).

WOW! eBook www.wowebook.org

Page 608: iOS Programming The Big Nerd Ranch Guide, 5th

Figure21.3CreatingthePhotoentity

NowyourPhotoentityneedsattributes.RememberthatthesewillbethepropertiesofthePhotoclass.Thenecessaryattributesarelistedbelow.Foreachattribute,clickthe+buttonintheAttributessectionandedittheAttributeandTypevalues.

photoIDisaString.

photoKeyisaString.

titleisaString.

dateTakenisaDate.

remoteURLisaTransformable.(ItisanNSURL,butthatisnotoneofthepossibilities.Wewilldiscuss“transformable”next.)

Bydefault,theCoreDatamodeleditormakesallattributesoptional.ItdoesnotmakesenseforthePhotoattributestobeoptional,solet’schangethemalltobenon-optional.

Selectalloftheattributesbyclickingonthefirstattributeinthelist,holdingtheShiftkey,andthenclickingthelastattributeinthelist.Opentheutilitiesview,openthedatamodel

WOW! eBook www.wowebook.org

Page 609: iOS Programming The Big Nerd Ranch Guide, 5th

inspector(the icon),andunchecktheOptionalcheckbox(Figure21.4).

Figure21.4Makingtheattributesnon-optional

Sincethesepropertiesarenolongeroptional,youwillhavetoprovidevaluesforthemwhentheobjectiscreated.Youwilldothatlaterinthischapter.

Transformableattributes

CoreDataisonlyabletostorecertaindatatypesinitsstore.NSURLisnotoneofthesetypes,soyoudeclaredtheremoteURLattributeastransformable.Withatransformableattribute,CoreDatawillconverttheobjectintoatypethatitcanstorewhensavingandthenconvertitbacktotheoriginalobjectwhenloadingfromthefilesystem.

AtransformableattributerequiresanNSValueTransformersubclasstohandletheconversionsbetweentypes.Ifyoudonotspecifyacustomsubclass,thesystemwillusethetransformernamedNSKeyedUnarchiveFromDataTransformer.ThistransformerusesarchivingtoconverttheobjecttoandfromNSData.SinceNSURLconformstoNSCoding,thedefaultNSKeyedUnarchiveFromDataTransformerwillbesufficient.IfthetypeyouwantedtotransformdidnotconformtoNSCoding,youwouldneedtowriteyourowncustomNSValueTransformersubclass.

Atthispoint,yourmodelfileissufficienttosaveandloadphotos.Inthenextsection,youwillcreateacustomsubclassforthePhotoentity.

NSManagedObjectandsubclasses

WhenanobjectisfetchedwithCoreData,itsclass,bydefault,isNSManagedObject.NSManagedObjectisasubclassofNSObjectthatknowshowtocooperatewiththerestofCoreData.AnNSManagedObjectworksabitlikeadictionary:itholdsakey-valuepairforeveryproperty(attributeorrelationship)intheentity.

AnNSManagedObjectislittlemorethanadatacontainer.Ifyouneedyourmodelobjectstodosomethinginadditiontoholdingdata,youmustsubclassNSManagedObject.Then,inyourmodelfile,youspecifythatthisentityisrepresentedbyinstancesofyoursubclass,notthestandardNSManagedObject.

WhenanewPhotoiscreated,anewUUIDneedstobegeneratedforthephotoKey.ThisfunctionalitycannotberepresentedwithNSManagedObjectitself,soyouwillcreateacustomsubclass.

WOW! eBook www.wowebook.org

Page 610: iOS Programming The Big Nerd Ranch Guide, 5th

XcodecangenerateNSManagedObjectsubclassesforyoubasedonwhatyouhavedefinedinyourCoreDatamodelfile.

Intheprojectnavigator,selectthePhoto.swiftfileanddeleteit.Makesureyoumoveittothetrashwhenpromptedtomakesureitdoesnotstillexistintheprojectdirectory.

Createanewfile.FromtheiOSsectionofthetemplateselectionscreen,chooseCoreData,andthenchooseNSManagedObjectsubclassandclickNext.Onthenextscreen,checktheboxforPhotoramaandclickNext.ChecktheboxforthePhotoentityandclickNext.Finally,makesurethelanguageissettoSwiftandclickCreate.

Thetemplatewillcreatetwofilesforyou:Photo.swiftandPhoto+CoreDataProperties.swift.ThetemplateplacesalloftheattributesthatyoudefinedinthemodelfileintoPhoto+CoreDataProperties.swift.Ifyoueverchangeyourentityinthemodelfile,youcansimplydeletePhoto+CoreDataProperties.swiftandrepeatthestepstogeneratetheNSManagedObjectsubclass.XcodewillrecognizethatyoualreadyhavePhoto.swiftandwillonlyrecreatePhoto+CoreDataProperties.swift.

OpenPhoto+CoreDataProperties.swiftandtakealookatwhatthetemplatecreatedforyou.

[email protected],whichisspecifictoCoreData,letsthecompilerknowthatthestorageandimplementationofthesepropertieswillbeprovidedatruntime.BecauseCoreDatawillcreatetheNSManagedObjectinstances,youcannolongeruseacustominitializer,sothepropertiesneedtobevariablesinsteadofconstants.

WhenyoumadethePhotoentity’sattributesnon-optionalinthemodelfile,youmadeacontractwithCoreDatathatthoseattributeswouldalwayshaveavaluewhenaninstanceofPhotowasbeingpersisted.Unfortunately,thatcontractwithCoreDataisnottranslatedbyXcodeintooptionalsinSwift,sowhenXcodegeneratedthePhotosubclass,itdidnotmakethepropertiesnon-optional.

ToadheretoyourcontractwithCoreData,maketheattributesinPhoto+CoreDataProperties.swiftnon-optional.(Theorderofyourattributesmightbedifferent;thatisOK.)@NSManagedvarphotoID:String?@NSManagedvarphotoKey:String?@NSManagedvartitle:String?@NSManagedvardateTaken:NSDate?@NSManagedvarremoteURL:NSObject?

InPhoto+CoreDataProperties.swift,changetheremoteURLtypetobeNSURL.RecallthatthispropertyisatransformablepropertyandwillautomaticallybeconvertedtoandfromNSDatawhenbeingstoredinandretrievedfromCoreData.@NSManagedvarphotoID:String@NSManagedvarphotoKey:String@NSManagedvartitle:String@NSManagedvardateTaken:NSDate@NSManagedvarremoteURL:NSObject@NSManagedvarremoteURL:NSURL

WOW! eBook www.wowebook.org

Page 611: iOS Programming The Big Nerd Ranch Guide, 5th

AnycustompropertiesorcodethatyouwanttoaddshouldbeaddedtoPhoto.swift.Let’saddbackthebehavioryoupreviouslyhad.

InPhoto.swift,addtheoptionalUIImageproperty.YouwillneedtoimportUIKitaswell.importFoundationimportUIKitimportCoreData

classPhoto:NSManagedObject{

//Insertcodeheretoaddfunctionalitytoyourmanagedobjectsubclass

varimage:UIImage?

}

Ofcourse,whenyoulaunchanapplicationthefirsttime,therearenosavedphotos.Whenthewebservicerequestreturns,thephotoswillbeaddedtothedatabase.Whenobjectsareaddedtothedatabase,theyaresentthemessageawakeFromInsert().HereiswhereyouwillsettheinitialvaluesforthepropertiesofaPhoto.

ImplementawakeFromInsert()inPhoto.swift.overridefuncawakeFromInsert(){super.awakeFromInsert()

//Givethepropertiestheirinitialvaluestitle=""photoID=""remoteURL=NSURL()photoKey=NSUUID().UUIDStringdateTaken=NSDate()}

YouhavecreatedyourmodelgraphanddefinedyourPhotoentity.ThenextstepistosetuptheCoreDatastack,whichwillmanagetheinteractionsbetweentheapplicationandCoreData.

WOW! eBook www.wowebook.org

Page 612: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 613: iOS Programming The Big Nerd Ranch Guide, 5th

BuildingtheCoreDataStackTheCoreDatastackconsistsoftheclassesthatinterfacewithyourentitiesaswellasthemodelfilethatdescribestheentitiestosaveandloadinstancestoastore(suchasthefilesystem).YouwilllearnabouteachcomponentinthestackandwhatroleitplaysinCoreData.

CreateanewSwiftfileandnameitCoreDataStack.OpenthisfileanddeclaretheCoreDataStackclassalongwitharequiredinitializerthatacceptsthenameofthemodelfile.DonotforgettoimporttheCoreDataframework.importFoundationimportCoreData

classCoreDataStack{

letmanagedObjectModelName:String

requiredinit(modelName:String){managedObjectModelName=modelName}

}

NSManagedObjectModel

Youworkedwiththemodelfileearlierinthechapter.Themodelfileiswhereyoudefinetheentitiesforyourapplicationalongwiththeirproperties.ThemodelfileisaninstanceofNSManagedObjectModel.

InCoreDataStack.swift,addapropertytoreadinthemodelfilefromthemainbundle.classCoreDataStack{

letmanagedObjectModelName:String

privatelazyvarmanagedObjectModel:NSManagedObjectModel={letmodelURL=NSBundle.mainBundle().URLForResource(self.managedObjectModelName,withExtension:"momd")!returnNSManagedObjectModel(contentsOfURL:modelURL)!}()

requiredinit(modelName:String){managedObjectModelName=modelName}}

First,themodelfileislocatedwithinthemainbundle.ThenaninstanceofNSManagedObjectModeliscreatedusingthisURL.

Noticethelazykeywordinthepropertydeclaration.Thelazykeywordallowsapropertytobelazilyloaded.Inotherwords,thefirsttimethatthemanagedObjectModelpropertyisaccessed,theassociatedclosurewillbeexecuted.Lazyloadingallowspropertyinitializationtobedeferreduntilthepropertyisactuallyneeded.

WOW! eBook www.wowebook.org

Page 614: iOS Programming The Big Nerd Ranch Guide, 5th

NSPersistentStoreCoordinator

CoreDatacanpersistdatatodiskusingafewdifferentformats:

SQLite DataissavedtodiskusingaSQLitedatabase.Thisisthemostcommonlyusedstoretype.

Atomic Dataissavedtodiskusingabinaryformat.

XML DataissavedtodiskusinganXMLformat.ThisstoretypeisnotavailableoniOS.

In-Memory Dataisnotsavedtodisk,butinsteadisstoredinmemory.

ThemappingbetweenanobjectgraphandthepersistentstoreisaccomplishedusinganinstanceofNSPersistentStoreCoordinator.Thepersistentstorecoordinatorneedstoknowtwothings:“Whataremyentities?”and“WhereamIsavingtoandloadingdatafrom?”Toanswerthesequestions,youwillinstantiateanNSPersistentStoreCoordinatorwiththeNSManagedObjectModel.Thenyouwilladdapersistentstore,representingoneofthepersistenceformatsabove,tothecoordinator.

InCoreDataStack.swift,addtwopropertiestosetupthepersistentstorecoordinator.privatevarapplicationDocumentsDirectory:NSURL={leturls=NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory,inDomains:.UserDomainMask)returnurls.first!}()

privatelazyvarpersistentStoreCoordinator:NSPersistentStoreCoordinator={

varcoordinator=NSPersistentStoreCoordinator(managedObjectModel:self.managedObjectModel)

letpathComponent="\(self.managedObjectModelName).sqlite"leturl=self.applicationDocumentsDirectory.URLByAppendingPathComponent(pathComponent)

letstore=try!coordinator.addPersistentStoreWithType(NSSQLiteStoreType,configuration:nil,URL:url,options:nil)

returncoordinator}()

Afterthecoordinatoriscreated,youattempttoaddaspecificstoretothecoordinator.Ataminimum,thisstoreneedstoknowitstypeandwhereitshouldpersistthedata.

NSManagedObjectContext

WOW! eBook www.wowebook.org

Page 615: iOS Programming The Big Nerd Ranch Guide, 5th

TheportalthroughwhichyouinteractwithyourentitiesistheNSManagedObjectContext.Themanagedobjectcontextisassociatedwithaspecificpersistentstorecoordinator.Youcanthinkofthemanagedobjectcontextasanintelligentscratchpad.Whenyouaskthecontexttofetchsomeentities,thecontextwillworkwithitspersistentstorecoordinatortobringtemporarycopiesoftheentitiesandobjectgraphintomemory.Unlessyouaskthecontexttosaveitschanges,thepersisteddataremainsthesame.

StillinCoreDataStack.swift,addanewpropertytoholdontoaninstanceofNSManagedObjectContext.lazyvarmainQueueContext:NSManagedObjectContext={

letmoc=NSManagedObjectContext(concurrencyType:.MainQueueConcurrencyType)moc.persistentStoreCoordinator=self.persistentStoreCoordinatormoc.name="MainQueueContext(UIContext)"

returnmoc}()

YourCoreDatastackiscompletefornow.NowyoujustneedtomakethePhotoStoreresponsibleforowningtheCoreDatastack.

OpenPhotoStore.swiftandaddapropertyforaCoreDataStackinstance.classPhotoStore{

letcoreDataStack=CoreDataStack(modelName:"Photorama")

WOW! eBook www.wowebook.org

Page 616: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 617: iOS Programming The Big Nerd Ranch Guide, 5th

UpdatingItemsWiththeCoreDatastackcomplete,youcannowinteractwithit.Primarily,youwilldothisthroughitsmainQueueContext.Thisishowyouwillbothcreatenewentitiesandsavechanges.

Insertingintothecontext

Whenanentityiscreated,itshouldbeinsertedintoamanagedobjectcontext.

OpenFlickrAPI.swiftandimportCoreData.importFoundationimportCoreData

ThenupdatethephotoFromJSONObject(_:)methodtotakeinanadditionalargumentoftypeNSManagedObjectContext.ItwillthenusethiscontexttoinsertnewPhotoinstances.privatestaticfuncphotoFromJSONObject(json:[String:AnyObject],inContextcontext:NSManagedObjectContext)->Photo?{

guardletphotoID=json["id"]as?String,title=json["title"]as?String,dateString=json["datetaken"]as?String,photoURLString=json["url_h"]as?String,url=NSURL(string:photoURLString),dateTaken=dateFormatter.dateFromString(dateString)else{returnnil}

returnPhoto(title:title,photoID:photoID,URL:url)

varphoto:Photo!context.performBlockAndWait(){photo=NSEntityDescription.insertNewObjectForEntityForName("Photo",inManagedObjectContext:context)as!Photophoto.title=titlephoto.photoID=photoIDphoto.remoteURL=urlphoto.dateTaken=dateTaken}

returnphoto}

EachNSManagedObjectContextisassociatedwithaspecificconcurrencyqueue,andthemainQueueContextisassociatedwiththemain,oruserinterface,queue.Youhavetointeractwithacontextonthequeuethatitisassociatedwith.NSManagedObjectContexthastwomethodsthatensurethishappens:performBlock(_:)andperformBlockAndWait(_:).ThedifferencebetweenthemisthatperformBlock(_:)isasynchronousandperformBlockAndWait(_:)issynchronous.SinceyouarereturningtheresultoftheinsertoperationfromthephotoFromJSONObject(_:inContext:)method,youusethesynchronousmethod.

ThephotoFromJSONObject(_:)methodiscalledfromthemethodphotosFromJSONData(_:).Updatethismethodtotakeinacontextandpassitto

WOW! eBook www.wowebook.org

Page 618: iOS Programming The Big Nerd Ranch Guide, 5th

thephotoFromJSONObject(_:)method.staticfuncphotosFromJSONData(data:NSData,inContextcontext:NSManagedObjectContext)->PhotosResult{

do{letjsonObject:AnyObject=tryNSJSONSerialization.JSONObjectWithData(data,options:[])

guardletjsonDictionary=jsonObjectas?[NSObject:AnyObject],photos=jsonDictionary["photos"]as?[String:AnyObject],photosArray=photos["photo"]as?[[String:AnyObject]]else{

//TheJSONstructuredoesn'tmatchourexpectationsreturn.Failure(FlickrError.InvalidJSONData)}

varfinalPhotos=[Photo]()forphotoJSONinphotosArray{ifletphoto=photoFromJSONObject(photoJSON,inContext:context){finalPhotos.append(photo)}}

...

Finally,youneedtopassthemainQueueContexttotheFlickrAPIstructoncethewebservicerequestsuccessfullycompletes.

OpenPhotoStore.swiftandupdateprocessRecentPhotosRequest(data:error:).funcprocessRecentPhotosRequest(datadata:NSData?,error:NSError?)->PhotosResult{

guardletjsonData=dataelse{return.Failure(error!)}

returnFlickrAPI.photosFromJSONData(jsonData,inContext:self.coreDataStack.mainQueueContext)}

Buildandruntheapplicationnowthatallerrorshavebeenaddressed.Althoughthebehaviorremainsunchanged,theapplicationisnowbackedbyCoreData.Inthenextsection,youwillimplementsavingforboththephotosandtheirassociatedimagedata.

Savingchanges

RecallthatNSManagedObjectchangesdonotpersistuntilyoutellthecontexttosavethesechanges.

OpenCoreDataStack.swiftandaddanewmethodthatsavesthechangestothecontext.funcsaveChanges()throws{varerror:ErrorType?mainQueueContext.performBlockAndWait(){

ifself.mainQueueContext.hasChanges{do{tryself.mainQueueContext.save()}catchletsaveError{error=saveError}}

WOW! eBook www.wowebook.org

Page 619: iOS Programming The Big Nerd Ranch Guide, 5th

}ifleterror=error{throwerror}}

ThesaveChanges()methodwillnothandleanysaveerrorsitself.Instead,itwillthrowtheerrorsothatitispassedalongtothecaller.

OpenPhotoStore.swiftandupdatefetchRecentPhotos(_:)tosavethechangestothecontextafterPhotoentitieshavebeeninsertedintothecontext.funcfetchRecentPhotos(completioncompletion:(PhotosResult)->Void){leturl=FlickrAPI.recentPhotosURL()letrequest=NSURLRequest(URL:url)lettask=session.dataTaskWithRequest(request){(data,response,error)->Voidin

letresult=self.processRecentPhotosRequest(data:data,error:error)varresult=self.processRecentPhotosRequest(data:data,error:error)

ifcaselet.Success(photos)=result{do{tryself.coreDataStack.saveChanges()}catchleterror{result=.Failure(error)}}

completion(result)}task.resume()}

WOW! eBook www.wowebook.org

Page 620: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 621: iOS Programming The Big Nerd Ranch Guide, 5th

UpdatingtheDataSourceOneproblemwiththeappatthemomentisthatfetchRecentPhotos(completion:)onlyreturnsthenewlyinsertedphotos.Nowthattheapplicationsupportssaving,itshouldreturnallofthephotos–thepreviouslysavedphotosaswellasthenewlyinsertedones.YouneedtoaskCoreDataforallofthePhotoentities,andyouwillaccomplishthisusingafetchrequest.

Fetchrequestsandpredicates

TogetobjectsbackfromtheNSManagedObjectContext,youmustprepareandexecuteanNSFetchRequest.Afterafetchrequestisexecuted,youwillgetanarrayofalltheobjectsthatmatchtheparametersofthatrequest.

Afetchrequestneedsanentitydescriptionthatdefineswhichentityyouwanttogetobjectsfrom.TofetchPhotoinstances,youspecifythePhotoentity.Youcanalsosettherequest’ssortdescriptorstospecifytheorderoftheobjectsinthearray.AsortdescriptorhasakeythatmapstoanattributeoftheentityandaBoolthatindicateswhethertheordershouldbeascendingordescending.

ThesortDescriptorspropertyonNSFetchRequestisanarrayofNSSortDescriptorinstances.Whyanarray?Thearrayisusefulifyouthinktheremightbecollisionswhensorting.Forexample,sayyouaresortinganarrayofpeoplebytheirlastnames.Itisentirelypossiblethatmultiplepeoplehavethesamelastname,soyoucanspecifythatpeoplewiththesamelastnameshouldbesortedbytheirfirstnames.ThiswouldbeimplementedbyanarrayoftwoNSSortDescriptorinstances.Thefirstsortdescriptorwouldhaveakeythatmapstotheperson’slastname,andthesecondsortdescriptorwouldhaveakeythatmapstotheperson’sfirstname.

ApredicateisrepresentedbytheNSPredicateclassandcontainsaconditionthatcanbetrueorfalse.Ifyouwantedtofindallphotoswithagivenidentifier,youwouldcreateapredicateandaddittothefetchrequestlikethis:letpredicate=NSPredicate.predicateWithFormat("photoID==\(someIdentifier)")request.predicate=predicate

Theformatstringforapredicatecanbeverylongandcomplex.Apple’sPredicateProgrammingGuideisacompletediscussionofwhatispossible.

YouwanttosortthereturnedinstancesofPhotobydateTakenindescendingorder.Todothis,youwillinstantiateanNSFetchRequest,givingitthe“Photo”entityname.ThenyouwillgivethefetchrequestanarrayofNSSortDescriptorinstances.ForPhotorama,thisarraywillcontainasinglesortdescriptorthatsortsphotosbytheirdateTakenproperties.Finally,youwillaskthemanagedobjectcontexttoexecutethisfetchrequest.

ImporttheCoreDataframeworkatthetopofPhotoStore.swift.importUIKitimportCoreData

WOW! eBook www.wowebook.org

Page 622: iOS Programming The Big Nerd Ranch Guide, 5th

StillinPhotoStore.swift,implementamethodthatwillfetchthePhotoinstancesfromthemainqueuecontext.funcfetchMainQueuePhotos(predicatepredicate:NSPredicate?=nil,sortDescriptors:[NSSortDescriptor]?=nil)throws->[Photo]{

letfetchRequest=NSFetchRequest(entityName:"Photo")fetchRequest.sortDescriptors=sortDescriptorsfetchRequest.predicate=predicate

letmainQueueContext=self.coreDataStack.mainQueueContextvarmainQueuePhotos:[Photo]?varfetchRequestError:ErrorType?mainQueueContext.performBlockAndWait(){do{mainQueuePhotos=trymainQueueContext.executeFetchRequest(fetchRequest)as?[Photo]}catchleterror{fetchRequestError=error}}

guardletphotos=mainQueuePhotoselse{throwfetchRequestError!}

returnphotos}

Afterthewebservicerequestfinishes,youneedtoreturnthephotos.InPhotoStore.swift,updatefetchRecentPhotos(completion:)tofetchthemainqueuephotoswhenthewebservicefinishes.varresult=self.processRecentPhotosRequest(data:data,error:error)

ifcaselet.Success(photos)=result{letmainQueueContext=self.coreDataStack.mainQueueContextmainQueueContext.performBlockAndWait(){try!mainQueueContext.obtainPermanentIDsForObjects(photos)}letobjectIDs=photos.map{$0.objectID}letpredicate=NSPredicate(format:"selfIN%@",objectIDs)letsortByDateTaken=NSSortDescriptor(key:"dateTaken",ascending:true)

do{tryself.coreDataStack.saveChanges()

letmainQueuePhotos=tryself.fetchMainQueuePhotos(predicate:predicate,sortDescriptors:[sortByDateTaken])result=.Success(mainQueuePhotos)}catchleterror{result=.Failure(error)}}

completion(result)

Thiswillonlyreturnthenewlyinsertedphotos.YouwanttheinterfaceforPhotosViewControllertodisplayallofthephotos.

OpenPhotosViewController.swiftandupdateviewDidLoad()tofetchanddisplayallofthephotossavedtoCoreData.overridefuncviewDidLoad()super.viewDidLoad()

collectionView.dataSource=photoDataSourcecollectionView.delegate=self

WOW! eBook www.wowebook.org

Page 623: iOS Programming The Big Nerd Ranch Guide, 5th

store.fetchRecentPhotos(){(photosResult)->Voidin

NSOperationQueue.mainQueue().addOperationWithBlock(){switchphotosResult{caselet.Success(photos):print("Successfullyfound\(photos.count)recentphotos.")self.photoDataSource.photos=photoscaselet.Failure(error):self.photoDataSource.photos.removeAll()print("Errorfetchingrecentphotos:\(error)")}self.collectionView.reloadSections(NSIndexSet(index:0))}

letsortByDateTaken=NSSortDescriptor(key:"dateTaken",ascending:true)letallPhotos=try!self.store.fetchMainQueuePhotos(predicate:nil,sortDescriptors:[sortByDateTaken])

NSOperationQueue.mainQueue().addOperationWithBlock(){self.photoDataSource.photos=allPhotosself.collectionView.reloadSections(NSIndexSet(index:0))}}}

Previouslysavedphotoswillnowbereturnedwhenthewebservicerequestfinishes.Butthereisstilloneproblem:iftheapplicationisrunmultipletimesandthesamephotoisreturnedfromthewebservicerequest,itwillbeinsertedintothecontextmultipletimes.Thisisnotgood–youdonotwantduplicatephotos.Luckilythereisauniqueidentifierforeachphoto.Whentherecentphotoswebservicerequestfinishes,theidentifierforeachphotointheincomingJSONcanbecomparedtothephotosstoredinCoreData.Ifoneisfoundwiththesameidentifier,thatphotowillbereturned.Otherwise,anewphotowillbeinsertedintothecontext.

Todothis,youneedawaytotellthefetchrequestthatitshouldnotreturnallphotosbutinsteadonlythephotosthatmatchsomespecificcriteria.Inthiscase,thespecificcriteriais“onlyphotosthathavethisspecificidentifier,”ofwhichthereshouldeitherbezerooronephoto.InCoreData,thisisdonewithapredicate.

InFlickrAPI.swift,updatephotoFromJSONObject(_:inContext:)tocheckwhetherthereisanexistingphotowithagivenIDbeforeinsertinganewone.privatestaticfuncphotoFromJSONObject(json:[String:AnyObject],inContextcontext:NSManagedObjectContext)->Photo?{

guardletphotoID=json["id"]as?String,title=json["title"]as?String,photoURLString=json["url_h"]as?String,url=NSURL(string:photoURLString),dateString=json["datetaken"]as?String,dateTaken=dateFormatter.dateFromString(dateString)else{

//Don'thaveenoughinformationtoconstructaPhotoreturnnil}

letfetchRequest=NSFetchRequest(entityName:"Photo")letpredicate=NSPredicate(format:"photoID==\(photoID)")fetchRequest.predicate=predicate

varfetchedPhotos:[Photo]!context.performBlockAndWait(){fetchedPhotos=try!context.executeFetchRequest(fetchRequest)as![Photo]}iffetchedPhotos.count>0{returnfetchedPhotos.first

WOW! eBook www.wowebook.org

Page 624: iOS Programming The Big Nerd Ranch Guide, 5th

}

varphoto:Photo!context.performBlockAndWait(){photo=NSEntityDescription.insertNewObjectForEntityForName("Photo",inManagedObjectContext:context)as!Photophoto.title=titlephoto.photoID=photoIDphoto.remoteURL=urlphoto.dateTaken=dateTaken}

returnphoto

}

DuplicatephotoswillnolongerbeinsertedintoCoreData.

Buildandruntheapplication.ThephotoswillappearjustastheydidbeforeintroducingCoreData.AsyoudidinChapter15,closetheapplicationusingtheHomebutton(orShift-Command-Hinthesimulator).LaunchtheapplicationagainandyouwillseethephotosthatCoreDatasavedinthecollectionview.

WOW! eBook www.wowebook.org

Page 625: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 626: iOS Programming The Big Nerd Ranch Guide, 5th

SavingImagestoDiskAlthoughthePhotoentitiesarebeingpersisted,theimagedataassociatedwitheachphotoisnot.Largeblobsofdata,suchasanimage,shouldnotbestoredinCoreDataduetoperformanceconcerns.Instead,youshouldstoredatalikethisonthefilesystem.

Fortheimagedata,youwillusethesameapproachthatyouusedinyourHomepwnerapplication.Infact,youwillusethesameImageStoreclassthatyouwroteforthatproject.

OpenHomepwner.xcodeprojanddragtheImageStore.swiftfilefromtheHomepwnerapplicationtothePhotoramaapplication.MakesuretochooseCopyitemsifneeded.OncetheImageStore.swiftfilehasbeenaddedtoPhotorama,youcanclosetheHomepwnerproject.

BackinPhotorama,openPhotoStore.swiftandgiveitapropertyforanImageStore.classPhotoStore{

letcoreDataStack=CoreDataStack(modelName:"Photorama")letimageStore=ImageStore()

ThenupdatefetchImageForPhoto(_:completion:)tosavetheimagesusingtheimageStore.funcfetchImageForPhoto(photo:Photo,completion:(ImageResult)->Void){

letphotoKey=photo.photoKeyifletimage=photo.image{ifletimage=imageStore.imageForKey(photoKey){photo.image=imagecompletion(.Success(image))return}

letphotoURL=photo.remoteURLletrequest=NSURLRequest(URL:photoURL)

lettask=session.dataTaskWithRequest(request){(data,response,error)->Voidinletresult=self.processImageRequest(data:data,error:error)

ifcaselet.Success(image)=result{photo.image=imageself.imageStore.setImage(image,forKey:photoKey)}

completion(result)}task.resume()}

Buildandruntheapplication.Nowwhentheimagedataisdownloaded,itwillbesavedtothefilesystem.Thenexttimethatphotoisrequested,itwillbeloadedfromthefilesystemifitisnotcurrentlyinmemory.

ThePhotoramaapplicationisnowpersistingitsdatabetweenruns.ThephotometadataisbeingpersistedusingCoreData,andtheimagedataisbeingpersisteddirectlytothefilesystem.Asyouhaveseen,thereisnoone-size-fits-allapproachtodatapersistence.Instead,eachpersistencemechanismhasitsownsetofbenefitsanddrawbacks.Inthis

WOW! eBook www.wowebook.org

Page 627: iOS Programming The Big Nerd Ranch Guide, 5th

chapter,youhaveexploredoneofthose,CoreData,butyouhaveonlyseenthetipoftheiceberg.InChapter22,youwillexploretheCoreDataframeworkfurthertolearnaboutrelationshipsandperformance.

WOW! eBook www.wowebook.org

Page 628: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 629: iOS Programming The Big Nerd Ranch Guide, 5th

BronzeChallenge:PhotoViewCountAddanattributetothePhotoentitythattrackshowmanytimesaphotoisviewed.DisplaythisnumbersomewhereonthePhotoInfoViewControllerinterface.

WOW! eBook www.wowebook.org

Page 630: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 631: iOS Programming The Big Nerd Ranch Guide, 5th

22CoreDataRelationships

CoreDataisnotthatexcitingwithjustoneentity.MuchofthepowerbehindCoreDatacomestolightwhentherearemultipleentitiesthatarerelatedtooneanother,becauseCoreDatamanagesrelationshipsbetweenentities.

Inthischapter,youaregoingtoaddtagstothephotosinPhotoramawithlabelssuchas“Nature,”“Electronics,”or“Selfies.”Userswillbeabletoaddoneormoretagstophotosandalsocreatetheirowncustomtags(Figure22.1).

Figure22.1FinalPhotoramaapplication

WOW! eBook www.wowebook.org

Page 632: iOS Programming The Big Nerd Ranch Guide, 5th

RelationshipsOneofthebenefitsofusingCoreDataisthatentitiescanberelatedtooneanotherinawaythatallowsyoutodescribecomplexmodels.Relationshipsbetweenentitiesarerepresentedbyreferencesbetweenobjects.Therearetwokindsofrelationships:to-oneandto-many.

Whenanentityhasato-onerelationship,eachinstanceofthatentitywillhaveareferencetoaninstanceintheentityithasarelationshipto.

Whenanentityhasato-manyrelationship,eachinstanceofthatentityhasareferencetoaSet.Thissetcontainstheinstancesoftheentitythatithasarelationshipwith.Toseethisinaction,youaregoingtoaddanewentitytothemodelfile.

ReopenthePhotoramaapplication.InPhotorama.xcdatamodeld,addanotherentitycalledTag.GiveitanattributecallednameoftypeString.Tagwillallowuserstotagphotos.

Aphotomighthavemultipletagsthatdescribeit,andatagmightbeassociatedwithmultiplephotos.Forexample,apictureofaniPhonemightbetagged“Electronics”and“Apple,”andapictureofaBetamaxplayermightbetagged“Electronics”and“Rare.”TheTagentitywillhaveato-manyrelationshiptothePhotoentitybecausemanyinstancesofPhotocanhavethesameTag.AndthePhotoentitywillhaveato-manyrelationshiptotheTagentitybecauseaphotocanbeassociatedwithmanyTags.

AsFigure22.2shows,aPhotowillhaveareferencetoasetofTags,andaTagwillhaveareferencetoasetofPhotos.

WOW! eBook www.wowebook.org

Page 633: iOS Programming The Big Nerd Ranch Guide, 5th

Figure22.2EntitiesinPhotorama

Whentheserelationshipsaresetup,youwillbeabletoaskaPhotoobjectforthesetofTagobjectsthatitisassociatedwithandaskaTagobjectforthesetofPhotoobjectsthatitisassociatedwith.

Toaddthesetworelationshipstothemodelfile,firstselecttheTagentityandclickthe+buttonintheRelationshipssection.IntheRelationshipcolumn,nametherelationshipphotos.IntheDestinationcolumn,selectPhoto.Inthedatamodelinspector,changetheTypedropdownfromToOnetoToManyandunchecktheOptionalcheckbox(Figure22.3).

Figure22.3Creatingthephotosrelationship

WOW! eBook www.wowebook.org

Page 634: iOS Programming The Big Nerd Ranch Guide, 5th

Next,selectthePhotoentity.AddarelationshipnamedtagsandpickTagasitsdestination.Inthedatamodelinspector,changetheTypedropdowntoToManyanduncheckitsOptionalcheckbox.

Nowthatyouhavetwounidirectionalrelationships,youcanmakethemintoaninverserelationship.Aninverserelationshipisabidirectionalrelationshipbetweentwoentities.WithaninverserelationshipsetupbetweenPhotoandTag,CoreDatacanensurethatyourobjectgraphremainsinaconsistentstatewhenanychangesaremade.

Tocreatetheinverserelationship,clickNoInverseintheInversecolumnandselectphotos(Figure22.4).IfyoureturntotheTagentity,youwillseethatthephotosrelationshipnowshowstagsasitsinverse.

Figure22.4Creatingthetagsrelationship

NowopenPhoto+CoreDataProperties.swift.Addthetagsproperty,whichwillbeaSetofNSManagedObjectinstances.extensionPhoto{

@NSManagedvarphotoID:String@NSManagedvarphotoKey:String@NSManagedvartitle:String@NSManagedvardateTaken:NSDate@NSManagedvarremoteURL:NSURL@NSManagedvartags:Set<NSManagedObject>

}

WOW! eBook www.wowebook.org

Page 635: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 636: iOS Programming The Big Nerd Ranch Guide, 5th

AddingTagstotheInterfaceWhenusersnavigatetoaspecificphoto,theycurrentlyseeonlythetitleofthephotoandtheimageitself.Let’supdatetheinterfacetoincludeaphoto’sassociatedtags.

OpenMain.storyboardandnavigatetotheinterfaceforPhotoInfoViewController.Addatoolbartothebottomoftheview.UpdatetheAutoLayoutconstraintssothatthetoolbarisanchoredtothebottom,justasitwasinHomepwner.ThebottomconstraintfortheimageViewshouldbeanchoredtothetopofthetoolbarinsteadofthebottomofthesuperview.AddaUIBarButtonItemtothetoolbar,ifoneisnotalreadypresent,andgiveitatitleofTags.YourinterfacewilllooklikeFigure22.5.

Figure22.5PhotoInfoViewControllerinterface

CreateanewSwiftfilenamedTagsViewController.OpenthisfileanddeclaretheTagsViewControllerclassasasubclassofUITableViewController.ImportUIKitandCoreDatainthisfile.importFoundationimportUIKitimportCoreData

WOW! eBook www.wowebook.org

Page 637: iOS Programming The Big Nerd Ranch Guide, 5th

classTagsViewController:UITableViewController{

}

TheTagsViewControllerwilldisplayalistofallthetags.Theuserwillseeandbeabletoselectthetagsthatareassociatedwithaspecificphoto.Theuserwillalsobeabletoaddnewtagsfromthisscreen.ThecompletedinterfacewilllooklikeFigure22.6.

Figure22.6TagsViewController

GivetheTagsViewControllerclassapropertytoreferencethePhotoStoreaswellasaspecificPhoto.Youwillalsoneedapropertytokeeptrackofthecurrentlyselectedtags,whichyouwilltrackusinganarrayofNSIndexPathinstances.classTagsViewController:UITableViewController{

varstore:PhotoStore!varphoto:Photo!

varselectedIndexPaths=[NSIndexPath]()}

Thedatasourceforthetableviewwillbeaseparateclass.AswediscussedwhenyoucreatedPhotoDataSourceinChapter20,anapplicationwhosetypeshaveasingleresponsibilityiseasiertoadapttofuturechanges.Thisclasswillberesponsiblefordisplayingthelistoftagsinthetableview.

CreateanewSwiftfilenamedTagDataSource.swift.DeclaretheTagDataSourceclassandimplementthetableviewdatasourcemethods.YouwillneedtoimportUIKitandCoreData.importFoundationimportUIKitimportCoreData

classTagDataSource:NSObject,UITableViewDataSource{

vartags:[NSManagedObject]=[]

functableView(tableView:UITableView,

WOW! eBook www.wowebook.org

Page 638: iOS Programming The Big Nerd Ranch Guide, 5th

numberOfRowsInSectionsection:Int)->Int{returntags.count}

functableView(tableView:UITableView,cellForRowAtIndexPathindexPath:NSIndexPath)->UITableViewCell{

letcell=tableView.dequeueReusableCellWithIdentifier("UITableViewCell",forIndexPath:indexPath)

lettag=tags[indexPath.row]letname=tag.valueForKey("name")as!Stringcell.textLabel?.text=name

returncell}

}

OpenPhotoStore.swiftanddefineanewmethodthatfetchesallthetagsfromthemainqueue.funcfetchMainQueueTags(predicatepredicate:NSPredicate?=nil,sortDescriptors:[NSSortDescriptor]?=nil)throws->[NSManagedObject]{letfetchRequest=NSFetchRequest(entityName:"Tag")fetchRequest.predicate=predicatefetchRequest.sortDescriptors=sortDescriptors

letmainQueueContext=self.coreDataStack.mainQueueContextvarmainQueueTags:[NSManagedObject]?varfetchRequestError:ErrorType?mainQueueContext.performBlockAndWait({do{mainQueueTags=trymainQueueContext.executeFetchRequest(fetchRequest)as?[NSManagedObject]}catchleterror{fetchRequestError=error}})

guardlettags=mainQueueTagselse{throwfetchRequestError!}

returntags}

Withthedatasourceandfetchrequestcomplete,youhavefinishedtheimplementationofTagsViewController.

OpenTagsViewController.swiftandsetthedataSourceforthetableviewtobeaninstanceofTagDataSource.classTagsViewController:UITableViewController{

varstore:PhotoStore!varphoto:Photo!

varselectedIndexPaths=[NSIndexPath]()

lettagDataSource=TagDataSource()

overridefuncviewDidLoad(){super.viewDidLoad()

tableView.dataSource=tagDataSource}}

Nowfetchthemainqueuetagsandassociatethemwiththetagspropertyonthedata

WOW! eBook www.wowebook.org

Page 639: iOS Programming The Big Nerd Ranch Guide, 5th

source.overridefuncviewDidLoad(){super.viewDidLoad()

tableView.dataSource=tagDataSource

updateTags()}

funcupdateTags(){lettags=try!store.fetchMainQueueTags(predicate:nil,sortDescriptors:[NSSortDescriptor(key:"name",ascending:true)])tagDataSource.tags=tags}

TheTagsViewControllerneedstomanagetheselectionoftagsandupdatethePhotoinstancewhentheuserselectsordeselectsatag.

AddtheappropriateUITableViewDelegatemethodsinTagsViewController.swift.AlsoaddtheappropriateindexpathstotheselectedIndexPathsarray.overridefuncviewDidLoad(){super.viewDidLoad()

tableView.dataSource=tagDataSourcetableView.delegate=selfupdateTags()}

funcupdateTags(){lettags=try!store.fetchMainQueueTags(predicate:nil,sortDescriptors:[NSSortDescriptor(key:"name",ascending:true)])tagDataSource.tags=tags

fortaginphoto.tags{ifletindex=tagDataSource.tags.indexOf(tag){letindexPath=NSIndexPath(forRow:index,inSection:0)selectedIndexPaths.append(indexPath)}}}

overridefunctableView(tableView:UITableView,didSelectRowAtIndexPathindexPath:NSIndexPath){

lettag=tagDataSource.tags[indexPath.row]

ifletindex=selectedIndexPaths.indexOf(indexPath){selectedIndexPaths.removeAtIndex(index)}else{selectedIndexPaths.append(indexPath)}

tableView.reloadRowsAtIndexPaths([indexPath],withRowAnimation:.Automatic)}

overridefunctableView(tableView:UITableView,willDisplayCellcell:UITableViewCell,forRowAtIndexPathindexPath:NSIndexPath){

ifselectedIndexPaths.indexOf(indexPath)!=nil{cell.accessoryType=.Checkmark}else{cell.accessoryType=.None}}

Currently,thetagsarenotbeingaddedorremovedfromthePhoto.Addingand

WOW! eBook www.wowebook.org

Page 640: iOS Programming The Big Nerd Ranch Guide, 5th

removingobjectsfromaCoreDatato-manyrelationshipisalittlebizarreduetothedynamicnatureofNSManagedObjectcombinedwiththeimmutabilityoftheSetthatCoreDatareturns.Toallowforchanges,ApplehasprovidedthemethodmutableSetValueForKey(_:).Thismethodreturnsanobjectthatisamutableset,andyouthenmakeanychangestothisobject.

OpenPhoto.swiftanddefinetwomethodsthathandleaddingandremovingTaginstancesfromthetagsset.funcaddTagObject(tag:NSManagedObject){letcurrentTags=mutableSetValueForKey("tags")currentTags.addObject(tag)}

funcremoveTagObject(tag:NSManagedObject){letcurrentTags=mutableSetValueForKey("tags")currentTags.removeObject(tag)}

ByusingthemutableSetValueForKey(_:)method,CoreDatawillbeinformedofanychangestotheto-manyrelationshipandwillallowthechangestopersist.

YoucannowusethesetwomethodsonPhototoupdatethetagsassociatedwithaphoto.

InTagsViewController.swift,updatetableView(_:didSelectRowAtIndexPath:)toaddorremovetheassociatedTagfromthePhoto.Alsosavethechangestothecontextwhentheusermakesaselection.overridefunctableView(tableView:UITableView,didSelectRowAtIndexPathindexPath:NSIndexPath){

lettag=tagDataSource.tags[indexPath.row]

ifletindex=find(selectedIndexPaths,indexPath){selectedIndexPaths.removeAtIndex(index)photo.removeTagObject(tag)}else{selectedIndexPaths.append(indexPath)photo.addTagObject(tag)}

tableView.reloadRowsAtIndexPaths([indexPath],withRowAnimation:.Automatic)

do{trystore.coreDataStack.saveChanges()}catchleterror{print("CoreDatasavefailed:\(error)")}}

Let’ssetupTagsViewControllertobepresentedmodallywhentheusertapstheTagsbarbuttonitemonthePhotoInfoViewController.

OpenMain.storyboardanddragaNavigationControllerontothecanvas.ThisshouldgiveyouaUINavigationControllerwitharootviewcontrollerthatisaUITableViewController.IftherootviewcontrollerisnotaUITableViewController,deletetherootviewcontroller,dragaTableViewControllerontothecanvas,andmakeittherootviewcontrolleroftheNavigationController.

Control-dragfromtheTagsitemonPhotoInfoViewControllertothenewNavigationControllerandWOW! eBook

www.wowebook.org

Page 641: iOS Programming The Big Nerd Ranch Guide, 5th

selectthePresentModallyseguetype(Figure22.7).OpentheattributesinspectorforthesegueandgiveitanIdentifiernamedShowTags.

Figure22.7Addingthetagsviewcontroller

SelecttheRootViewControllerthatyoujustaddedtothecanvasandopenitsidentityinspector.ChangeitsClasstoTagsViewController.Thisnewviewcontrollerdoesnothaveanavigationitemassociatedwithit,sofindNavigationItemintheobjectlibraryanddragitontotheviewcontroller.Double-clickthenewnavigationitem’sTitlelabelandchangeittoTags.

Next,theUITableViewCellontheTagsViewControllerinterfaceneedstomatchwhattheTagDataSourceexpects.Itneedstousethecorrectstyleandhavethecorrectreuseidentifier.

WOW! eBook www.wowebook.org

Page 642: iOS Programming The Big Nerd Ranch Guide, 5th

SelecttheUITableViewCell.(Itmightbeeasiertoselectinthedocumentoutline.)Openitsattributesinspector.ChangetheStyletoBasicandsettheIdentifiertoUITableViewCell(Figure22.8).

Figure22.8ConfiguringtheUITableViewCell

Now,theTagsViewControllerneedstwobarbuttonitemsonitsnavigationbar:aDonebuttonthatdismissestheviewcontrolleranda+buttonthatallowstheusertoaddanewtag.

DragabarbuttonitemtotheleftandrightbarbuttonitemslotsfortheTagsViewController.SettheleftitemtousetheDonestyleandsystemitem.SettherightitemtousetheBorderedstyleandanAddsystemitem(Figure22.9).

Figure22.9Barbuttonitemattributes

CreateandconnectanactionforeachoftheseitemstotheTagsViewController.TheDoneitemshouldbeconnectedtoamethodnameddone(_:),andthe+itemshouldbeconnectedtoamethodnamedaddNewTag(_:).ThetwomethodsinTagsViewController.swiftwillbe:@IBActionfuncdone(sender:AnyObject){

}

@IBActionfuncaddNewTag(sender:AnyObject){

}

Theimplementationofdone(_:)issimple:theviewcontrollerjustneedstobe

WOW! eBook www.wowebook.org

Page 643: iOS Programming The Big Nerd Ranch Guide, 5th

dismissed.Implementthisfunctionalityindone(_:).@IBActionfuncdone(sender:AnyObject){presentingViewController?.dismissViewControllerAnimated(true,completion:nil)}

Whentheusertapsthe+item,analertwillbepresentedthatwillallowtheusertotypeinthenameforanewtag.

Figure22.10Addinganewtag

SetupandpresentaninstanceofUIAlertControllerinaddNewTag(_:).@IBActionfuncaddNewTag(sender:AnyObject){letalertController=UIAlertController(title:"AddTag",message:nil,preferredStyle:.Alert)

alertController.addTextFieldWithConfigurationHandler({(textField)->VoidintextField.placeholder="tagname"textField.autocapitalizationType=.Words})

letokAction=UIAlertAction(title:"OK",style:.Default,handler:{(action)->Voidin

})alertController.addAction(okAction)

letcancelAction=UIAlertAction(title:"Cancel",style:.Cancel,handler:nil)alertController.addAction(cancelAction)

presentViewController(alertController,animated:true,completion:nil)}

UpdatethecompletionhandlerfortheokActiontoinsertanewTagintothecontext.Thensavethecontext,updatethelistoftags,andreloadthetableviewsection.letokAction=UIAlertAction(title:"OK",style:.Default,handler:{(action)->Voidin

iflettagName=alertController.textFields?.first!.text{letcontext=self.store.coreDataStack.mainQueueContextletnewTag=NSEntityDescription.insertNewObjectForEntityForName("Tag",inManagedObjectContext:context)newTag.setValue(tagName,forKey:"name")

do{tryself.store.coreDataStack.saveChanges()}catchleterror{

WOW! eBook www.wowebook.org

Page 644: iOS Programming The Big Nerd Ranch Guide, 5th

print("CoreDatasavefailed:\(error)")}self.updateTags()self.tableView.reloadSections(NSIndexSet(index:0),withRowAnimation:.Automatic)}})alertController.addAction(okAction)

Finally,whentheTagsbarbuttonitemonPhotoInfoViewControlleristapped,thePhotoInfoViewControllerneedstopassalongitsstoreandphotototheTagsViewController.

OpenPhotoInfoViewControllerandimplementprepareForSegue(_:).overridefuncprepareForSegue(segue:UIStoryboardSegue,sender:AnyObject?){ifsegue.identifier=="ShowTags"{letnavController=segue.destinationViewControlleras!UINavigationControllerlettagController=navController.topViewControlleras!TagsViewController

tagController.store=storetagController.photo=photo}}

Buildandruntheapplication.NavigatetoaphotoandtaptheTagsitemonthetoolbaratthebottom.TheTagsViewControllerwillbepresentedmodally.Tapthe+item,enteranewtag,andselectthenewtagtoassociateitwiththephoto.

WOW! eBook www.wowebook.org

Page 645: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 646: iOS Programming The Big Nerd Ranch Guide, 5th

Parent-ChildContextsYoulearnedinChapter21thataninstanceofNSManagedObjectContextisassociatedwithaspecificqueue,whichisthemainqueueinthecaseofthemainQueueContext.AsinglecontextassociatedwiththemainqueueistheminimumthatyouneedforaworkingCoreDataapplication,butanyCoreDataoperationsthattakealongtimewillnoticeablyblockthemainqueue,resultinginanunresponsiveapplication.

Toaddressthisproblem,applicationsusingCoreDataoftenhavemultiplecontexts.Photoramawillhaveonecontextassociatedwiththemainqueueandanothercontextassociatedwithabackgroundqueue.

OpenCoreDataStack.swift.Addanothercontextthatisassociatedwithaprivate,orbackground,queue.lazyvarprivateQueueContext:NSManagedObjectContext={

letmoc=NSManagedObjectContext(concurrencyType:.PrivateQueueConcurrencyType)moc.parentContext=self.mainQueueContextmoc.name="PrimaryPrivateQueueContext"

returnmoc}()

AnNSManagedObjectContextcaneitherbeassociatedwithanNSPersistentStoreCoordinatororwithaparentNSManagedObjectContext.Thecontextatthe“top”ofthestackmustbeassociatedwithapersistentstorecoordinator.

Sohowdoesthiswork?Whenyousaveacontextassociatedwithapersistentstorecoordinator,thosechangesarepersistedtothestore.Whenyousaveacontextthathasaparentcontext,anychangesfromthechildcontextpropagateuponlyoneleveltotheparentcontext.Atthispoint,theparentcontextwillhavenewchanges.Thesenewchangeswillnotautomaticallybesaved,soyouwillalsoneedtosavetheparentcontext.

InCoreDataStack.swift,updatesaveChanges(_:)tosupportthisfunctionality.funcsaveChanges()throws{varerror:ErrorType?

privateQueueContext.performBlockAndWait{()->Voidinifself.privateQueueContext.hasChanges{do{tryself.privateQueueContext.save()}catchletsaveError{error=saveError}}}

ifleterror=error{throwerror}

mainQueueContext.performBlockAndWait{()->Voidinifself.mainQueueContext.hasChanges{do{tryself.mainQueueContext.save()}

WOW! eBook www.wowebook.org

Page 647: iOS Programming The Big Nerd Ranch Guide, 5th

catchletsaveError{error=saveError}}}

ifleterror=error{throwerror}}

First,anychangesfromtheprivateQueueContextarepropagateduptothemainQueueContext.Then,anychangestothemainQueueContextwillbepersistedusingitspersistentstorecoordinator.

Nowlet’susethisnewcontexttoimprovetheperformanceofPhotorama.Currently,thePhotoentitiesareaddedonthemainqueue,whichcanblockotheroperations.Youaregoingtoupdatetheapplicationtoaddentitiesonthebackgroundqueueinstead.

OpenPhotoStore.swiftandupdateprocessRecentPhotosRequest(data:error:)tousetheprivateQueueContext.funcprocessRecentPhotosRequest(datadata:NSData?,error:NSError?)->PhotosResult{

guardletjsonData=dataelse{return.Failure(error!)}

returnFlickrAPI.photosFromJSONData(jsonData,inContext:self.coreDataStack.mainQueueContext)

returnFlickrAPI.photosFromJSONData(jsonData,inContext:self.coreDataStack.privateQueueContext)}

NowupdatefetchRecentPhotos(completion:)togetthepermanentIDsforthephotosfromtheprivatequeuecontext,sincethatisthecontextthatthephotosarebeingaddedtonow.funcfetchRecentPhotos(completioncompletion:(PhotosResult)->Void){leturl=FlickrAPI.recentPhotosURL()letrequest=NSURLRequest(URL:url)lettask=session.dataTaskWithRequest(request,completionHandler:{(data,response,error)->Voidin

varresult=self.processRecentPhotosRequest(data:data,error:error)

ifcaselet.Success(photos)=result{letmainQueueContext=self.coreDataStack.mainQueueContextmainQueueContext.performBlockAndWait({try!mainQueueContext.obtainPermanentIDsForObjects(photos)})

letprivateQueueContext=self.coreDataStack.privateQueueContextprivateQueueContext.performBlockAndWait({try!privateQueueContext.obtainPermanentIDsForObjects(photos)})

...

Buildandruntheapplication.Althoughthebehaviorhasnotchanged,theapplicationisnolongerindangerofbecomingunresponsivewhilenewphotosarebeingadded.Asthescaleofyourapplicationsincreases,handlingCoreDataentitiessomewhereotherthanthemainqueue(likeyouhavedonehere)canresultinhugeperformancewins.

WOW! eBook www.wowebook.org

Page 648: iOS Programming The Big Nerd Ranch Guide, 5th

Congratulations!Overthepastfourchapters,youhaveworkedonarathercomplexapp.Photoramaisabletomakemultiplewebservicecalls,displayphotosinagrid,cacheimagedatatothefilesystem,andpersistphotodatausingCoreData.Toaccomplishthis,youusedknowledgethatyouhavegainedthroughoutthisbook,andyouappliedthatknowledgetocreateanawesomeappthatisalsorobustandmaintainable.Itwashardwork,andyoushouldbeproudofyourself.

WOW! eBook www.wowebook.org

Page 649: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 650: iOS Programming The Big Nerd Ranch Guide, 5th

SilverChallenge:FavoritesAllowtheusertofavoritephotos.Becreativeinhowyoupresentthefavoritephotostotheuser.TwopossibilitiesincludeviewingthemusingaUITabBarControlleroraddingaUISegmentedControltothePhotosViewControllerthatswitchesbetweenallphotosandfavoritephotos.(Hint:youwillneedtoaddanewattributetothePhotoentity.)

WOW! eBook www.wowebook.org

Page 651: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 652: iOS Programming The Big Nerd Ranch Guide, 5th

23Afterword

Welcometotheendofthebook!Youshouldbeveryproudofallyourworkandallthatyouhavelearned.Nowthereisgoodnewsandbadnews:

Thegoodnews:ThestuffthatleavesprogrammersbefuddledwhentheycometotheiOSplatformisbehindyounow.YouareaniOSdeveloper.

Thebadnews:YouareprobablynotaverygoodiOSdeveloper.

WOW! eBook www.wowebook.org

Page 653: iOS Programming The Big Nerd Ranch Guide, 5th

WhattoDoNextItisnowtimetomakesomemistakes,readsomereallytediousdocumentation,andbehumbledbytheheartlessexpertswhowillridiculeyourquestions.Hereiswhatwerecommend:

Writeappsnow.Ifyoudonotimmediatelyusewhatyouhavelearned,itwillfade.Exerciseandextendyourknowledge.Now.

Godeep.Thisbookhasconsistentlyfavoredbreadthoverdepth;anychaptercouldhavebeenexpandedintoanentirebook.Findatopicthatyoufindinterestingandreallywallowinit–dosomeexperiments,readApple’sdocsonthetopic,readapostingonablogoronStackOverflow.

Connect.ThereisaniOSDeveloperMeetupinmostcities,andthetalksaresurprisinglygood.ThereareCocoaHeadschaptersaroundtheworld.Therearediscussiongroupsonline.Ifyouaredoingaproject,findpeopletohelpyou:designers,testers(AKAguineapigs),andotherdevelopers.

Makemistakesandfixthem.Youwilllearnalotonthedayswhenyousay,“Thisapplicationhasbecomeaballofcrap!I’mgoingtothrowitawayandwriteitagainwithanarchitecturethatmakessense.”Politeprogrammerscallthisrefactoring.

Giveback.Sharetheknowledge.Answeradumbquestionwithgrace.Giveawaysomecode.

WOW! eBook www.wowebook.org

Page 654: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 655: iOS Programming The Big Nerd Ranch Guide, 5th

ShamelessPlugsYoucanfindusonTwitter,wherewekeepyouinformedaboutprogrammingandentertainedaboutlife:@cbkeurand@aaronhillegass.

KeepaneyeoutforotherguidesfromBigNerdRanch.Wealsoofferweek-longcoursesfordevelopers.Andifyoujustneedsomecodewritten,wedocontractprogramming.Formoreinformation,visitourwebsiteathttp://www.bignerdranch.com/.

You,dearreader,makeourlivesofwriting,coding,andteachingpossible.Sothankyouforbuyingourbook.

WOW! eBook www.wowebook.org

Page 656: iOS Programming The Big Nerd Ranch Guide, 5th

WOW! eBook www.wowebook.org

Page 657: iOS Programming The Big Nerd Ranch Guide, 5th

IndexABCDEFGHIJKLMNOPQRSTUVWX

Symbols

.xcassets(assetcatalog),ApplicationIcons

.xcdatamodeld(datamodelfile),Modelingentities//MARK:,//MARK:@IBInspectable,@IBInspectable

A

accesscontrol,NSURLComponentsaccessoryindicator(UITableViewCell),UITableViewCellsactionmethods

connectingininterfacefile,Addingacamerabutton

defining,Definingactionmethods

implementing,Implementingactionmethods

andUIControl,FortheMoreCurious:UIControl

activestate,ApplicationStatesandTransitionsaddSubview(_:),ViewsandFramesalerts,displaying,DisplayingUserAlertsalignmentrectangles,Alignmentrectangleandlayoutattributesanchors,AnchorsanimateWithDuration:animations:,BasicAnimationsanimations

animatingconstraints,AnimatingConstraints

basic,BasicAnimations

markingcompletionof,AnimationCompletion

spring-like,BronzeChallenge:SpringAnimations

timingfunctions,TimingFunctions

anti-aliasing,FortheMoreCurious:RetinaDisplayAPIReference,Archivingappend(_:),Instancemethodsapplicationbundle

explained,FortheMoreCurious:TheApplicationBundle

WOW! eBook www.wowebook.org

Page 658: iOS Programming The Big Nerd Ranch Guide, 5th

andinternationalization,Baseinternationalization,FortheMoreCurious:NSBundle’sRoleinInternationalization

applicationsandbox,ApplicationSandbox,FortheMoreCurious:TheApplicationBundleapplicationstates,ApplicationStatesandTransitions,FortheMoreCurious:ApplicationStateTransitionsapplicationDidBecomeActive:,ApplicationStatesandTransitions,FortheMoreCurious:ApplicationStateTransitionsapplicationDidEnterBackground(_:),NSKeyedArchiverandNSKeyedUnarchiver,FortheMoreCurious:ApplicationStateTransitionsapplicationDidEnterBackground:,ApplicationStatesandTransitionsapplications

(seealsoapplicationbundle,debugging,projects)

building,Runningonthesimulator,Localization

cleaning,Localization

datastorage,ApplicationSandbox

directoriesin,ApplicationSandbox

iconsfor,ApplicationIcons

launchimagesfor,LaunchScreen

multiplethreadsin,TheMainThread

runningoniPad,CreatinganXcodeProject

runningonsimulator,Runningonthesimulator

applicationWillEnterForeground:,ApplicationStatesandTransitions,FortheMoreCurious:ApplicationStateTransitionsapplicationWillResignActive:,ApplicationStatesandTransitions,FortheMoreCurious:ApplicationStateTransitionsarchiving

vs.CoreData,CoreData

described,Archiving

implementing,Archiving

withNSKeyedArchiver,NSKeyedArchiverandNSKeyedUnarchiver

arrays

about,Collectiontypes

append(_:),Instancemethods

count,Properties

reverse(),Instancemethods

subscripting,Literalsandsubscripting

WOW! eBook www.wowebook.org

Page 659: iOS Programming The Big Nerd Ranch Guide, 5th

andtraps,Literalsandsubscripting

writingtofilesystem,FortheMoreCurious:ReadingandWritingtotheFilesystem

assetcatalogs(Xcode),ApplicationIconsassistanteditor(Xcode),Addingacamerabuttonattributes(CoreData),EntitiesAutoLayout

(seealsoconstraints(AutoLayout),InterfaceBuilder)

alignmentrectangles,Alignmentrectangleandlayoutattributes

autoresizingmasksand,FortheMoreCurious:NSAutoresizingMaskLayoutConstraint

dynamiccellheights,DynamicCellHeights

introductionto,AbriefintroductiontoAutoLayout

layoutattributes,Alignmentrectangleandlayoutattributes

purposeof,TheAutoLayoutSystem

autoresizingmasks,ProgrammaticConstraints,FortheMoreCurious:NSAutoresizingMaskLayoutConstraintawakeFromInsert,NSManagedObjectandsubclasses

B

backgroundstate,ApplicationStatesandTransitions,FortheMoreCurious:ApplicationStateTransitionsBaseinternationalization,Baseinternationalizationbaselines,Alignmentrectangleandlayoutattributesbasicanimations,BasicAnimationsbecomeFirstResponder,DismissingthekeyboardBool,NumberandBooleantypesbooleantypes,NumberandBooleantypesbundles

application(seeapplicationbundle)

NSBundle,FortheMoreCurious:NSBundle’sRoleinInternationalization,FortheMoreCurious:TheApplicationBundle

buttons

addingtonavigationbars,Addingbuttonstothenavigationbar

camera,Addingacamerabutton

C

CALayer,TheViewHierarchycallbacks,Delegation

WOW! eBook www.wowebook.org

Page 660: iOS Programming The Big Nerd Ranch Guide, 5th

(seealsodelegation,target-actionpairs)

camera

(seealsoimages)

takingpictures,Addingacamerabutton

cancelsTouchesInView,UIPanGestureRecognizerandsimultaneousrecognizerscanPerformAction(_:withSender:),FortheMoreCurious:UIMenuControllerandUIResponderStandardEditActionscells

(seealsoUITableViewCell)

addingpaddingto,ContentInsets

changingcellclass,CreatingItemCell

customizinglayoutof,CustomizingtheLayout

dynamiccellheights,DynamicCellHeights

prototype,ReusingUITableViewCells,CreatingaCustomUICollectionViewCell

reusing,ReusingUITableViewCells

CGPoint,ViewsandFramesCGRect,ViewsandFramesCGSize,ViewsandFramesclosures,Closures,ConstructingafileURLcollectionview

customizinglayoutof,CustomizingtheLayout

displaying,DisplayingtheGrid

downloadingimagedata,DownloadingtheImageData

layoutobject,CollectionViews

navigatingto/displayingphotos,NavigatingtoaPhoto

settingdatasource,CollectionViewDataSource

colors

background,ViewsandFrames,DisplayingtheGrid

customizing,Customizingthelabels

commonancestor,Activatingconstraintsconcurrency,TheMainThreadconditionals

if-let,Optionals

switch,EnumerationsandtheSwitchStatement

WOW! eBook www.wowebook.org

Page 661: iOS Programming The Big Nerd Ranch Guide, 5th

connections(inInterfaceBuilder),Makingconnectionsconnectionsinspector,Summaryofconnectionsconsole

printingto,Usingadelegate

viewinginplayground,Optionals

constants,UsingStandardTypesconstraints(AutoLayout)

activatingprogrammatically,Activatingconstraints

addingtolabels,Preparingforlocalization

animating,AnimatingConstraints

clearing,AbriefintroductiontoAutoLayout,Addingmoreconstraints

collectionview,DisplayingtheGrid

creatingexplicitconstraints,Explicitconstraints

creatinginInterfaceBuilder,Constraints

creatingprogrammatically,ProgrammaticConstraints

implicit,Implicitconstraints

nearestneighborand,Constraints

overview,Constraints

pin,AddingconstraintsinInterfaceBuilder

resolvingunsatisfiable,FortheMoreCurious:NSAutoresizingMaskLayoutConstraint

specifying,AbriefintroductiontoAutoLayout

constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:,Explicitconstraintscontentcompression,ContentcompressionresistanceprioritiescontentMode(UIImageView),DisplayingImagesandUIImageViewcontentView(UITableViewCell),UITableViewCellscontrolevents,FortheMoreCurious:UIControlcontrollerobjects,Model-View-Controllercontrols,programmatic,ProgrammaticControlsCoreData

vs.archiving,CoreData

attributes,Entities

fetchrequests,Fetchrequestsandpredicates

persistentstoreformats,NSPersistentStoreCoordinator

relationshipmanagementwith,CoreDataRelationships

WOW! eBook www.wowebook.org

Page 662: iOS Programming The Big Nerd Ranch Guide, 5th

roleof,Entities

subclassingNSManagedObject,NSManagedObjectandsubclasses

transformingvalues,Transformableattributes

CoreGraphics,FortheMoreCurious:RetinaDisplaycount(arrays),PropertiescurrentLocale,Formatters

D

datasourcemethods,Implementingdatasourcemethods,DesignPatterns,CollectionViewDataSource,UpdatingtheDataSourcedatastorage

(seealsoarchiving,CoreData)

forapplicationdata,ApplicationSandbox

binary,WritingtotheFilesystemwithNSData,FortheMoreCurious:ReadingandWritingtotheFilesystem

withNSData,WritingtotheFilesystemwithNSData

dataSource(UITableView),UITableViewController,UITableView’sDataSource,Implementingdatasourcemethodsdebugging

(seealsodebuggingtools,exceptions)

debuggingtools

issuenavigator,BuildingtheFinishedApplication

Xcode,Optionals

declarations

protocol,Conformingtoaprotocol

default:(switchstatement),EnumerationsandtheSwitchStatementdelegation

designpattern,DesignPatterns

forUIImagePickerController,Settingtheimagepicker’sdelegate

forUITableView,UITableViewController

overview,Delegation

UICollectionViewDelegate,DownloadingtheImageData

deleteRowsAtIndexPaths(_:withRowAnimation:),DeletingRowsdependencyinjection,Givingthecontrolleraccesstothestoredependencyinversionprinciple,Givingthecontrolleraccesstothestore

WOW! eBook www.wowebook.org

Page 663: iOS Programming The Big Nerd Ranch Guide, 5th

designpatterns,DesignPatternsDetailViewController,WrappingUpImageStore,SizeClassesdeveloperdocumentation,Archivingdevices

checkingforcamera,Settingtheimagepicker’ssourceType

displayresolution,ViewsandFrames

Retinadisplay,ApplicationIcons,FortheMoreCurious:RetinaDisplay

dictionaries

(seealsoJSONdata)

about,Collectiontypes

accessing,Subscriptingdictionaries

subscripting,Subscriptingdictionaries

using,CreatingandUsingKeys

writingtofilesystem,FortheMoreCurious:ReadingandWritingtotheFilesystem

directories

application,ApplicationSandbox

Documents,ApplicationSandbox

Library/Caches,ApplicationSandbox

Library/Preferences,ApplicationSandbox

lproj,Baseinternationalization,FortheMoreCurious:NSBundle’sRoleinInternationalization

temporary,ApplicationSandbox

displayresolution,ViewsandFramesdocumentoutline(InterfaceBuilder),InterfaceBuilderdocumentation

developer,Archiving

opening,ControllingAnimations

forSwift,ExploringApple’sSwiftDocumentation

Documentsdirectory,ApplicationSandboxDouble,NumberandBooleantypesdrawing(seeviews)drill-downinterface,UINavigationControllerDynamicType,DynamicType

E

WOW! eBook www.wowebook.org

Page 664: iOS Programming The Big Nerd Ranch Guide, 5th

editButtonItem,Addingbuttonstothenavigationbarediting(UITableView,UITableViewController),EditingModeeditorarea(Xcode),InterfaceBuilderencodeInteger(_:forKey:),ArchivingencodeObject(_:forKey:),ArchivingencodeWithCoder(_:),NSKeyedArchiverandNSKeyedUnarchiverencodeWithCoder:,ArchivingendEditing(_:),Dismissingbytappingelsewhereentities

creating,UpdatingItems

defined,Entities

modeling,Modelingentities

relationshipsbetween,Relationships

savingchangesto,Savingchanges

enumerate(),LoopsandStringInterpolationenums(enumerations)

defined,EnumerationsandtheSwitchStatement

overviewof,Enumerationsandassociatedvalues

andrawvalues,Enumerationsandrawvalues

andswitchstatements,EnumerationsandtheSwitchStatement

errorhandling,ErrorHandling,DownloadingtheImageDataerrors

dealingwith,Optionals

inplaygrounds,UsingStandardTypes

traps,Literalsandsubscripting

eventhandling,Eventhandlingbasicsevents

control,ProgrammaticControls,FortheMoreCurious:UIControl

touch,Eventhandlingbasics,TouchEvents

(seealsotouchevents)

exceptions

vs.errorhandling,FortheMoreCurious:ReadingandWritingtotheFilesystem

internalinconsistency,AddingRows

Swiftvs.otherlanguages,FortheMoreCurious:ReadingandWritingtotheFilesystem

expressions,stringinterpolationand,LoopsandStringInterpolationWOW! eBook

www.wowebook.org

Page 665: iOS Programming The Big Nerd Ranch Guide, 5th

extensions,Extensions

F

fallthrough(switchstatement),EnumerationsandtheSwitchStatementfetchrequests,Fetchrequestsandpredicatesfileinspector,LocalizationfileURLs,retrieving,ConstructingafileURLfilesystem,writingto,WritingtotheFilesystemwithNSData,FortheMoreCurious:ReadingandWritingtotheFilesystemfirstresponder

becoming,Dismissingthekeyboard

andnil-targetedactions,FortheMoreCurious:UIControl

overview,DismissingtheKeyboard

resigning,Dismissingthekeyboard,DismissingbypressingtheReturnkey,Dismissingbytappingelsewhere

andresponderchain,FortheMoreCurious:TheResponderChain

andUIMenuController,UIMenuController

Flickr,WebServicesFloat,NumberandBooleantypesfloating-pointtypes,NumberandBooleantypes,Initializersfonts

changingpreferredsize,DynamicType

customizingsize,Customizingthelabels

for-in,LoopsandStringInterpolationforcedunwrapping(ofoptionals),Optionalsframe(UIView),ViewsandFramesframeworks

CoreData(seeCoreData)

definitionof,ViewsandFrames

linkingmanually,SettingtheInitialViewController

functions

(seealsoindividualfunctionnames)

callback,Delegation

G

genstrings,NSLocalizedStringandstringstables

WOW! eBook www.wowebook.org

Page 666: iOS Programming The Big Nerd Ranch Guide, 5th

gesturerecognizer(seeUIGestureRecognizer)gestures

(seealsoUIGestureRecognizer,UIScrollView)

discretevs.continuous,UILongPressGestureRecognizer

longpress,UILongPressGestureRecognizer

panning,UILongPressGestureRecognizer,UIPanGestureRecognizerandsimultaneousrecognizers

taps,DetectingTapswithUITapGestureRecognizer

GUIDs,CreatingandUsingKeys

H

Hashable,Collectiontypesheaderview(UITableView),EditingModehierarchies,view,TheViewHierarchyHomepwnerapplication

addinganimagestore,CreatingImageStore

addingdrill-downinterface,UINavigationController,UINavigationController

addingitemimages,Camera

adjustingviewpropertiespersizeclass,AnotherSizeClass

applicationsandbox,NSKeyedArchiverandNSKeyedUnarchiver

creatingnestedstackviews,StackViews

enablingediting,EditingUITableView

objectdiagrams,UITableView’sDataSource,UINavigationController

storingimages,WritingtotheFilesystemwithNSData

horizontalambiguity,AddingconstraintsinInterfaceBuilderHTTPprotocol,FortheMoreCurious:HTTP

I

IBAction,Definingactionmethods,Addingacamerabutton@IBInspectable,@IBInspectableIBOutlet,Declaringoutlets,HookingUptheContenticons

(seealsoimages)

application,ApplicationIcons

WOW! eBook www.wowebook.org

Page 667: iOS Programming The Big Nerd Ranch Guide, 5th

assetcatalogsfor,ApplicationIcons

camera,Addingacamerabutton

if-let,Optionalsimagepicker(seeUIImagePickerController)imageNamed:,FortheMoreCurious:RetinaDisplayimagePickerController:didFinishPickingMediaWithInfo:,Settingtheimagepicker’sdelegateimagePickerControllerDidCancel:,Settingtheimagepicker’sdelegateimages

(seealsocamera,icons,UIImageView)

accessingfromthecache,CreatingandUsingKeys

caching,WritingtotheFilesystemwithNSData

displayinginUIImageView,DisplayingImagesandUIImageView

downloadingimagedata,DownloadingandDisplayingtheImageData,DownloadingtheImageData

fetching,GivingViewControllersAccesstotheImageStore

modelingPhotoclass,ModelingthePhoto

navigatingtophotos,NavigatingtoaPhoto

forRetinadisplay,FortheMoreCurious:RetinaDisplay

saving,Savingtheimage

savingtodisk,SavingImagestoDisk

storing,CreatingImageStore

wrappinginNSData,WritingtotheFilesystemwithNSData

imageWithContentsOfFile(_:),WritingtotheFilesystemwithNSDataimplementationfiles,navigating,FortheMoreCurious:NavigatingImplementationFilesimplicitconstraints,Implicitconstraintsinactivestate,ApplicationStatesandTransitionsinequalityconstraints,Preparingforlocalizationinit(coder:),Archivinginit(frame:),ViewsandFramesinit?(contentsOfFile:encoding:error:),FortheMoreCurious:ReadingandWritingtotheFilesysteminitialviewcontroller,SettingtheInitialViewControllerinitializers

about,Initializers

convenience,Custominitializers

custom,Custominitializers

WOW! eBook www.wowebook.org

Page 668: iOS Programming The Big Nerd Ranch Guide, 5th

designated,Custominitializers

free,Custominitializers

member-wise,Structs

forstandardtypes,Initializers

initWithCoder:,Archivinginspectors(Xcode)

connections,Summaryofconnections

file,Localization

instancevariables,NSKeyedArchiverandNSKeyedUnarchiver

(seealsopointers,properties)

instances,InitializersInt,NumberandBooleantypesintegertypes,NumberandBooleantypesInterfaceBuilder

(seealsoXcode)

addingconstraintsin,AddingconstraintsinInterfaceBuilder

attributesinspector,ViewsandFrames

canvas,InterfaceBuilder

connectingobjects,Makingconnections

connectingwithsourcefiles,ExposingthePropertiesofItemCell

documentoutline,InterfaceBuilder

modifyingviewattributes,@IBInspectable

andproperties,ExposingthePropertiesofItemCell

scene,InterfaceBuilder

settingoutletsin,Settingoutlets,HookingUptheContent

settingtarget-actionin,Settingtargetsandactions

sizeinspector,ViewsandFrames

interfacefiles

badconnectionsin,HookingUptheContent

Baseinternationalizationand,Baseinternationalization

connectingwithsourcefiles,Addingacamerabutton

makingconnectionsin,Addingacamerabutton

WOW! eBook www.wowebook.org

Page 669: iOS Programming The Big Nerd Ranch Guide, 5th

internalinconsistencyexception,AddingRowsinternationalization,Localization,FortheMoreCurious:NSBundle’sRoleinInternationalization

(seealsolocalization)

intrinsiccontentsize,Intrinsiccontentsizeinverserelationships,RelationshipsiOSsimulator

runningapplicationson,Runningonthesimulator

sandboxlocation,NSKeyedArchiverandNSKeyedUnarchiver

savingimagesto,Presentingtheimagepickermodally

viewingapplicationbundlein,FortheMoreCurious:TheApplicationBundle

iPad

(seealsodevices)

applicationiconsfor,ApplicationIcons

runningiPhoneapplicationson,CreatinganXcodeProject

isEmpty(strings),PropertiesisSourceTypeAvailable:,Settingtheimagepicker’ssourceTypeissuenavigator,BuildingtheFinishedApplication

J

JSONdata,JSONData

K

key-valuepairs

indictionaries,Collectiontypes

inJSONdata,JSONData

inwebservices,FormattingURLsandrequests

keyboard

attributes,Keyboardattributes

dismissing,Dismissingthekeyboard,DismissingtheKeyboard

numberpad,BronzeChallenge:DisplayingaNumberPad

keys

creating/using,CreatingandUsingKeys

indictionaries,Collectiontypes

WOW! eBook www.wowebook.org

Page 670: iOS Programming The Big Nerd Ranch Guide, 5th

L

labels

adding,ViewsandFrames

addingadditional,AnotherLabel

addingconstraintsto,Preparingforlocalization

addingtotabbar,Tabbaritems

customizing,Customizingthelabels

updatingpreferredtextsize,Respondingtouserchanges

languagesettings,Localization

(seealsolocalization)

launchimages,LaunchScreenlayers(ofviews),TheViewHierarchylayoutattributes,Alignmentrectangleandlayoutattributeslayoutguides,Layoutguides,SilverChallenge:LayoutGuideslazyloading,TheViewofaViewController,LoadedandAppearingViewslet,UsingStandardTypeslibraries

(seealsoframeworks)

object,Creatingviewobjects

Library/Cachesdirectory,ApplicationSandboxLibrary/Preferencesdirectory,ApplicationSandboxliteralvalues,LiteralsandsubscriptingloadView,TheViewofaViewController,CreatingaViewProgrammaticallylocalvariables,LoopsandStringInterpolationlocalization

Baseinternationalizationand,Baseinternationalization

internationalization,Localization,FortheMoreCurious:NSBundle’sRoleinInternationalization

lprojdirectories,Baseinternationalization,FortheMoreCurious:NSBundle’sRoleinInternationalization

NSBundle,FortheMoreCurious:NSBundle’sRoleinInternationalization

stringstables,NSLocalizedStringandstringstables

usersettingsfor,Localization

XLIFFdatatype,FortheMoreCurious:ImportingandExportingasXLIFF

locationInView:,MultipleGestureRecognizers

WOW! eBook www.wowebook.org

Page 671: iOS Programming The Big Nerd Ranch Guide, 5th

loops

examininginValueHistory,LoopsandStringInterpolation

for,LoopsandStringInterpolation

for-in,LoopsandStringInterpolation

inSwift,LoopsandStringInterpolation

low-memorywarnings,Savingtheimagelprojdirectories,Baseinternationalization,FortheMoreCurious:NSBundle’sRoleinInternationalization

M

mainbundle,Baseinternationalization,FortheMoreCurious:NSBundle’sRoleinInternationalization

(seealsoapplicationbundle)

maininterface,SettingtheInitialViewControllermainthread,TheMainThreadmainBundle,FortheMoreCurious:TheApplicationBundlemargins,Margins,Explicitconstraints//MARK:,//MARK:member-wiseinitializers,Structsmemorymanagement

memorywarnings,Savingtheimage

UITableViewCell,ReusingUITableViewCells

menus(UIMenuController),UIMenuController,FortheMoreCurious:UIMenuControllerandUIResponderStandardEditActionsmessages

(seealsomethods)

action,FortheMoreCurious:UIControl,UIGestureRecognizerSubclasses,UIMenuController

log,Handlingmultipletouches

UIResponder,TouchEvents

methods

(seealsoindividualmethodnames)

action,Definingactionmethods,FortheMoreCurious:UIControl

datasource,Implementingdatasourcemethods

defined,TypesinSwift,Instancemethods

protocol,Moreonprotocols

static,TypesinSwift

WOW! eBook www.wowebook.org

Page 672: iOS Programming The Big Nerd Ranch Guide, 5th

minimumPressDuration,UILongPressGestureRecognizermodalviewcontroller,DisplayingUserAlerts,Presentingtheimagepickermodallymodellayer,Model-View-ControllerModel-View-Controller(MVC),Model-View-Controller,UITableViewController,DesignPatternsmulti-threading,TheMainThreadmultipleTouchEnabled(UIView),Handlingmultipletouchesmultitouch,enabling,HandlingmultipletouchesmutableSetValueForKey(_:),AddingTagstotheInterfaceMVC(Model-View-Controller),Model-View-Controller,UITableViewController,DesignPatterns

N

namingconventions

cellreuseidentifiers,ReusingUITableViewCells

delegateprotocols,Conformingtoaprotocol

navigationcontrollers(seeUINavigationController)navigationItem(UIViewController),UINavigationBarnavigators(Xcode)

defined,CreatinganXcodeProject

issue,BuildingtheFinishedApplication

project,CreatinganXcodeProject

nearestneighbor,ConstraintsnextResponder,FortheMoreCurious:TheResponderChainnil-targetedactions,FortheMoreCurious:UIControlNSBundle,FortheMoreCurious:NSBundle’sRoleinInternationalizationNSCoder,ArchivingNSCodingprotocol,ArchivingNSData,WritingtotheFilesystemwithNSDataNSDate,FortheMoreCurious:ReadingandWritingtotheFilesystemNSDateFormatter,FormattersNSFetchRequest,FetchrequestsandpredicatesNSIndexPath,CreatingandretrievingUITableViewCells,DeletingRowsNSJSONSerialization,NSJSONSerializationNSKeyedArchiver,NSKeyedArchiverandNSKeyedUnarchiverNSKeyedUnarchiver,LoadingfilesNSLocale,FormattersNSLocalizedString(),NSLocalizedStringandstringstablesNSManagedObject,NSManagedObjectandsubclasses,RelationshipsNSNumber,FortheMoreCurious:ReadingandWritingtotheFilesystemNSString

conversionto,Archiving

WOW! eBook www.wowebook.org

Page 673: iOS Programming The Big Nerd Ranch Guide, 5th

propertylistserializable,FortheMoreCurious:ReadingandWritingtotheFilesystem

NSTemporaryDirectory,ApplicationSandboxNSURL,NSURLComponentsNSURLRequest,SendingtheRequest,FortheMoreCurious:HTTPNSURLSession,SendingtheRequestNSURLSessionDataTask,NSURLSession,ParsingJSONdata,TheMainThreadNSURLSessionTask,FortheMoreCurious:HTTPNSUserDefaults,ApplicationSandboxNSUUID,CreatingandUsingKeysNSValueTransformer,Transformableattributesnumberformatters,Numberformatters,Formattersnumberpad,BronzeChallenge:DisplayingaNumberPad

O

objectgraphs,ObjectGraphsobjectlibrary,Creatingviewobjectsobjects

(seealsomemorymanagement)

propertylistserializable,FortheMoreCurious:ReadingandWritingtotheFilesystem

optional,Moreonprotocolsoptionalbinding,Optionalsoptionalmethods(protocols),Moreonprotocolsoptionals

about,Optionals

anddictionarysubscripting,Subscriptingdictionaries

forcedunwrappingof,Optionals

if-let,Optionals

andoptionalbinding,Optionals

syntaxfor,Optionals

unwrapping,Optionals

outlets

autogenerating/connecting,HookingUptheContent

connectingconstraintsto,AnimatingConstraints

connectingwithsourcefiles,ExposingthePropertiesofItemCell

defined,Makingconnections

setting,Makingconnections

WOW! eBook www.wowebook.org

Page 674: iOS Programming The Big Nerd Ranch Guide, 5th

settinginInterfaceBuilder,HookingUptheContent

P

padding,ContentInsetsparallelcomputing,TheMainThreadparent-childcontexts,Parent-ChildContextsPhotoramaapplication

addingpersistenceto,ObjectGraphs

addingtagstophotos,CoreDataRelationships

collectionview,CollectionViews

downloadingimagedata,DownloadingandDisplayingtheImageData

webservicerequests,StartingthePhotoramaApplication

photos(seecamera,images)pixels,ViewsandFramesplaygrounds(Xcode),UsingStandardTypes

errorsin,UsingStandardTypes

ValueHistory,LoopsandStringInterpolation

viewingconsolein,Optionals

pointers

inInterfaceBuilder(seeoutlets)

points(vs.pixels),ViewsandFramespredicates,Fetchrequestsandpredicatespreferences,ApplicationSandbox

(seealsoDynamicType,localization)

prepareForSegue:sender:,PassingDataAroundpresentViewController:animated:completion:,Presentingtheimagepickermodallypreviewassistant,Preparingforlocalizationprojectnavigator,CreatinganXcodeProjectprojects

cleaningandbuilding,Localization

creating,CreatinganXcodeProject

targetsettingsin,FortheMoreCurious:TheApplicationBundle

properties

creatinginInterfaceBuilder,ExposingthePropertiesofItemCell

defined,Properties

WOW! eBook www.wowebook.org

Page 675: iOS Programming The Big Nerd Ranch Guide, 5th

propertylistserializableobjects,FortheMoreCurious:ReadingandWritingtotheFilesystempropertyobserver,ImplementingtheTemperatureConversionprotocol,Conformingtoaprotocolprotocols

conformingto,Conformingtoaprotocol

declaring,Conformingtoaprotocol

delegate,Conformingtoaprotocol

NSCoding,Archiving

optionalmethodsin,Moreonprotocols

requiredmethodsin,Moreonprotocols

structureof,Conformingtoaprotocol

UIApplicationDelegate,ApplicationStatesandTransitions

UICollectionViewDataSourceprotocol,CollectionViewDataSource

UICollectionViewDelegate,DownloadingtheImageData

UIGestureRecognizerDelegate,UIPanGestureRecognizerandsimultaneousrecognizers

UIImagePickerControllerDelegate,Settingtheimagepicker’sdelegate,Savingtheimage

UINavigationControllerDelegate,Savingtheimage

UIResponderStandardEditActions,FortheMoreCurious:UIMenuControllerandUIResponderStandardEditActions

UITableViewDataSource,UITableViewController,Implementingdatasourcemethods,CreatingandretrievingUITableViewCells,DeletingRows,MovingRows

UITableViewDelegate,UITableViewController

UITextFieldDelegate,Conformingtoaprotocol,DismissingtheKeyboard

pseudolanguage,Preparingforlocalization

Q

Quartz,FortheMoreCurious:RetinaDisplay(seeCoreGraphics)queryitems,FormattingURLsandrequestsQuickHelp,ArchivingQuickHelp(Xcode),InferringtypesQuizapplication,CreatinganXcodeProject

R

Range,LoopsandStringInterpolationrawValue(enums),Enumerationsandrawvalues

WOW! eBook www.wowebook.org

Page 676: iOS Programming The Big Nerd Ranch Guide, 5th

referencepages,Archivingreferencetypes,Valuetypesvs.referencetypesregionsettings,Localizationreorderingcontrols,MovingRowsrequiredmethods(protocols),MoreonprotocolsrequireGestureRecognizerToFail(_:),MoreonUIGestureRecognizerresignFirstResponder,Dismissingthekeyboard,DismissingbypressingtheReturnkeyresources

assetcatalogsfor,ApplicationIcons

defined,ApplicationIcons,FortheMoreCurious:TheApplicationBundle

responderchain,FortheMoreCurious:TheResponderChainresponders(seefirstresponder,UIResponder)Retinadisplay,ApplicationIcons,FortheMoreCurious:RetinaDisplayreuseidentifiers,DisplayingtheGridreuseIdentifier(UITableViewCell),ReusingUITableViewCellsreverse(),Instancemethodsrootviewcontroller(UINavigationController),UINavigationControllerrows(UITableView)

adding,AddingRows

deleting,DeletingRows

moving,MovingRows

S

sandbox,application,ApplicationSandbox,FortheMoreCurious:TheApplicationBundleschemes,Runningonthesimulatorsections(UITableView),Implementingdatasourcemethods,EditingModesegues,SeguessendAction(_:to:from:forEvent:),FortheMoreCurious:UIControlsendActionsForControlEvents(_:),FortheMoreCurious:UIControlsetEditing:animated:,EditingMode,Addingbuttonstothenavigationbarsets,Collectiontypes,Initializerssettings(seepreferences)Settingsapplication,ApplicationSandboxsimulator

runningapplicationson,Runningonthesimulator

sandboxlocation,NSKeyedArchiverandNSKeyedUnarchiver

savingimagesto,Presentingtheimagepickermodally

viewingapplicationbundlein,FortheMoreCurious:TheApplicationBundle

sizeclasses,SizeClassesWOW! eBook

www.wowebook.org

Page 677: iOS Programming The Big Nerd Ranch Guide, 5th

sortdescriptors(NSFetchRequest),FetchrequestsandpredicatessourceType(UIImagePickerController),TakingPicturesandUIImagePickerControllerstackviews,StackViewsstates,application,ApplicationStatesandTransitionsstaticmethods,TypesinSwiftString

internationalizing,NSLocalizedStringandstringstables

writingtofilesystem,WritingtotheFilesystemwithNSData

stringinterpolation,LoopsandStringInterpolationstrings

(seealsoNSString)

initializersfor,Initializers

interpolation,LoopsandStringInterpolation

isEmpty,Properties

literal,Literalsandsubscripting

stringstables,NSLocalizedStringandstringstablesstructs,Structssubscripting

arrays,Literalsandsubscripting

dictionaries,Subscriptingdictionaries

subviews,TheViewHierarchy,Accessingsubviewssuperview,ViewsandFramessuspendedstate,ApplicationStatesandTransitionsSwift

about,TheSwiftLanguage

documentationfor,ExploringApple’sSwiftDocumentation

enumerationsandswitchstatement,EnumerationsandtheSwitchStatement

extensionsin,Extensions

loopsandstringinterpolation,LoopsandStringInterpolation

optionaltypesin,Optionals,ErrorHandling

typesin,TypesinSwift

usingstandardtypes,UsingStandardTypes

valuetypes,Valuetypesvs.referencetypes

switch,EnumerationsandtheSwitchStatementswitchstatements,EnumerationsandtheSwitchStatement

WOW! eBook www.wowebook.org

Page 678: iOS Programming The Big Nerd Ranch Guide, 5th

T

tabbarcontrollers(seeUITabBarController)tabbaritems,Tabbaritemstableviewcells(seeUITableViewCell)tableviewcontrollers(seeUITableViewController)tableviews(seeUITableView)tables(database),EntitiestableView,ContentInsetstableView(_:commitEditingStyle:forRowAtIndexPath:),DeletingRowstableView(_:moveRowAtIndexPath:toIndexPath:),MovingRowstableView:cellForRowAtIndexPath:,Implementingdatasourcemethods,CreatingandretrievingUITableViewCellstableView:numberOfRowsInSection:,Implementingdatasourcemethodstags

addingtophotos,AddingTagstotheInterface

addingtotheinterface,AddingTagstotheInterface

creatingrelationshipsbetween,Relationships

target-actionpairs

defined,Definingactionmethods,DesignPatterns

settingprogrammatically,Addingbuttonstothenavigationbar

andUIControl,FortheMoreCurious:UIControl

andUIGestureRecognizer,UIGestureRecognizerSubclasses

targets,buildsettingsfor,FortheMoreCurious:TheApplicationBundletemplates(Xcode),StyleChoicestext

(seealsoAutoLayout)

aligning,TextEditing

compressionof,Contentcompressionresistancepriorities

customizingappearance,Customizingthelabels,TextEditing

dynamicstylingof,DynamicType

input,TextInputandDelegation

textFieldShouldReturn:,DismissingtheKeyboardthreads,TheMainThreadtimingfunctions,TimingFunctionstmpdirectory,ApplicationSandboxto-manyrelationships,Relationshipsto-onerelationships,Relationships

WOW! eBook www.wowebook.org

Page 679: iOS Programming The Big Nerd Ranch Guide, 5th

toggleEditingMode:,EditingModetoolbars

adding,Addingacamerabutton

addingbuttonsto,Addingacamerabutton

addingconstraintsto,Addingacamerabutton

anchoring,AddingTagstotheInterface

topViewController(UINavigationController),UINavigationControllertouchevents

basicsof,TouchEvents

defined,Eventhandlingbasics

enablingmultitouch,Handlingmultipletouches

andresponderchain,FortheMoreCurious:TheResponderChain

andtarget-actionpairs,FortheMoreCurious:UIControl

andUIControl,FortheMoreCurious:UIControl

touchesBegan(_:withEvent:),TouchEventstouchesCancelled(_:withEvent:),TouchEventstouchesEnded(_:withEvent:),TouchEventstouchesMoved(_:withEvent:),TouchEventsTouchTrackerapplication

creating,CreatingtheTouchTrackerApplication

drawinglines,CreatingtheLineStruct

recognizinggestures,UIGestureRecognizerandUIMenuController

transformableattributes(CoreData),TransformableattributestranslationInView(_:),UIPanGestureRecognizerandsimultaneousrecognizerstraps,Literalsandsubscriptingtuples,LoopsandStringInterpolationtypeinference,Inferringtypestypes

boolean,NumberandBooleantypes

floating-point,NumberandBooleantypes,Initializers

hashable,Collectiontypes

inferenceof,Inferringtypes

instancesof,Initializers

integer,NumberandBooleantypes

sets,Collectiontypes,InitializersWOW! eBook

www.wowebook.org

Page 680: iOS Programming The Big Nerd Ranch Guide, 5th

specifying,Specifyingtypes

tuples,LoopsandStringInterpolation

U

UIthread,TheMainThreadUIAlertController,DisplayingUserAlertsUIApplication

andevents,TouchEvents

andresponderchain,FortheMoreCurious:TheResponderChain,FortheMoreCurious:UIControl

UIApplicationDelegate,ApplicationStatesandTransitionsUIBarButtonItem,UINavigationBar,AddingacamerabuttonUICollectionViewCell,CreatingaCustomUICollectionViewCellUICollectionViewDataSourceprotocol,CollectionViewDataSourceUICollectionViewDelegateprotocol,DownloadingtheImageDataUICollectionViewFlowLayout,CollectionViewsUIColor,ViewsandFramesUIControl,FortheMoreCurious:UIControlUIControlEvent.TouchUpInside,FortheMoreCurious:UIControlUIControlEvents,ProgrammaticControlsUIGestureRecognizer

actionmessagesof,UIGestureRecognizerSubclasses,UILongPressGestureRecognizer

cancelsTouchesInView,UIPanGestureRecognizerandsimultaneousrecognizers

chainingrecognizers,MoreonUIGestureRecognizer

delayingtouches,MoreonUIGestureRecognizer

described,UIGestureRecognizerandUIMenuController

detectingtaps,DetectingTapswithUITapGestureRecognizer

enablingsimultaneousrecognizers,UIPanGestureRecognizerandsimultaneousrecognizers

implementingmultiple,MultipleGestureRecognizers,UIPanGestureRecognizerandsimultaneousrecognizers

interceptingtouchesfromview,UIGestureRecognizerSubclasses,UIPanGestureRecognizerandsimultaneousrecognizers

locationInView:,MultipleGestureRecognizers

longpress,UILongPressGestureRecognizer

panning,UILongPressGestureRecognizer,UIPanGestureRecognizerandsimultaneousrecognizers

WOW! eBook www.wowebook.org

Page 681: iOS Programming The Big Nerd Ranch Guide, 5th

state(property),UILongPressGestureRecognizer,UIPanGestureRecognizerandsimultaneousrecognizers,MoreonUIGestureRecognizer

subclasses,Dismissingthekeyboard,UIGestureRecognizerSubclasses,MoreonUIGestureRecognizer

subclassing,MoreonUIGestureRecognizer

translationInView(_:),UIPanGestureRecognizerandsimultaneousrecognizers

andUIRespondermethods,UIPanGestureRecognizerandsimultaneousrecognizers

UIGestureRecognizerDelegate,UIPanGestureRecognizerandsimultaneousrecognizersUIImage,WritingtotheFilesystemwithNSData

(seealsoimages,UIImageView)

UIImageJPEGRepresentation,WritingtotheFilesystemwithNSDataUIImagePickerController

instantiating,TakingPicturesandUIImagePickerController

presenting,Presentingtheimagepickermodally

UIImagePickerControllerDelegate,Settingtheimagepicker’sdelegate,SavingtheimageUIImageView,DisplayingImagesandUIImageViewUIInterpolatingMotionEffect,BasicAnimationsUIKit,ViewsandFramesUILongPressGestureRecognizer,UILongPressGestureRecognizerUIMenuController,UIMenuController,FortheMoreCurious:UIMenuControllerandUIResponderStandardEditActionsUINavigationBar,UINavigationControllerUINavigationController

(seealsoviewcontrollers)

addingviewcontrollersto,AppearingandDisappearingViews

described,UINavigationController

instantiating,UINavigationController

managingviewcontrollerstack,UINavigationController

rootviewcontroller,UINavigationController

instoryboards,Segues

topViewController,UINavigationController

andUINavigationBar,UINavigationBar

view,UINavigationController

viewControllers,UINavigationController

viewWillAppear:,AppearingandDisappearingViews

WOW! eBook www.wowebook.org

Page 682: iOS Programming The Big Nerd Ranch Guide, 5th

viewWillDisappear:,AppearingandDisappearingViews

UINavigationControllerDelegate,SavingtheimageUINavigationItem,UINavigationBarUIPanGestureRecognizer,UILongPressGestureRecognizer,UIPanGestureRecognizerandsimultaneousrecognizersUIResponder

menuactions,FortheMoreCurious:UIMenuControllerandUIResponderStandardEditActions

andresponderchain,FortheMoreCurious:TheResponderChain

andtouchevents,TouchEvents

UIResponderStandardEditActions(protocol),FortheMoreCurious:UIMenuControllerandUIResponderStandardEditActionsUIScrollView,ContentInsetsUIStackView,UsingUIStackViewUIStoryboardSegue,SeguesUITabBarController

implementing,UITabBarController

vs.UINavigationController,UINavigationController

view,UITabBarController

UITabBarItem,TabbaritemsUITableView,UITableViewandUITableViewController

(seealsoUITableViewCell,UITableViewController)

addingrowsto,AddingRows

deletingrowsfrom,DeletingRows

editingmodeof,EditingMode,SubclassingUITableViewCell,Addingbuttonstothenavigationbar

editingproperty,EditingMode

footerview,EditingMode

headerview,EditingMode

movingrowsin,MovingRows

populating,UITableView’sDataSource

sections,Implementingdatasourcemethods,EditingMode

view,SubclassingUITableViewController

UITableViewCell

cellstyles,UITableViewCells

contentView,UITableViewCells

WOW! eBook www.wowebook.org

Page 683: iOS Programming The Big Nerd Ranch Guide, 5th

editingstyles,DeletingRows

retrievinginstancesof,CreatingandretrievingUITableViewCells

reusinginstancesof,ReusingUITableViewCells

subclassing,SubclassingUITableViewCell

UITableViewCellStyle,UITableViewCells

UITableViewCellEditingStyleDelete,DeletingRowsUITableViewController

(seealsoUITableView)

addingrows,AddingRows

datasourcemethods,Implementingdatasourcemethods

dataSource,UITableView’sDataSource

deletingrows,DeletingRows

described,UITableViewController

editingproperty,EditingMode

movingrows,MovingRows

returningcells,CreatingandretrievingUITableViewCells

subclassing,SubclassingUITableViewController

tableView,ContentInsets

UITableViewDataSource(protocol),UITableViewController,Implementingdatasourcemethods,CreatingandretrievingUITableViewCells,DeletingRows,MovingRowsUITableViewDelegate,UITableViewControllerUITapGestureRecognizer,Dismissingthekeyboard,DetectingTapswithUITapGestureRecognizerUITextField

asfirstresponder,DismissingtheKeyboard,FortheMoreCurious:UIControl

andkeyboard,DismissingtheKeyboard

settingattributesof,BronzeChallenge:DisplayingaNumberPad

textediting,TextEditing

UITextFieldDelegate,Conformingtoaprotocol,DismissingtheKeyboardUIToolbar,UINavigationBar,AddingacamerabuttonUITouch,TouchEvents,TurningTouchesintoLines,HandlingmultipletouchesUIView

(seealsoUIViewController,views)

animationdocumentation,ControllingAnimations

defined,ViewsandtheViewHierarchy

WOW! eBook www.wowebook.org

Page 684: iOS Programming The Big Nerd Ranch Guide, 5th

frame,ViewsandFrames

instantiating,ViewsandFrames

superview,ViewsandFrames

UIViewController

(seealsoUIView,viewcontrollers)

loadView,TheViewofaViewController,CreatingaViewProgrammatically

navigationItem,UINavigationBar

tabBarItem,Tabbaritems

view,TheViewofaViewController,FortheMoreCurious:TheResponderChain

viewDidLoad,Accessingsubviews

viewWillAppear:,Accessingsubviews,Savingtheimage

UIWindow

purposeof,TheViewHierarchy

andresponderchain,FortheMoreCurious:TheResponderChain

unarchiveObjectWithFile(_:),LoadingfilesURLForResource(_:withExtension:),FortheMoreCurious:NSBundle’sRoleinInternationalizationURLs,FormattingURLsandrequests

(seealsoNSURL)

useralerts,displaying,DisplayingUserAlertsuserinterface

(seealsoAutoLayout,views)

drill-down,UINavigationController

keyboard,DismissingtheKeyboard

usersettings(seepreferences)UUIDs,CreatingandUsingKeys,NSManagedObjectandsubclasses

V

valuetypes,Valuetypesvs.referencetypesvar,UsingStandardTypesvariables,UsingStandardTypes

(seealsoinstancevariables,localvariables,pointers,properties)

view(UIViewController),TheViewofaViewControllerviewcontrollers

WOW! eBook www.wowebook.org

Page 685: iOS Programming The Big Nerd Ranch Guide, 5th

(seealsoUIViewController,views)

allowingaccesstoimagestore,GivingViewControllersAccesstotheImageStore

interactingwith,InteractingwithViewControllersandTheirViews

lazyloadingofviews,TheViewofaViewController,LoadedandAppearingViews

modal,DisplayingUserAlerts,Presentingtheimagepickermodally

navigatingbetween,Segues

presenting,UITabBarController

reloadingsubviews,Savingtheimage

root,UINavigationController

settinginitial,SettingtheInitialViewController

andviewhierarchy,TheViewofaViewController,CreatingaViewProgrammatically

viewhierarchy,TheViewHierarchy,CreatingaViewProgrammaticallyviewControllers(UINavigationController),UINavigationControllerviewDidLoad,ViewsandFrames,Accessingsubviewsviews

(seealsoAutoLayout,touchevents,UIView,viewcontrollers)

addingtowindow,TheViewHierarchy,CreatingaViewProgrammatically

animating,ControllingAnimations

appearing/disappearing,AppearingandDisappearingViews

contentcompressionresistancepriorities,Contentcompressionresistancepriorities

contenthuggingpriorities,Contenthuggingpriorities

creatingprogrammatically,CreatingaViewProgrammatically

defined,ViewsandtheViewHierarchy

drawingtoscreen,TheViewHierarchy

inhierarchy,TheViewHierarchy

layersand,TheViewHierarchy

lazyloadingof,TheViewofaViewController,LoadedandAppearingViews

misplaced,Misplacedviews

modalpresentationof,Presentingtheimagepickermodally

inModel-View-Controller,Model-View-Controller

removingfromstoryboard,ProgrammaticViews

rendering,TheViewHierarchy

WOW! eBook www.wowebook.org

Page 686: iOS Programming The Big Nerd Ranch Guide, 5th

resizing,DisplayingImagesandUIImageView

scroll,DisplayingtheGrid

sizeandpositionof,ViewsandFrames

stackview,StackViews,AnotherSizeClass

andsubviews,TheViewHierarchy

viewWillAppear:,Accessingsubviews,AppearingandDisappearingViews,SavingtheimageviewWillDisappear:,AppearingandDisappearingViews

W

webservices

andHTTPprotocol,FortheMoreCurious:HTTP

withJSONdata,JSONData

andNSURLSession,SendingtheRequest

overview,WebServices

requestingdatafrom,BuildingtheURL

wildcardAnyWidth/Heightlayout,SizeClassesworkspaces(Xcode),CreatinganXcodeProjectWorldTrotterapplication

addingtabbarcontroller,UITabBarController

configuring,CreatingaNewProject

implementingtemperatureconversion,ImplementingtheTemperatureConversion

interfacelayout,ViewsandFrames

localizing,Internationalization

multipleviewcontrollersfor,ViewControllers

programmaticviewsin,ProgrammaticViews

textinput,TextInputandDelegation

writeToURL(_:atomically:),WritingtotheFilesystemwithNSDatawriteToURL(_:atomically:encoding:error:),FortheMoreCurious:ReadingandWritingtotheFilesystem

X

.xcassets(assetcatalog),ApplicationIcons

.xcdatamodeld(datamodelfile),ModelingentitiesXcode

WOW! eBook www.wowebook.org

Page 687: iOS Programming The Big Nerd Ranch Guide, 5th

(seealsodebuggingtools,InterfaceBuilder,projects,iOSsimulator)

APIReference,Archiving

assetcatalogs,ApplicationIcons

assistanteditor,Addingacamerabutton

creatingprojectsin,CreatinganXcodeProject

documentation,ControllingAnimations

editorarea,InterfaceBuilder

fileinspector,Localization

issuenavigator,BuildingtheFinishedApplication

navigatorarea,CreatinganXcodeProject

navigators,CreatinganXcodeProject

objectlibrary,Creatingviewobjects

organizingmethodswith//MARK:,//MARK:

playgrounds,UsingStandardTypes

QuickHelp,Inferringtypes,Archiving

schemes,Runningonthesimulator

sourceeditorjumpbar,FortheMoreCurious:NavigatingImplementationFiles

versions,CreatinganXcodeProject

workspaces,CreatinganXcodeProject

XLIFFdatatype,FortheMoreCurious:ImportingandExportingasXLIFFXMLpropertylists,FortheMoreCurious:ReadingandWritingtotheFilesystem

WOW! eBook www.wowebook.org