Swift From Scratch controlul accesului și observatorii proprietății

În lecția anterioară, am adăugat capacitatea de a crea elemente de rezolvat. În timp ce această adăugire a făcut aplicația un pic mai utilă, ar fi de asemenea convenabil să adăugați abilitatea de a marca articole ca fiind terminate și să ștergeți elementele. Asta ne vom concentra în această lecție.

Cerințe preliminare

Dacă doriți să urmați alături de mine, asigurați-vă că aveți instalat Xcode 8.3.2 sau mai nou pe aparat. Puteți descărca Xcode 8.3.2 de la Apple's App Store.

1. Ștergerea elementelor

Pentru a șterge elemente, trebuie să implementăm două metode suplimentare ale UITableViewDataSource protocol. Mai întâi trebuie să spunem tabelului care rânduri pot fi editate prin implementarea tableView (_: canEditRowAt :) metodă. După cum puteți vedea în fragmentul de cod de mai jos, implementarea este simplă. Spunem vederii tabelului că fiecare rând poate fi editat prin întoarcere Adevărat.

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

A doua metodă de care suntem interesați este tableView (_: comite: forRowAt :). Implementarea este un pic mai complexă, dar destul de ușor de înțeles.

func tableView (_ tableView: UITableView, comite editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) dacă editingStyle == .delete // Actualizați elementele items.remove (la: indexPath.row) // Update Table View tableView.deleteRows : [indexPath], cu: .right)

Începem verificând valoarea editingStyle, o enumerare de tip UITableViewCellEditingStyle. Ștergem doar un element dacă valoarea lui editingStyle este egal cu UITableViewCellEditingStyle.delete.

Swift este mai deștept decât asta. Pentru că știe asta editingStyle este de tip UITableViewCellEditingStyle, putem omite UITableViewCellEditingStyle, numele enumerării și scrieți .șterge, valoarea de membru a enumerării că suntem interesați. Dacă sunteți nou la enumerări în Swift, atunci vă recomandăm să citiți acest sfat rapid despre enumerări în Swift.

Apoi, actualizăm sursa de date a tabelului, articole, invocând eliminați (at :) pe articole proprietate, trecând în indexul corect. De asemenea, actualizăm vizualizarea tabelului invocând deleteRows (la: cu :) pe tableView, trecând într-o matrice cu indexPath și .dreapta pentru a specifica tipul de animație. După cum am văzut mai devreme, putem omite numele enumerării, UITableViewRowAnimation, deoarece Swift știe tipul celui de-al doilea argument UITableViewRowAnimation.

Utilizatorul ar trebui să poată șterge acum articole din listă. Construiți și rulați aplicația pentru a testa acest lucru.

2. Verificarea elementelor dezactivate

Pentru a marca un element ca făcut, vom adăuga un semn de selectare la rândul corespunzător. Aceasta implică faptul că trebuie să urmărim elementele pe care utilizatorul le-a marcat ca terminate. În acest scop, vom declara o nouă proprietate care gestionează acest lucru pentru noi. Declarați o proprietate variabilă, checkedItems, de tip [Şir], și inițializați-o cu o matrice goală.

var checkedItems: [String] = []

În tableView (_: cellForRowAt :), verificăm dacă checkedItems conține elementul respectiv invocând conține (_ :) , trecând în elementul care corespunde rândului curent. Metoda revine Adevărat dacă checkedItems conține articol.

func tableView (_tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell // Returnați elementul lasă item = items [indexPath.row] // Dequeue Cell lasă cell = tableView.dequeueReusableCell (withIdentifier: "TableViewCell" ) // Configure Cell cell.textLabel? .Text = element dacă checkedItems.contains (item) cell.accessoryType = .checkmark altceva cell.accessoryType = .none return cell

Dacă articol se găsește în checkedItems, am setat celula accessoryType proprietate la .bifează marcajul, o valoare a membrilor UITableViewCellAccessoryType enumerare. Dacă articol nu este găsit, ne întoarcem la .nici unul ca tip accesoriu al celulei.

Următorul pas este adăugarea abilității de a marca un element așa cum este făcut prin implementarea unei metode a UITableViewDelegate protocol, tableView (_: didSelectRowAt :). În această metodă delegată, sunăm mai întâi deselectRow (la: animat :) pe tableView pentru a deselecta rândul pe care l-a lovit utilizatorul.

// MARK: - Metode de vizualizare a tablelor Delegate func tableView (_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) tableView.deselectRow (la: indexPath, animat: true) // Fetch Item let = item [indexPath.row] // Fetch Cell lasa cell = tableView.cellForRow (la: indexPath) // Găsiți Indexul elementului let index = checkedItems.index (de: item) dacă let index = index checkedItems.remove (at: index) cell? .AccessoryType =. none altceva checkedItems.append (element) cell? .accessoryType = .checkmark

Apoi, preluăm elementul corespunzător de la articole și o referință la celula care corespunde cu rândul apăsat. Noi intrebam checkedItems pentru indexul elementului corespunzător invocând Index de:). Această metodă returnează o opțiune Int. Dacă checkedItems conține articol, îl eliminăm checkedItems și setați tipul de accesorii al celulei .nici unul. Dacă checkedItems nu conține articol, îl adăugăm checkedItems și setați tipul de accesorii al celulei .bifează marcajul.

