Swift From Scratch Tilgangskontroll og eiendom observatører

I den forrige leksjonen har vi lagt til evnen til å lage oppgaver. Mens dette tillegget har gjort applikasjonen litt mer nyttig, vil det også være praktisk å legge til muligheten til å merke elementer som ferdig og slette elementer. Det er det vi vil fokusere på i denne leksjonen.

Forutsetninger

Hvis du vil følge med meg, så sørg for at du har Xcode 8.3.2 eller høyere installert på maskinen din. Du kan laste ned Xcode 8.3.2 fra Apples App Store.

1. Slette elementer

For å slette elementer må vi implementere to ytterligere metoder for UITableViewDataSource protokoll. Vi må først fortelle tabellvisningen hvilke rader som kan redigeres ved å implementere Tableview (_: canEditRowAt :) metode. Som du kan se i koden nedenfor, er implementeringen enkel. Vi forteller tabellvisningen at hver rad er redigerbar ved å returnere ekte.

func tableView (_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool return true

Den andre metoden vi er interessert i er Tableview (_: begå: forRowAt :). Implementeringen er litt mer kompleks, men lett nok til å forstå.

func tableView (_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) hvis editingStyle == .delete // Oppdater elementer items.remove (på: indexPath.row) // Oppdater tabelloversikt tableView.deleteRows : [indexPath], med: .right)

Vi starter med å sjekke verdien av editingStyle, en oppsummering av type UITableViewCellEditingStyle. Vi sletter bare et element hvis verdien av editingStyle er lik UITableViewCellEditingStyle.delete.

Swift er smartere enn det, skjønt. Fordi det vet det editingStyle er av typen UITableViewCellEditingStyle, vi kan utelate UITableViewCellEditingStyle, navnet på opptellingen, og skriv .slette, medlemsverdien av oppregningen som vi er interessert i. Hvis du er ny i tall i Swift, så anbefaler jeg at du leser dette raske tipset om tall i Swift.

Deretter oppdaterer vi tabellvisningens datakilde, elementer, ved å påkalle fjerne (ved :) på elementer eiendom, passerer i riktig indeks. Vi oppdaterer også tabellvisningen ved å påkalle deleteRows (ved: med :) på Tableview, passerer i en matrise med indexPath og .Ikke sant for å angi animasjonstypen. Som vi så tidligere, kan vi utelate navnet på opptellingen, UITableViewRowAnimation, siden Swift vet hvilken type det andre argumentet er UITableViewRowAnimation.

Brukeren skal nå kunne slette elementer fra listen. Bygg og kjør programmet for å teste dette.

2. Sjekke av elementer

For å markere et element som ferdig, skal vi legge til et merke i den tilsvarende raden. Dette innebærer at vi må holde styr på elementene brukeren har merket som ferdig. Til det formål vil vi erklære en ny eiendom som forvalter dette for oss. Erklære en variabel egenskap, checkedItems, av type [String], og initialiser den med en tom rekkefølge.

var checkedItems: [String] = []

I Tableview (_: cellForRowAt :), vi sjekker om checkedItems inneholder det respektive elementet ved å påkalle inneholder (_ :) metode, passerer inn elementet som tilsvarer gjeldende rad. Metoden returnerer ekte hvis checkedItems inneholder punkt.

func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell // Hente element let element = elementer [indexPath.row] // Dequeue Cell lar celle = tableView.dequeueReusableCell (withIdentifier: "TableViewCell", for: indexPath ) // Konfigurer Cell cell.textLabel? .Text = element hvis checkedItems.contains (item) cell.accessoryType = .checkmark else cell.accessoryType = .none returcelle

Hvis punkt er funnet i checkedItems, vi setter cellens accessoryType eiendom til .hake, en medlemsverdi av UITableViewCellAccessoryType telling. Hvis punkt er ikke funnet, vi faller tilbake til .ingen som celle tilbehørstype.

Det neste trinnet er å legge til evnen til å markere et element som gjort ved å implementere en metode for UITableViewDelegate protokollen, Tableview (_: didSelectRowAt :). I denne delegatemetoden kaller vi først deselectRow (i: :) animert på Tableview å avmarkere raden brukeren tappet på.

// MARK: - Tabellvisning Delegate Methods func tableView (_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) tableView.deselectRow (ved: indexPath, animated: true) // Hente Item let item = elementer [indexPath.row] // Hent celle la celle = tableView.cellForRow (ved: indexPath) // Finn vareindeks let index = checkedItems.index (av: element) hvis la index = index checkedItems.remove (ved: index) celle? .AccessoryType =. ingen else checkedItems.append (item) celle? .accessoryType = .checkmark

Vi henter deretter den tilsvarende varen fra elementer og en referanse til cellen som tilsvarer den trukne rad. Vi spør checkedItems for indeksen for det tilsvarende elementet ved å påkalle oversikt over:). Denne metoden returnerer en valgfri int. Hvis checkedItems inneholder punkt, vi fjerner den fra checkedItems og sett celleens tilbehørstype til .ingen. Hvis checkedItems inneholder ikke punkt, vi legger til den i checkedItems og sett celleens tilbehørstype til .hake.

