In de vorige les hebben we de mogelijkheid toegevoegd om actie-items te maken. Hoewel deze toevoeging de toepassing iets nuttiger heeft gemaakt, zou het ook handig zijn om de mogelijkheid toe te voegen om items te markeren als gereed en items verwijderen. Dat is waar we ons in deze les op zullen concentreren.
Als je me wilt volgen, zorg er dan voor dat Xcode 8.3.2 of hoger op je computer is geïnstalleerd. Je kunt Xcode 8.3.2 downloaden van de App Store van Apple.
Om items te verwijderen, moeten we twee extra methoden van de UITableViewDataSource
protocol. We moeten eerst in de tabel zien welke rijen kunnen worden bewerkt door het tableView (_: canEditRowAt :)
methode. Zoals u in het onderstaande codefragment kunt zien, is de implementatie eenvoudig. We vertellen de tabelweergave dat elke rij bewerkbaar is door terug te keren waar
.
func tableView (_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool return true
De tweede methode waarin we geïnteresseerd zijn, is tableView (_: commit: forRowAt :)
. De implementatie is wat ingewikkelder, maar eenvoudig genoeg om te begrijpen.
func tableView (_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) if editingStyle == .delete // Update Items items.remove (at: indexPath.row) // Update tabel TabelView.deleteRows (op : [indexPath], met: .Right)
We beginnen met het controleren van de waarde van editingStyle
, een opsomming van het type UITableViewCellEditingStyle
. We verwijderen alleen een item als de waarde van editingStyle
is gelijk aan UITableViewCellEditingStyle.delete
.
Swift is echter slimmer dan dat. Omdat het dat weet editingStyle
is van het type UITableViewCellEditingStyle
, we kunnen weglaten UITableViewCellEditingStyle
, de naam van de opsomming en schrijf .verwijderen
, de lidwaarde van de opsomming waarin we geïnteresseerd zijn. Als je nieuw bent in opsommingen in Swift, raad ik je aan deze korte tip over opsommingen in Swift te lezen.
Vervolgens werken we de gegevensbron van de tabelweergave bij, items
, door aan te roepen verwijderen (bij :)
op de items
eigendom, waarbij de juiste index wordt opgegeven. We werken ook het tabeloverzicht bij door op te roepen deleteRows (bij: met :)
op tableView
, passeren in een array met indexPath
en .rechts
om het animatietype te specificeren. Zoals we eerder zagen, kunnen we de naam van de opsomming weglaten, UITableViewRowAnimation
, omdat Swift weet welk type van het tweede argument is UITableViewRowAnimation
.
De gebruiker moet nu items uit de lijst kunnen verwijderen. Bouw en voer de applicatie uit om dit te testen.
Om een item als gereed te markeren, voegen we een vinkje toe aan de bijbehorende rij. Dit betekent dat we de items die de gebruiker heeft gemarkeerd als voltooid, moeten bijhouden. Voor dat doel zullen we een nieuwe woning aanduiden die dit voor ons beheert. Declareer een variabel eigendom, checkedItems
, van type [Draad]
, en initialiseer het met een lege array.
var checkedItems: [String] = []
In tableView (_: cellForRowAt :)
, we controleren of checkedItems
bevat het respectieve item door het bevat (_ :)
methode, waarbij het item wordt doorgegeven dat overeenkomt met de huidige rij. De methode retourneert waar
als checkedItems
bevat item
.
func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell // Fetch Item let item = items [indexPath.row] // Cel van wachtrij laten cel = tableView.dequeueReusableCell (withIdentifier: "TableViewCell", voor: indexPath ) // Configure Cell cell.textLabel? .Text = item als checkedItems.contains (item) cell.accessoryType = .checkmark else cell.accessoryType = .none return cell
Als item
is te vinden in checkedItems
, we plaatsen de cellen accessoryType
eigendom aan .vinkje
, een lidwaarde van de UITableViewCellAccessoryType
opsomming. Als item
wordt niet gevonden, we vallen terug naar .geen
als het accessoire-type van de cel.
De volgende stap is het toevoegen van de mogelijkheid om een item als voltooid te markeren door een methode van de UITableViewDelegate
protocol, tableView (_: didSelectRowAt :)
. In deze gedelegeerde methode bellen we eerst deselectRow (bij: geanimeerde :)
op tableView
om de rij te deselecteren waarop de gebruiker heeft getikt.
// MARK: - Tabel View Delegate Methods func tableView (_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) tableView.deselectRow (at: indexPath, geanimeerde: true) // Fetch Item let item = items [indexPath.row] // Cel ophaalcel ophalen = tableView.cellForRow (at: indexPath) // Index van item zoeken let index = checkedItems.index (van: item) if let index = index checkedItems.remove (at: index) cell? .AccessoryType =. none else checkedItems.append (item) cell? .accessoryType = .checkmark
Vervolgens halen we het bijbehorende item op items
en een verwijzing naar de cel die overeenkomt met de afgetopte rij. Wij vragen checkedItems
voor de index van het overeenkomstige item door aan te roepen index van:)
. Deze methode retourneert een optioneel Int
. Als checkedItems
bevat item
, we verwijderen het van checkedItems
en stel het soort accessoire van de cel in .geen
. Als checkedItems
bevat niet item
, we voegen het toe checkedItems
en stel het soort accessoire van de cel in .vinkje
.
Met deze toevoegingen kan de gebruiker items nu als voltooid markeren. Bouw en voer de applicatie uit om te zorgen dat alles werkt zoals verwacht.
De applicatie slaat momenteel geen status op tussen de lanceringen. Om dit op te lossen, gaan we het opslaan items
en checkedItems
arrays in de database met gebruikersstandaarden van de toepassing.
Begin met het maken van twee hulpmethoden, loadItems ()
en loadCheckedItems ()
. Merk op privaat
voorvoegsel voor elke helpermethode. De privaat
keyword vertelt Swift dat deze methoden alleen toegankelijk zijn vanuit de ViewController
klasse.
// MARK: Private Helper Methods private func loadItems () let userDefaults = UserDefaults.standard if let items = userDefaults.object (forKey: "items") als? [String] self.items = items private func loadCheckedItems () let userDefaults = UserDefaults.standard if let checkedItems = userDefaults.object (forKey: "checkedItems") as? [String] self.checkedItems = checkedItems
De privaat
trefwoord maakt deel uit van Swift's toegangscontrole. Zoals de naam al aangeeft, definieert toegangscontrole welke code toegang heeft tot welke code. Toegangsniveaus zijn van toepassing op methoden, functies, typen, etc. Apple verwijst eenvoudigweg naar entiteiten. Er zijn vijf toegangsniveaus: open, openbaar, intern, bestand-privé en privé.
ViewController
klasse is alleen toegankelijk via de ViewController
klasse.ViewController
klasse in ViewController.swift, entiteiten die zijn gemarkeerd als bestand-privé zouden niet toegankelijk zijn in de extensie, maar privé-entiteiten zouden toegankelijk zijn.De implementatie van de hulpmethoden is eenvoudig als u bekend bent met de UserDefaults
klasse. Voor het gebruiksgemak slaan we een verwijzing op naar het standaard standaard gebruikersobject in een constante met de naam userDefaults
. In het geval van loadItems ()
, wij vragen userDefaults
voor het object dat is gekoppeld aan de sleutel "Items"
en downcast het naar een optionele array van strings. We pakken de optionele optie veilig uit, wat betekent dat we de waarde in de constante opslaan items
als de optionele niet is nul
, en wijs de waarde toe aan de items
eigendom van de view controller.
Als het als
verklaring lijkt verwarrend, kijk dan eens naar een eenvoudigere versie van de loadItems ()
methode in het volgende voorbeeld. Het resultaat is identiek; het enige verschil is bondigheid.
private func loadItems () let userDefaults = UserDefaults.standard laat storedItems = userDefaults.object (forKey: "items") als? [String] if let items = storedItems self.items = items
De implementatie van loadCheckedItems ()
is identiek, behalve de sleutel die wordt gebruikt om het object te laden dat is opgeslagen in de database met gebruikersstandaarden. Laten we loadItems ()
en loadCheckedItems ()
te gebruiken door het updaten van viewDidLoad ()
methode.
override func viewDidLoad () super.viewDidLoad () // Titel instellen title = "Taken" // Items vullen met items = ["Melk kopen", "Les voltooien", "Minecraft spelen"] // Load State loadItems () loadCheckedItems () // Klasse registreren voor Cell Reuse tableView.register (UITableViewCell.self, forCellReuseIdentifier: "TableViewCell")
Om de status te behouden, implementeren we nog twee persoonlijke hulpmethoden, saveItems ()
en saveCheckedItems ()
. De logica is vergelijkbaar met die van loadItems ()
en loadCheckedItems ()
. Het verschil is dat we gegevens opslaan in de database met gebruikersstandaarden. Zorg ervoor dat de toetsen die in de setObject (_: Forkey :)
oproepen komen overeen met die gebruikt in loadItems ()
en loadCheckedItems ()
.
private func saveItems () let userDefaults = UserDefaults.standard // Update User Defaults userDefaults.set (items, forKey: "items") userDefaults.synchronize () private func saveCheckedItems () let userDefaults = UserDefaults.standard // Update Standaard gebruikersinstellingen userDefaults.set (checkedItems, forKey: "checkedItems") userDefaults.synchronize ()
De synchroniseren()
bellen is niet strikt noodzakelijk. Het besturingssysteem zorgt ervoor dat de gegevens die u in de standaard gebruikersdatabase opslaat, naar de schijf worden geschreven op een gegeven moment. Door aan te roepen synchroniseren()
, u geeft het besturingssysteem echter expliciet de opdracht om eventuele wijzigingen in behandeling op schijf te schrijven. Dit is handig tijdens de ontwikkeling, omdat het besturingssysteem uw wijzigingen niet naar de schijf zal schrijven als u de toepassing doodt. Het kan dan lijken alsof iets niet goed werkt.
We moeten aanroepen saveItems ()
en saveCheckedItems ()
op een aantal plaatsen. Om te beginnen, bel saveItems ()
wanneer een nieuw item aan de lijst wordt toegevoegd. We doen dit in de gedelegeerde methode van de AddItemViewControllerDelegate
protocol.
// MARK: Add Item View Controller Delegate Methods func controller (_ controller: AddItemViewController, didAddItem: String) // Update Data Source items.append (didAddItem) // Save State saveItems () // Reload Table View tableView.reloadData ( ) // Dismiss Add Item View Controller dismiss (geanimeerd: true)
Wanneer de status van een item verandert in de tableView (_: didSelectRowAt :)
, we updaten checkedItems
. Het is een goed idee om ook aan te roepen saveCheckedItems ()
op dat punt.
// MARK: - Tabel View Delegate Methods func tableView (_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) tableView.deselectRow (at: indexPath, geanimeerde: true) // Fetch Item let item = items [indexPath.row] // Cel ophaalcel ophalen = tableView.cellForRow (at: indexPath) // Index van item zoeken let index = checkedItems.index (van: item) if let index = index checkedItems.remove (at: index) cell? .AccessoryType =. none else checkedItems.append (item) cell? .accessoryType = .checkmark // Save State saveCheckedItems ()
Wanneer een item wordt verwijderd, beide items
en checkedItems
zijn bijgewerkt. Om deze wijziging op te slaan, bellen we beide saveItems ()
en saveCheckedItems ()
.
func tableView (_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) if editingStyle == .delete // Fetch Item let item = items [indexPath.row] // Update Items items.remove (at: indexPath .row) if let index = checkedItems.index (van: item) checkedItems.remove (at: index) // Update tabel TabelView.deleteRows (op: [indexPath], met: .Right) // Save State saveItems () saveCheckedItems ()
Dat is het. Bouw en voer de applicatie uit om uw werk te testen. Speel met de applicatie en stop ermee. Wanneer u de toepassing opnieuw start, moet de laatst bekende status worden geladen en zichtbaar zijn.
De gebruikerservaring van de applicatie ontbreekt op dit moment een beetje. Wanneer elk item wordt verwijderd of wanneer de toepassing voor de eerste keer wordt gestart, ziet de gebruiker een leeg tabeloverzicht. Dit is niet geweldig. We kunnen dit oplossen door een bericht weer te geven als er geen items zijn. Dit geeft me ook de mogelijkheid om je een andere functie van Swift te laten zien, vastgoedwaarnemers.
Laten we beginnen met het toevoegen van een label aan de gebruikersinterface voor het tonen van het bericht. Verklaar een afzet genaamd messageLabel
van type UILabel
in de ViewController
klas, open Main.storyboard, en voeg een label toe aan de weergave van de view controller.
@IBOutlet var messageLabel: UILabel!
Voeg de nodige layoutbeperkingen toe aan het label en verbind het met de view controller's messageLabel
stopcontact in de Verbindingen Inspecteur. Zet de tekst van het label op Je hebt geen taken. en centreer de tekst van het label in de Kenmerken Inspector.
Het berichtlabel moet alleen zichtbaar zijn als items
bevat geen elementen. Wanneer dat gebeurt, moeten we ook de tabelweergave verbergen. We zouden dit probleem kunnen oplossen door verschillende controles toe te voegen in de ViewController
klasse, maar een handigere en elegantere benadering is om een waarnemer te gebruiken.
Zoals de naam al aangeeft, observeren vastgoedwaarnemers een eigendom. Een eigenschapwaarnemer wordt opgeroepen wanneer een eigenschap verandert, zelfs wanneer de nieuwe waarde gelijk is aan de oude waarde. Er zijn twee typen waarnemers voor onroerend goed.
zal instellen
: aangeroepen voordat de waarde is gewijzigddidSet
: wordt aangeroepen nadat de waarde is gewijzigdVoor ons doel zullen we het didSet
waarnemer voor de items
eigendom. Bekijk de syntaxis in het volgende codefragment.
var items: [String] = [] didSet let hasItems = items.count> 0 tableView.isHidden =! hasItems messageLabel.isHidden = hasItems
Het construct ziet er in het begin misschien wat raar uit, dus laat me uitleggen wat er gebeurt. Wanneer de didSet
eigenschap waarnemer wordt aangeroepen, na de items
eigendom is veranderd, wij controleren of het items
property bevat elementen. Gebaseerd op de waarde van de hasItems
constant, werken we de gebruikersinterface bij. Zo simpel is het.
De didSet
waarnemer krijgt een constante parameter doorgegeven die de waarde van de oude waarde van de eigenschap bevat. Het is weggelaten in het bovenstaande voorbeeld, omdat we het niet nodig hebben bij onze implementatie. Het volgende voorbeeld laat zien hoe het kan worden gebruikt.
var items: [String] = [] didSet (oldValue) if oldValue! = items let hasItems = items.count> 0 tableView.isHidden =! hasItems messageLabel.isHidden = hasItems
De OldValue
parameter in het voorbeeld heeft geen expliciet type, omdat Swift het type kent items
eigendom. In het voorbeeld werken we alleen de gebruikersinterface bij als de oude waarde verschilt van de nieuwe waarde.
EEN zal instellen
waarnemer werkt op een vergelijkbare manier. Het belangrijkste verschil is dat de parameter is doorgegeven aan de zal instellen
waarnemer is een constante die de nieuwe waarde van het eigendom behoudt. Wanneer u vastgoedwaarnemers gebruikt, moet u er rekening mee houden dat ze niet worden aangeroepen wanneer de instantie wordt geïnitialiseerd.
Bouw en voer de applicatie uit om te controleren of alles correct is aangesloten. Hoewel de toepassing niet perfect is en nog een paar andere functies kan gebruiken, hebt u uw eerste iOS-applicatie gemaakt met Swift.
In de loop van de laatste drie lessen van deze serie heb je een functionele iOS-applicatie gemaakt met behulp van de objectgerichte functies van Swift. Als je wat ervaring hebt met het programmeren en ontwikkelen van applicaties, dan moet je gemerkt hebben dat het huidige datamodel enkele tekortkomingen vertoont, om het zachtjes te formuleren.
Het opslaan van items als strings en het maken van een aparte array om de status van een item op te slaan, is geen goed idee als u een juiste applicatie aan het bouwen bent. Een betere benadering zou zijn om een aparte te maken Te doen
klasse voor het modelleren van items en deze op te slaan in de sandbox van de toepassing. Dat is ons doel voor de volgende les van deze serie.
Bekijk in de tussentijd enkele van onze andere cursussen en zelfstudies over de ontwikkeling van de Swift-taal voor iOS!