I den tidigare lektionen har vi lagt till möjligheten att skapa uppgiftsobjekt. Medan denna tillägg har gjort programmet lite mer användbar, skulle det också vara bekvämt att lägga till möjligheten att markera objekt som färdiga och radera objekt. Det är vad vi ska fokusera på i den här lektionen.
Om du vill följa med mig, se till att du har Xcode 8.3.2 eller högre installerad på din maskin. Du kan ladda ner Xcode 8.3.2 från Apples App Store.
För att radera objekt måste vi genomföra två ytterligare metoder för UITableViewDataSource
protokoll. Vi måste först se tabellvyn vilka rader som kan redigeras genom att implementera Tableview (_: canEditRowAt :)
metod. Som du kan se i koden nedan är implementeringen enkel. Vi berättar på tabellen att varje rad kan redigeras genom att återvända Sann
.
func tableView (_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool return true
Den andra metoden vi är intresserade av är Tableview (_: commit: forRowAt :)
. Implementeringen är lite mer komplex men lätt att förstå.
func tableView (_ tableView: UITableView, commit redigeringStyle: UITableViewCellEditingStyle, förRowAt indexPath: IndexPath) om redigeringStyle == .delete // Uppdatera objekt items.remove (på: indexPath.row) // Uppdatera tabellvy tableView.deleteRows : [indexPath], med: .right)
Vi börjar med att kontrollera värdet av editingStyle
, en uppräkning av typ UITableViewCellEditingStyle
. Vi tar bara bort ett objekt om värdet av editingStyle
är lika med UITableViewCellEditingStyle.delete
.
Swift är smartare än det. Eftersom det vet det editingStyle
är av typ UITableViewCellEditingStyle
, vi kan utelämna UITableViewCellEditingStyle
, namnet på uppräkningen och skriv .radera
, medlemsvärdet av den uppräkning som vi är intresserade av. Om du är ny på uppräkningar i Swift rekommenderar jag dig att läsa detta snabba tips om uppräkningar i Swift.
Därefter uppdaterar vi tabellvyns datakälla, objekt
, genom att åberopa ta bort (vid :)
på objekt
egendom, passerar i rätt index. Vi uppdaterar även tabellvyn genom att åberopa deleteRows (vid: med :)
på Tableview
, passerar i en array med indexPath
och .höger
för att ange animationstypen. Som vi såg tidigare kan vi utesluta namnet på uppräkningen, UITableViewRowAnimation
, eftersom Swift vet vilken typ av det andra argumentet är UITableViewRowAnimation
.
Användaren ska nu kunna radera objekt från listan. Bygg och kör programmet för att testa detta.
För att markera ett föremål som gjort, lägger vi till en ruta i motsvarande rad. Det innebär att vi måste hålla reda på de saker som användaren har markerat som gjort. Därför förklarar vi en ny egendom som hanterar detta för oss. Förklara en variabel egenskap, checkedItems
, av typ [Sträng]
, och initiera den med en tom matris.
var checkedItems: [String] = []
I Tableview (_: cellForRowAt :)
, vi kontrollerar om checkedItems
innehåller respektive objekt genom att åberopa innehåller (_ :)
metod, passerar i objektet som motsvarar den aktuella raden. Metoden returnerar Sann
om checkedItems
innehåller Artikel
.
func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell // Hämta Item let item = objekt [indexPath.row] // Dequeue Cell låt cell = tableView.dequeueReusableCell (withIdentifier: "TableViewCell", för: indexPath ) // Konfigurera Cell cell.textLabel? .Text = objekt om checkedItems.contains (item) cell.accessoryType = .checkmark else cell.accessoryType = .none returnera cell
Om Artikel
finns i checkedItems
, vi satte cellens accessoryType
egendom till .bock
, ett medlemsvärde av UITableViewCellAccessoryType
uppräkning. Om Artikel
finns inte, vi faller tillbaka till .ingen
som cellens tillbehörstyp.
Nästa steg är att lägga till möjligheten att markera ett objekt som gjort genom att implementera en metod av UITableViewDelegate
protokoll, Tableview (_: didSelectRowAt :)
. I denna delegerade metod kallar vi först deselectRow (vid: animerade :)
på Tableview
för att avmarkera raden som användaren knackade på.
// MARK: - Tabellvisning Delegerade metoder func tableView (_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) tableView.deselectRow (vid: indexPath, animated: true) // Hämta objektlåtobjekt = objekt [indexPath.row] // Hämta cellen låt cellen = tableView.cellForRow (vid: indexPath) // Hitta index över objekt let index = checkedItems.index (av: objekt) om låt index = index checkedItems.remove (vid: index) cell? .AccessoryType =. ingen annars checkedItems.append (item) cell? .accessoryType = .checkmark
Vi hämtar sedan motsvarande föremål från objekt
och en referens till cellen som motsvarar den tappade raden. Vi frågar checkedItems
för index för motsvarande objekt genom att åberopa index för:)
. Denna metod returnerar en valfri int
. Om checkedItems
innehåller Artikel
, vi tar bort det från checkedItems
och ställ in cellens tillbehörstyp till .ingen
. Om checkedItems
innehåller inte Artikel
, vi lägger till den till checkedItems
och ställ in cellens tillbehörstyp till .bock
.
Med dessa tillägg kan användaren nu markera objekt som gjort. Bygg och kör programmet för att se till att allt fungerar som förväntat.
Ansökan sparar inte tillstånd mellan lanseringar. För att lösa detta ska vi lagra objekt
och checkedItems
arrays i programmets användarinställningsdatabas.
Börja med att skapa två hjälpar metoder, loadItems ()
och loadCheckedItems ()
. Notera privat
Nyckelord prefix varje hjälpar metod. De privat
sökord berättar Swift att dessa metoder endast är tillgängliga från inom ViewController
klass.
// MARK: Privata hjälpar metoder privata func loadItems () let userDefaults = UserDefaults.standard om låter objekt = userDefaults.object (forKey: "items") som? [String] self.items = items privat func loadCheckedItems () let userDefaults = UserDefaults.standard om låt checkedItems = userDefaults.object (forKey: "checkedItems") som? [String] self.checkedItems = checkedItems
De privat
sökord är en del av swift s åtkomstkontroll. Som namnet antyder definierar åtkomstkontroll vilken kod som har åtkomst till vilken kod. Åtkomstnivåer gäller metoder, funktioner, typer etc. Apple refererar bara till enheter. Det finns fem åtkomstnivåer: öppna, offentliga, interna, fil-privata och privata.
ViewController
klassen är endast tillgänglig av ViewController
klass.ViewController
klass i ViewController.swift, Alla enheter som är markerade som fil-privata skulle inte vara tillgängliga i tillägget, men privata enheter skulle vara tillgängliga.Implementeringen av hjälpar metoder är enkel om du är bekant med UserDefaults
klass. För enkel användning, lagrar vi en referens till standardvärdet för standardinställningar i en konstant namngiven userDefaults
. I fallet med loadItems ()
, vi frågar userDefaults
för objektet som är associerat med nyckeln "objekt"
och sänka den till ett valfritt utbud av strängar. Vi sparar säkert valfri, vilket innebär att vi lagrar värdet i konstanten objekt
om valfri inte är noll
, och ange värdet till objekt
egenskapskontrollenheten.
Om om
uttalandet ser förvirrande ut, sedan ta en titt på en enklare version av loadItems ()
metod i följande exempel. Resultatet är identiskt; Den enda skillnaden är korthet.
privata func loadItems () låt userDefaults = UserDefaults.standard låt storageItems = userDefaults.object (forKey: "items") som? [String] om låt poster = lagradeItems self.items = items
Genomförandet av loadCheckedItems ()
är identisk med undantag för nyckeln som används för att ladda objektet som är lagrat i databasens standardinställningar. Låt oss sätta loadItems ()
och loadCheckedItems ()
att använda genom att uppdatera viewDidLoad ()
metod.
åsidosätta func viewDidLoad () super.viewDidLoad () // Set Titel title = "Att göra" // Populera artiklar objekt = ["Köp mjölk", "Slutför självstudie", "Spela Minecraft"] // Ladda State loadItems loadCheckedItems () // Registrera Klass för Cellåtervinning tableView.register (UITableViewCell.self, forCellReuseIdentifier: "TableViewCell")
För att rädda staten implementerar vi ytterligare två privata hjälpar metoder, saveitems ()
och saveCheckedItems ()
. Logiken liknar den för loadItems ()
och loadCheckedItems ()
. Skillnaden är att vi lagrar data i databasens standardinställningar. Kontrollera att knapparna som används i setObject (_: forKey :)
samtal matchar de som används loadItems ()
och loadCheckedItems ()
.
privat func saveItems () let userDefaults = UserDefaults.standard // Uppdatera användarinställningar userDefaults.set (items, forKey: "items") userDefaults.synchronize () privat func saveCheckedItems () let userDefaults = UserDefaults.standard // Uppdatering Användarinställningar userDefaults.set (checkedItems, forKey: "checkedItems") userDefaults.synchronize ()
De synkronisera()
samtal är inte strikt nödvändigt. Operativsystemet ser till att data som du lagrar i databasens standard är skrivet till disken vid något tillfälle. Genom att åberopa synkronisera()
, Men du säger explicit att operativsystemet skriver några väntande ändringar på disken. Detta är användbart under utveckling, eftersom operativsystemet inte skriver dina ändringar till disken om du dödar programmet. Det kan då verka som om något inte fungerar ordentligt.
Vi måste åberopa saveitems ()
och saveCheckedItems ()
på ett antal platser. För att börja ringa saveitems ()
när ett nytt objekt läggs till i listan. Vi gör detta i delegationsmetoden för AddItemViewControllerDelegate
protokoll.
// MARK: Lägg till objekt Visa Controller Delegate Methods func controller (_ controller: AddItemViewController, didAddItem: String) // Uppdatera datakälla items.append (didAddItem) // Spara State saveItems () // Uppdatera tabellvy tableView.reloadData ( ) // Avvisa Lägg till föremål Visa Controller dismiss (animated: true)
När tillståndet för ett objekt ändras i Tableview (_: didSelectRowAt :)
, vi uppdaterar checkedItems
. Det är en bra idé att också åberopa saveCheckedItems ()
vid det tillfället.
// MARK: - Tabellvisning Delegerade metoder func tableView (_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) tableView.deselectRow (vid: indexPath, animated: true) // Hämta objektlåtobjekt = objekt [indexPath.row] // Hämta cellen låt cellen = tableView.cellForRow (vid: indexPath) // Hitta index över objekt let index = checkedItems.index (av: objekt) om låt index = index checkedItems.remove (vid: index) cell? .AccessoryType =. ingen annan checkedItems.append (item) cell? .accessoryType = .checkmark // Spara stat sparaCheckedItems ()
När ett objekt raderas, båda objekt
och checkedItems
uppdateras. För att rädda denna ändring, ringer vi båda saveitems ()
och saveCheckedItems ()
.
func tableView (_ tableView: UITableView, commit redigeringStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) om redigeringStyle == .delete // Hämta objektlåt objekt = objekt [indexPath.row] // Uppdatera objekt items.remove (vid: indexPath .row) om låt index = checkedItems.index (av: item) checkedItems.remove (på: index) // Uppdatera tabell Visa tableView.deleteRows (vid: [indexPath], with: .right) // Spara State SaveItems () sparaCheckedItems ()
Det är allt. Bygg och kör programmet för att testa ditt arbete. Spela med programmet och tvinga sluta det. När du startar programmet igen ska det senast kända tillståndet laddas och synas.
Applikationens användarupplevelse är lite bristfällig just nu. När varje objekt raderas eller när programmet startas för första gången ser användaren en tom tabellvy. Detta är inte bra. Vi kan lösa detta genom att visa ett meddelande när det inte finns några föremål. Detta ger mig också möjlighet att visa dig en annan funktion hos Swift, fastighetsobservatörer.
Låt oss börja med att lägga till en etikett i användargränssnittet för att visa meddelandet. Förklara ett utlopp som heter messageLabel
av typ UILabel
i ViewController
klass, öppen Main.storyboard, och lägg till en etikett i vyens kontroll.
@IBOutlet var messageLabel: UILabel!
Lägg till nödvändiga layoutbegränsningar på etiketten och anslut den med bildkontrollen messageLabel
utlopp i Anslutningsinspektör. Ange etikettens text till Du har ingen dosering. och centrera etikettens text i Attribut Inspector.
Meddelandemärket ska bara vara synligt om objekt
innehåller inga element. När det händer bör vi också dölja tabellvyn. Vi kunde lösa detta problem genom att lägga till olika kontroller i ViewController
klass, men ett mer bekvämt och elegant tillvägagångssätt är att använda en fastighetsobservatör.
Som namnet antyder observerar fastighetsobservatörer en egendom. En egenskapsobservatör åberopas när en egenskap ändras, även om det nya värdet är detsamma som det gamla värdet. Det finns två typer av fastighetsobservatörer.
kommer sätta
: påkallad innan värdet har ändratsdidSet
: åberopas efter att värdet har ändratsFör vårt syfte kommer vi att genomföra didSet
observatör för objekt
fast egendom. Ta en titt på syntaxen i följande kodbit.
var poster: [String] = [] didSet let hasItems = items.count> 0 tableView.isHidden =! hasItems messageLabel.isHidden = hasItems
Konstruktionen kan se lite udda först, så låt mig förklara vad som händer. När didSet
egenskapsobservatör åberopas, efter objekt
fastigheten har förändrats, vi kontrollerar om objekt
egendom innehåller några element. Baserat på värdet av hasItems
konstant, vi uppdaterar användargränssnittet. Så enkelt är det.
De didSet
observatören överförs en konstant parameter som innehåller värdet av egenskapens gamla värde. Det utelämnas i ovanstående exempel, eftersom vi inte behöver det i vår implementering. Följande exempel visar hur det kan användas.
var items: [String] = [] didSet (oldValue) om oldValue! = objekt let hasItems = items.count> 0 tableView.isHidden =! hasItems messageLabel.isHidden = hasItems
De oldValue
parametern i exemplet har ingen explicit typ, eftersom Swift känner till typen av objekt
fast egendom. I exemplet uppdaterar vi bara användargränssnittet om det gamla värdet skiljer sig från det nya värdet.
en kommer sätta
observatör fungerar på ett liknande sätt. Huvudskillnaden är att parametern övergår till kommer sätta
observatör är en konstant innehav av det nya värdet av fastigheten. När du använder fastighetsobservatörer, kom ihåg att de inte åberopas när instansen initieras.
Bygg och kör programmet för att se till att allt är anslutet korrekt. Även om programmet inte är perfekt och kan använda några fler funktioner, har du skapat ditt första iOS-program med Swift.
Under de senaste tre lektionerna i denna serie skapade du ett funktionellt iOS-program med Swifts objektorienterade funktioner. Om du har någon erfarenhet av programmering och utveckling av applikationer, måste du ha märkt att den nuvarande datamodellen har några brister, för att uttrycka det lätt.
Om du lagrar objekt som strängar och skapar en separat matris för att lagra ett objekts tillstånd, är det inte en bra idé om du bygger en riktig applikation. Ett bättre tillvägagångssätt skulle vara att skapa en separat Att göra
klass för modellering av objekt och lagra dem i programmets sandlåda. Det är vårt mål för nästa lektion i serien.
Under tiden, kolla in några av våra andra kurser och handledning om Swift-språk iOS-utveckling!