Med disse tilleggene kan brukeren nå markere elementer som ferdig. Bygg og kjør programmet for å sikre at alt fungerer som forventet.

3. Lagringsstat

Programmet lagrer for øyeblikket ikke tilstand mellom lanseringer. For å løse dette skal vi lagre elementer og checkedItems arrays i programmets brukerstandarddatabase.

Trinn 1: Laster stat

Begynn med å lage to hjelpemetoder, loadItems () og loadCheckedItems (). Legg merke til privat søkeord prefiks hver hjelpemetode. De privat søkeord forteller Swift at disse metodene bare er tilgjengelige fra innsiden av ViewController klasse.

// MARK: Private Helper Methods private func loadItems () la userDefaults = UserDefaults.standard hvis la ting = userDefaults.object (forKey: "items") som? [String] self.items = items privat func loadCheckedItems () la userDefaults = UserDefaults.standard hvis la checkedItems = userDefaults.object (forKey: "checkedItems") som? [String] self.checkedItems = checkedItems

De privat Søkeord er en del av Swift's adgangskontroll. Som navnet antyder, definerer tilgangskontroll hvilken kode som har tilgang til hvilken kode. Tilgangsnivåer gjelder for metoder, funksjoner, typer, etc. Apple refererer bare til enheter. Det er fem tilgangsnivåer: åpen, offentlig, intern, fil-privat og privat.

  • Åpne / Public: Enheter som er merket som åpne eller offentlige, er tilgjengelige av enheter definert i samme modul, så vel som andre moduler. Dette er ideelt for å utsette grensesnittet til et rammeverk. Det er flere forskjeller mellom åpne og offentlige tilgangsnivåer. Du kan lese mer om disse forskjellene i The Swift Programming Language.
  • Innvendig: Dette er standard tilgangsnivå. Med andre ord, hvis ikke tilgangsnivå er spesifisert, gjelder dette tilgangsnivået. En enhet med et tilgangsnivå for intern er kun tilgjengelig av enheter definert i samme modul.
  • Fil-Privat: En enhet som er erklært som fil-privat, er bare tilgjengelig av enheter definert i samme kildefil. For eksempel, de private hjelpemetoder som er definert i ViewController Klassen er kun tilgjengelig av ViewController klasse.
  • Privat: Privat er veldig lik fil-privat. Den eneste forskjellen er at en enhet som er erklært som privat, kun er tilgjengelig fra den erklæringen den er vedlagt. For eksempel, hvis vi lager en utvidelse for ViewController klasse i ViewController.swift, Enhver enhet som er merket som fil-privat, vil ikke være tilgjengelig i utvidelsen, men private enheter vil være tilgjengelige.

Implementeringen av hjelpemetoder er enkel hvis du er kjent med UserDefaults klasse. For enkel bruk, lagrer vi en referanse til standardbrukerstandardobjektet i en konstant navngitt userDefaults. I tilfelle av loadItems (), Vi spør userDefaults for objektet som er knyttet til nøkkelen "elementer" og nedkast det til et valgfritt utvalg av strenger. Vi pakker ut valgfritt på en trygg måte, noe som betyr at vi lagrer verdien i konstanten elementer hvis valgfri ikke er nil, og tilordne verdien til elementer egenskapen til visningskontrolleren.

Hvis hvis setningen ser forvirrende ut, så ta en titt på en enklere versjon av loadItems () metode i følgende eksempel. Resultatet er identisk; Den eneste forskjellen er konsistens.