Cu aceste adăugiri, utilizatorul poate acum să marcheze articole ca terminate. Construiți și rulați aplicația pentru a vă asigura că totul funcționează conform așteptărilor.

3. Statul de economisire

În prezent, aplicația nu salvează statul între lansări. Pentru a rezolva acest lucru, vom stoca articole și checkedItems în baza de date implicită a aplicației.

Pasul 1: Starea de încărcare

Începeți prin crearea a două metode de ajutor, loadItems () și loadCheckedItems (). Rețineți privat cuvinte cheie care prefixează fiecare metodă de ajutor. privat cuvântul cheie spune Swift că aceste metode sunt accesibile numai din interiorul ViewController clasă.

// MARK: Metode Private Helper private func loadItems () let userDefaults = UserDefaults.standard dacă let items = userDefaults.object (pentruKey: "items") ca? [String] self.items = items priv func loadCheckedItems () let userDefaults = UserDefaults.standard dacă lasă checkedItems = userDefaults.object (pentruKey: "checkedItems") ca? [String] self.checkedItems = checkedItems

 privat cuvântul cheie face parte din Swift's controlul accesului. După cum sugerează și numele, controlul accesului definește ce cod are acces la care cod. Nivelurile de acces se aplică metodelor, funcțiilor, tipurilor etc. Apple se referă pur și simplu la entități. Există cinci niveluri de acces: deschise, publice, interne, private-file și private.

  • Deschideți / Public: Entitățile marcate ca deschise sau publice sunt accesibile entităților definite în același modul, precum și altor module. Acesta este ideal pentru a expune interfața unui cadru. Există mai multe diferențe între nivelele deschise și cele de acces public. Puteți citi mai multe despre aceste diferențe în Limba de programare Swift.
  • Intern: Acesta este nivelul implicit de acces. Cu alte cuvinte, dacă nu este specificat niciun nivel de acces, acest nivel de acces se aplică. O entitate cu un nivel de acces intern este accesibilă numai entităților definite în același modul.
  • File-privat: O entitate declarată ca fișier privat este accesibilă numai entităților definite în același fișier sursă. De exemplu, metodele de ajutor privat definite în ViewController clasa sunt accesibile numai de către ViewController clasă.
  • Privat: Private este foarte similar cu fișierul privat. Singura diferență este că o entitate declarată privată este accesibilă numai din declarația în care este anexată. De exemplu, dacă creem o extensie pentru ViewController clasă în ViewController.swift, toate entitățile care sunt marcate ca fișiere private nu ar fi accesibile în extensie, însă entitățile private ar fi accesibile.

Implementarea metodelor de ajutor este simplă dacă sunteți familiarizat cu UserDefaults clasă. Pentru ușurința utilizării, stocăm o referință la obiectul implicit pentru utilizatori standard într-o constanță numită userDefaults. În cazul în care loadItems (), noi intrebam userDefaults pentru obiectul asociat cheii „elemente“ și downcast la o serie opțională de șiruri de caractere. Desfășurăm în mod sigur opțiunea, ceea ce înseamnă că stocăm valoarea în constanță articole dacă opțiunea nu este zero, și atribuie valoarea lui articole proprietatea controlorului de vizualizare.

În cazul în care dacă declarația pare confuză, apoi aruncați o privire la o versiune mai simplă a loadItems () în exemplul următor. Rezultatul este identic; singura diferență este concisitatea.

