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.
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.
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.
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.
Programmet lagrer for øyeblikket ikke tilstand mellom lanseringer. For å løse dette skal vi lagre elementer
og checkedItems
arrays i programmets brukerstandarddatabase.
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.
ViewController
Klassen er kun tilgjengelig av ViewController
klasse.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")
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.
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.
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.
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 segdidSet
: påkrevd etter at verdien har endret segFor 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.
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!