private func loadItems () la userDefaults = UserDefaults.standard la storedItems = userDefaults.object (forKey: "items") som? [String] hvis la ting = lagretItems self.items = items

Gjennomføringen av loadCheckedItems () er identisk med unntak av nøkkelen som brukes til å laste objektet som er lagret i brukerens standarddatabase. La oss sette loadItems () og loadCheckedItems () å bruke ved å oppdatere viewDidLoad () metode.

overstyr func viewDidLoad () super.viewDidLoad () // Sett tittel title = "Å gjøre" // Populere elementer elementer = ["Kjøp melk", "Avslutt opplæring", "Spill Minecraft"] // Last State loadItems () loadCheckedItems () // Registrer Class for Cell Reuse tableView.register (UITableViewCell.self, forCellReuseIdentifier: "TableViewCell")

Trinn 2: Lagringsstat

For å redde staten implementerer vi to nye private hjelpemetoder, saveItems () og saveCheckedItems (). Logikken ligner den av loadItems () og loadCheckedItems (). Forskjellen er at vi lagrer data i brukerstandarddatabasen. Kontroller at tastene som brukes i setObject (_: Forkey :) samtaler samsvarer med de som brukes i loadItems () og loadCheckedItems ().

privat func saveItems () la userDefaults = UserDefaults.standard // Oppdater brukerstandarder userDefaults.set (elementer, forKey: "items") userDefaults.synchronize () privat func saveCheckedItems () la userDefaults = UserDefaults.standard // Oppdater Brukerstandarder userDefaults.set (checkedItems, forKey: "checkedItems") userDefaults.synchronize ()

De synkronisere () samtale er ikke strengt nødvendig. Operativsystemet vil sørge for at dataene du lagrer i brukerstandarddatabasen er skrevet til disk på et tidspunkt. Ved å påkalle synkronisere (), Men du sier eksplisitt at operativsystemet skal skrive eventuelle ventende endringer på disken. Dette er nyttig under utvikling, fordi operativsystemet ikke vil skrive endringene dine til disk hvis du dreper programmet. Det kan da virke som om noe ikke fungerer som det skal.

Vi må påberope saveItems () og saveCheckedItems () på en rekke steder. For å starte, ring saveItems () når et nytt element legges til listen. Vi gjør dette i delegatemetoden til AddItemViewControllerDelegate protokollen.

// MARK: Legg til element View Controller Delegate Methods func controller (_ kontroller: AddItemViewController, didAddItem: String) // Oppdater datakilde items.append (didAddItem) // Lagre stat saveItems () // Oppdater tabelloversikt tableView.reloadData ( ) // Avvis Legg til produkt Vis Controller avvis (animert: true)

Når tilstanden til et element endres i Tableview (_: didSelectRowAt :), vi oppdaterer checkedItems. Det er en god ide å også påberope seg saveCheckedItems () på punktet.

// MARK: - Tabellvisning Delegate Methods func tableView (_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) tableView.deselectRow (ved: indexPath, animated: true) // Hente Item let item = elementer [indexPath.row] // Hent celle la celle = tableView.cellForRow (ved: indexPath) // Finn vareindeks let index = checkedItems.index (av: element) hvis la index = index checkedItems.remove (ved: index) celle? .AccessoryType =. ingen else checkedItems.append (item) celle? .accessoryType = .checkmark // Lagre stat saveCheckedItems ()

Når et element slettes, begge deler elementer og checkedItems er oppdatert. For å lagre denne endringen, kaller vi begge saveItems () og saveCheckedItems ().

func tableView (_ tableView: UITableView, commit redigeringStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) hvis editingStyle == .delete // Hente element let element = elementer [indexPath.row] // Oppdater elementer items.remove (ved: indexPath .row) hvis la indeks = checkedItems.index (av: item) checkedItems.remove (på: index) // Oppdater tabell Se tableView.deleteRows (på: [indexPath], med: .right) // Lagre State saveItems () saveCheckedItems ()

Det er det. Bygg og kjør programmet for å teste arbeidet ditt. Spill med programmet og tvinge å avslutte det. Når du starter programmet på nytt, må den sist kjente tilstanden lastes og vises.

4. Eiendomsobservatører

