Î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.
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.
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.
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.
Î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.
Î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.
ViewController
clasa sunt accesibile numai de către ViewController
clasă.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")
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ă.
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.
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.
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 modificedidSet
: invocată după ce valoarea sa schimbatPentru 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.
Î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!