private func loadItems () let userDefaults = UserDefaults.standard lăsa stocItems = userDefaults.object (pentruKey: "items") ca? [String] dacă permiteți elemente = storedItems self.items = items

Implementarea sistemului loadCheckedItems () este identic, cu excepția cheii folosite pentru încărcarea obiectului stocat în baza de date implicită a utilizatorului. Sa punem loadItems () și loadCheckedItems () să se utilizeze prin actualizarea viewDidLoad () metodă.

override funcția viewDidLoad () super.viewDidLoad () // Set titlul titlu = "a face" // Populate item items = ["Cumpăra lapte", "Finalizați tutorial", "Play Minecraft"] // Load State loadItems loadCheckedItems () // Înregistrați Clasa pentru reutilizarea celulelor tableView.register (UITableViewCell.self, forCellReuseIdentifier: "TableViewCell")

Pasul 2: Statul de economisire

Pentru a salva statul, implementăm două metode de ajutor privat, saveitems () și saveCheckedItems (). Logica este similară cu cea a lui loadItems () și loadCheckedItems (). Diferența este că stocăm datele în baza de date implicită a utilizatorilor. Asigurați-vă că tastele utilizate în setObject (_: forKey :) apelurile se potrivesc cu cele utilizate în loadItems () și loadCheckedItems ().

private func saveItems () permite userDefaults = UserDefaults.standard // Actualizeaza setarile utilizatorului userDefaults.set (items, forKey: "items") userDefaults.synchronize () private function saveCheckedItems () let usernameDefaults = UserDefaults.standard // Update Setări implicite utilizator userDefaults.set (checkedItems, pentruKey: "checkedItems") userDefaults.synchronize ()

sincroniza() apelul nu este strict necesar. Sistemul de operare se va asigura că datele stocate în baza de date implicită a utilizatorului sunt scrise pe disc la un moment dat. Invocând sincroniza(), cu toate acestea, spuneți în mod explicit sistemului de operare să scrie orice modificări în așteptare la disc. Acest lucru este util în timpul dezvoltării, deoarece sistemul de operare nu va scrie modificările pe disc dacă omori aplicația. Se poate ca atunci când ceva nu funcționează corect.

Trebuie să invocăm saveitems () și saveCheckedItems () în mai multe locuri. Pentru a începe, apelați saveitems () atunci când un element nou este adăugat în listă. Facem acest lucru în metoda delegat a AddItemViewControllerDelegate protocol.

// MARK: Adăugare element Vizualizare controler Delegate Metode func controller (_ controler: AddItemViewController, didAddItem: String) // Update Sursa de date items.append (didAddItem) // Save State saveItems () // Reîncarcă tabela View tableView.reloadData ( ) // Eliminați adăugarea elementului Vizualizați controlerul (animat: true)

Când starea unui element se modifică în tableView (_: didSelectRowAt :), ne actualizăm checkedItems. Este o idee bună să invocați saveCheckedItems () in acel moment.

// MARK: - Metode de vizualizare a tablelor Delegate func tableView (_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) tableView.deselectRow (la: indexPath, animat: true) // Fetch Item let = item [indexPath.row] // Fetch Cell lasa cell = tableView.cellForRow (la: indexPath) // Găsiți Indexul elementului let index = checkedItems.index (de: item) dacă let index = index checkedItems.remove (at: index) cell? .AccessoryType =. none altceva checkedItems.append (element) cell? .accessoryType = .checkmark // Salvați stat saveCheckedItems ()

Când un element este șters, ambele articole și checkedItems sunt actualizate. Pentru a salva această modificare, sunăm amândoi saveitems () și saveCheckedItems ().

func tableView (_ tableView: UITableView, comite editingStyle: UITableViewCellEditingStyle, pentruRowAt indexPath: IndexPath) dacă editingStyle == .delete // Fetch Item let = item [indexPath.row] // Actualizați items.remove (at: indexPath .row) dacă index = checkedItems.index (de: item) checkedItems.remove (la: index) // Actualizare tabelă View tableView.deleteRows (la: [indexPath], cu:. () saveCheckedItems ()

Asta e. Construiți și rulați aplicația pentru a vă testa munca. Joacă-te cu aplicația și forțează-o. Când lansați din nou aplicația, ultima stare cunoscută trebuie să fie încărcată și vizibilă.

4. Observatorii proprietății