Applikasjonens brukeropplevelse er litt mangelfull i øyeblikket. Når hvert element slettes eller når programmet startes for første gang, ser brukeren en tom tabellvisning. Dette er ikke bra. Vi kan løse dette ved å vise en melding når det ikke er noen elementer. Dette vil også gi meg muligheten til å vise deg en annen funksjon av Swift, eiendom observatører.

Trinn 1: Legge til en etikett

La oss begynne med å legge til en etikett i brukergrensesnittet for å vise meldingen. Erklære et utsalgsnavn som heter messageLabel av type UILabel i ViewController klasse, åpen Main.storyboard, og legg til en etikett til visningskontrollørens visning.

@IBOutlet var messageLabel: UILabel!

Legg til nødvendige layoutbegrensninger på etiketten og koble den sammen med visningskontrolleren messageLabel uttak i Tilkoblingsinspektør. Sett etikettens tekst til Du har ingen dosis. og senter etikettens tekst i Attributtsinspektør.

Trinn 2: Implementere en eiendomsobservatør

Meldingsetiketten skal bare være synlig hvis elementer inneholder ingen elementer. Når det skjer, bør vi også skjule tabellvisningen. Vi kunne løse dette problemet ved å legge til forskjellige kontroller i ViewController klasse, men en mer praktisk og elegant tilnærming er å bruke en eiendomsobservator.

Som navnet tilsier, observerer observatører en eiendom. En eiendomsobservator er påkalt når en eiendom endres, selv når den nye verdien er den samme som den gamle verdien. Det er to typer eiendomsobservatører.

  • willSet: Påkrevd før verdien har endret seg
  • didSet: påkrevd etter at verdien har endret seg

For vår hensikt vil vi implementere didSet observatør for elementer eiendom. Ta en titt på syntaksen i følgende kodestykke.

var elementer: [String] = [] didSet let hasItems = items.count> 0 tableView.isHidden =! hasItems messageLabel.isHidden = hasItems

Konstruksjonen kan se litt merkelig først, så la meg forklare hva som skjer. Når didSet eiendom observatør er påkalt, etter elementer Eiendommen har endret seg, vi sjekker om elementer Eiendommen inneholder noen elementer. Basert på verdien av hasItems konstant, vi oppdaterer brukergrensesnittet. Det er så enkelt.

De didSet observatør er bestått en konstant parameter som inneholder verdien av den gamle verdien av eiendommen. Det er utelatt i eksemplet ovenfor, fordi vi ikke trenger det i implementeringen vår. Følgende eksempel viser hvordan det kan brukes.

var elementer: [String] = [] didSet (oldValue) hvis oldValue! = elementer la hasItems = items.count> 0 tableView.isHidden =! hasItems messageLabel.isHidden = hasItems

De OLDVALUE parameteren i eksemplet har ingen eksplisitt type, fordi Swift vet typen av elementer eiendom. I eksemplet oppdaterer vi bare brukergrensesnittet hvis den gamle verdien er forskjellig fra den nye verdien.

EN willSet observatør fungerer på lignende måte. Hovedforskjellen er at parameteren passerte til willSet observatør er en konstant holder den nye verdien av eiendommen. Når du bruker eiendomsobservatører, må du huske på at de ikke påberopes når forekomsten er initialisert.

Bygg og kjør programmet for å sikre at alt er tilkoblet riktig. Selv om søknaden ikke er perfekt og kan bruke noen flere funksjoner, har du opprettet ditt første iOS-program ved hjelp av Swift.

Konklusjon

I løpet av de tre siste leksjonene i denne serien, opprettet du et funksjonelt iOS-program ved hjelp av Swifts objektorienterte funksjoner. Hvis du har erfaring med programmering og utvikling av applikasjoner, må du ha lagt merke til at dagens datamodell har noen få svakheter, for å si det lett.

Lagring av elementer som strenge og opprette et eget array for å lagre en vares tilstand er ikke en god ide hvis du bygger et riktig program. En bedre tilnærming ville være å skape et eget Å gjøre klasse for modellering av elementer og lagre dem i programmets sandkasse. Det vil være vårt mål for neste leksjon i denne serien.

I mellomtiden kan du sjekke ut noen av våre andre kurs og opplæringsprogrammer om swift språk iOS-utvikling!