Swift From Scratch toegangscontrole en waarnemingswaarnemers

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.

voorwaarden

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.

1. Items verwijderen

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.

2. Items afvinken

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.

3. Staat opslaan

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.

Stap 1: Staat laden

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é.

  • Open / Ppenbare: Entiteiten die zijn gemarkeerd als open of openbaar, zijn toegankelijk voor entiteiten die in dezelfde module zijn gedefinieerd, en voor andere modules. Dit is ideaal om de interface van een raamwerk te belichten. Er zijn verschillende verschillen tussen de open en openbare toegangsniveaus. U kunt meer lezen over deze verschillen in The Swift programmeertaal.
  • intern: Dit is het standaard toegangsniveau. Met andere woorden, als er geen toegangsniveau is opgegeven, is dit toegangsniveau van toepassing. Een entiteit met een toegangsniveau van intern is alleen toegankelijk voor entiteiten die in dezelfde module zijn gedefinieerd.
  • File-Private: Een entiteit die als privé-bestand is gedeclareerd, is alleen toegankelijk voor entiteiten die in hetzelfde bronbestand zijn gedefinieerd. Bijvoorbeeld de persoonlijke hulpmethoden die zijn gedefinieerd in de ViewController klasse is alleen toegankelijk via de ViewController klasse.
  • Privaat: Prive lijkt erg op file-private. Het enige verschil is dat een als privé gedeclareerde entiteit alleen toegankelijk is vanuit de aangegane verklaring. Als we bijvoorbeeld een extensie maken voor de 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")

Stap 2: Staat opslaan

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.

4. Eigendomswaarnemers

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.

Stap 1: Een label toevoegen

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.

Stap 2: Een waarnemingswaarnemer implementeren

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 gewijzigd
  • didSet: wordt aangeroepen nadat de waarde is gewijzigd

Voor 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.

Conclusie

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!