Experiența utilizatorului din aplicație este puțin lipsită de moment. Când fiecare element este șters sau când aplicația este lansată pentru prima dată, utilizatorul vede o vizualizare de tabelă goală. Acest lucru nu este minunat. Putem rezolva acest lucru prin afișarea unui mesaj atunci când nu există elemente. Acest lucru îmi va da ocazia să vă arăt o altă caracteristică a lui Swift, observatori de proprietate.

Pasul 1: Adăugarea unei etichete

Să începem prin adăugarea unei etichete la interfața de utilizator pentru afișarea mesajului. Declarați o priză numită messageLabel de tip UILabel în ViewController clasă, deschisă Main.storyboard, și adăugați o etichetă la vizualizarea controlerului de vizualizare.

@IBOutlet var mesajLabel: UILabel!

Adăugați constrângerile privind aspectul necesar etichetei și conectați-o cu controlerul de vizualizare messageLabel ieșire în Conectarea inspectorului. Setați textul etichetării la Nu ai niciun dos. și centralizați textul etichetei în Atribuții Inspector.

Pasul 2: Implementarea unui observator de proprietate

Eticheta mesajului trebuie să fie vizibilă numai dacă articole nu conține elemente. Când se întâmplă acest lucru, ar trebui să ascundem și vizualizarea tabelului. Am putea rezolva această problemă prin adăugarea de diverse verificări în ViewController dar o abordare mai convenabilă și mai elegantă este utilizarea unui observator de proprietate.

După cum sugerează și numele, observatorii de proprietate observă o proprietate. Un observator de proprietate este invocat ori de câte ori o proprietate se schimbă, chiar dacă noua valoare este aceeași cu cea a vechii valori. Există două tipuri de observatori de proprietate.

  • willSet: invocate înainte ca valoarea să se modifice
  • didSet: invocată după ce valoarea sa schimbat

Pentru scopul nostru, vom implementa didSet observator pentru articole proprietate. Uitați-vă la sintaxa din următorul fragment de cod.

obiecte var: [String] = [] didSet lăsați hasItems = items.count> 0 tableView.isHidden =! hasItems messageLabel.isHidden = hasItems

Construcția ar putea părea un pic ciudat la început, așa că lasă-mă să explic ce se întâmplă. Cand didSet observatorul de proprietate este invocat, după articole proprietatea sa schimbat, verificăm dacă articole proprietatea conține orice elemente. Pe baza valorii hasItems constanta, actualizam interfata de utilizator. Este la fel de simplu ca asta.

didSet observatorul primește un parametru constant care conține valoarea vechii valori a proprietății. Este omisă în exemplul de mai sus, deoarece nu avem nevoie de ea în implementarea noastră. Următorul exemplu arată modul în care ar putea fi utilizat.

var elemente: [String] = [] didSet (oldValue) dacă oldValue! = elemente let hasItems = items.count> 0 tableView.isHidden =! hasItems messageLabel.isHidden = hasItems

OLDVALUE parametrul din exemplu nu are un tip explicit, deoarece Swift cunoaște tipul de articole proprietate. În exemplu, actualizăm interfața de utilizator numai dacă valoarea veche diferă de noua valoare.

A willSet observatorul lucrează într-un mod similar. Principala diferență constă în faptul că parametrul a fost transferat către willSet observatorul este o constantă care deține noua valoare a proprietății. Când utilizați observatori de proprietăți, rețineți că nu sunt invocate atunci când instanța este inițializată.

Construiți și rulați aplicația pentru a vă asigura că totul este conectat corect. Chiar dacă aplicația nu este perfectă și ar putea utiliza mai multe caracteristici, ați creat prima aplicație iOS folosind Swift.

Concluzie

În cursul ultimelor trei lecții din această serie, ați creat o aplicație funcțională iOS utilizând caracteristicile orientate spre obiect Swift. Dacă aveți experiență de programare și dezvoltare de aplicații, atunci trebuie să fi observat că modelul actual de date are câteva deficiențe,.

Stocarea elementelor ca șiruri de caractere și crearea unui tablou separat pentru a stoca starea unui element nu este o idee bună dacă construiți o aplicație adecvată. O abordare mai bună ar fi aceea de a crea un sistem separat A face pentru a modela elementele și a le păstra în nisipul aplicației. Acesta va fi scopul nostru pentru următoarea lecție din această serie.

Între timp, verificați câteva dintre celelalte cursuri și tutoriale despre dezvoltarea iOS a limbajului Swift!

Cod