Swift From Scratch Observateurs de contrôle d'accès et de propriété

Dans la leçon précédente, nous avons ajouté la possibilité de créer des tâches. Bien que cet ajout ait rendu l'application un peu plus utile, il serait également pratique d'ajouter la possibilité de marquer des éléments comme étant terminés et de les supprimer. C'est ce sur quoi nous allons nous concentrer dans cette leçon.

Conditions préalables

Si vous souhaitez suivre avec moi, assurez-vous que Xcode 8.3.2 ou une version supérieure est installé sur votre ordinateur. Vous pouvez télécharger Xcode 8.3.2 à partir de l'App Store d'Apple.

1. Suppression d'éléments

Pour supprimer des éléments, nous devons implémenter deux méthodes supplémentaires du UITableViewDataSource protocole. Nous devons d’abord indiquer à la vue tableau quelles lignes peuvent être éditées en mettant en œuvre tableView (_: canEditRowAt :) méthode. Comme vous pouvez le voir dans l'extrait de code ci-dessous, la mise en œuvre est simple. Nous disons au tableau que chaque ligne est éditable en retournant vrai.

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

La deuxième méthode qui nous intéresse est tableView (_: commit: forRowAt :). La mise en œuvre est un peu plus complexe mais assez facile à comprendre.

func tableView (_ tableView: UITableView, commettent une édition de style. UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) if éditerStyle == .delete // Mettre à jour les éléments items.remove (à: indexPath.row) // View Table View tableView.deleteRows (at : [indexPath], avec: .right)

Nous commençons par vérifier la valeur de style de montage, une énumération de type UITableViewCellEditingStyle. Nous ne supprimons un élément que si la valeur de style de montage est égal à UITableViewCellEditingStyle.delete.

Swift est plus intelligent que cela, cependant. Parce qu'il sait que style de montage est de type UITableViewCellEditingStyle, nous pouvons omettre UITableViewCellEditingStyle, le nom de l'énumération, et écrivez .effacer, la valeur membre de l'énumération qui nous intéresse. Si vous débutez dans les énumérations de Swift, je vous recommande de lire cette astuce concernant les énumérations de Swift..

Ensuite, nous mettons à jour la source de données de la vue table., articles, en invoquant enlever (à :) sur le articles propriété, en passant dans le bon index. Nous mettons également à jour la vue de table en appelant deleteRows (à: avec :) sur tableView, passer dans un tableau avec indexPath et .droite pour spécifier le type d'animation. Comme nous l'avons vu précédemment, nous pouvons omettre le nom de l'énumération, UITableViewRowAnimation, puisque Swift sait que le type du second argument est UITableViewRowAnimation.

L'utilisateur devrait maintenant pouvoir supprimer des éléments de la liste. Générez et exécutez l'application pour tester cela.

2. Cochez les articles

Pour marquer un élément comme terminé, nous allons ajouter une coche à la ligne correspondante. Cela implique que nous devions suivre les éléments que l'utilisateur a marqués comme étant effectués. Pour cela, nous allons déclarer une nouvelle propriété qui gère cela pour nous. Déclarer une propriété de variable, éléments vérifiés, de type [Chaîne], et l'initialiser avec un tableau vide.

var checkItems: [String] = []

Dans tableView (_: cellForRowAt :), on vérifie si éléments vérifiés contient l'élément correspondant en invoquant le contient (_ :) méthode, en passant l’élément correspondant à la ligne en cours. La méthode retourne vrai si éléments vérifiés contient article.

func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell // Extraire l'élément item = éléments [indexPath.row] // Dequeue Cell laisse cell = tableView.dequeueReusableCell (withIdentifier: "TableViewCell", pour: indexPath ) // Configurez la cellule cell.textLabel? .Text = item si selectedItems.contains (item) cell.accessoryType = .checkmark else cell.accessoryType = .none return cell

Si article se trouve dans éléments vérifiés, nous définissons la cellule type d'accessoire propriété à .coche, une valeur membre du UITableViewCellAccessoryType énumération. Si article n'est pas trouvé, nous retombons à .aucun comme type d'accessoire de la cellule.

L’étape suivante consiste à ajouter la possibilité de marquer un élément de la même manière en implémentant une méthode de UITableViewDelegate protocole, tableView (_: didSelectRowAt :). Dans cette méthode déléguée, nous appelons d’abord deselectRow (à: animé :) sur tableView pour désélectionner la ligne que l'utilisateur a tapée.

// MARK: - Vue de table Méthodes de délégué func tableView (_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) tableView.deselectRow (à: indexPath, animated: true) // Récupération d'un élément, élément = éléments [indexPath.row] // Fetch Cell let cell = tableView.cellForRow (at: indexPath) // // Index de recherche d'élément let index = checkedItems.index (of: item) if let index = index checkedItems.remove (at: index) cell?? .AccessoryType =. aucun autre vérifiéItems.append (élément) cellule? .accessoryType = .checkmark

Nous allons ensuite chercher l'article correspondant de articles et une référence à la cellule qui correspond à la ligne tapée. Nous demandons éléments vérifiés pour l'index de l'élément correspondant en invoquant Indice de:). Cette méthode retourne un optionnel Int. Si éléments vérifiés contient article, on l'enlève éléments vérifiés et définissez le type d'accessoire de la cellule sur .aucun. Si éléments vérifiés ne contient pas article, on l'ajoute à éléments vérifiés et définissez le type d'accessoire de la cellule sur .coche.

Avec ces ajouts, l'utilisateur peut maintenant marquer les éléments comme étant terminés. Générez et exécutez l'application pour vous assurer que tout fonctionne comme prévu.

3. État d'épargne

L'application ne sauvegarde pas actuellement l'état entre les lancements. Pour résoudre ce problème, nous allons stocker le articles et éléments vérifiés tableaux de la base de données par défaut de l'application.

Étape 1: état de chargement

Commencez par créer deux méthodes d'assistance, loadItems () et loadCheckedItems (). Noter la privé mot-clé préfixant chaque méthode d'assistance. le privé mot-clé indique à Swift que ces méthodes ne sont accessibles que depuis le ViewController classe.

// MARK: Méthodes d'assistance privées private func loadItems () let userDefaults = UserDefaults.standard if let items = userDefaults.object (forKey: "items") as? [String] self.items = items private func loadCheckedItems () let userDefaults = UserDefaults.standard si let checkedItems = userDefaults.object (forKey: "checkedItems") comme? [String] self.checkedItems = disabledItems

le privé mot-clé fait partie de Swift contrôle d'accès. Comme son nom l'indique, le contrôle d'accès définit quel code a accès à quel code. Les niveaux d'accès s'appliquent aux méthodes, fonctions, types, etc. Apple se réfère simplement à entités. Il existe cinq niveaux d’accès: ouvert, public, interne, fichier privé et privé..

  • Open / Public: Les entités marquées comme ouvertes ou publiques sont accessibles aux entités définies dans le même module ainsi que d'autres modules. C'est idéal pour exposer l'interface d'un framework. Il existe plusieurs différences entre les niveaux d’accès ouvert et public. Vous pouvez en savoir plus sur ces différences dans le langage de programmation Swift.
  • Interne: C'est le niveau d'accès par défaut. En d'autres termes, si aucun niveau d'accès n'est spécifié, ce niveau d'accès s'applique. Une entité avec un niveau d'accès interne n'est accessible que par des entités définies dans le même module.
  • Fichier-privé: Une entité déclarée comme privée de fichier n'est accessible que par des entités définies dans le même fichier source. Par exemple, les méthodes d’aide privée définies dans la ViewController classe ne sont accessibles que par le ViewController classe.
  • Privé: Private est très similaire à file-private. La seule différence est qu'une entité déclarée privée est uniquement accessible depuis la déclaration dans laquelle elle est incluse. Par exemple, si nous créons une extension pour le ViewController cours en ViewController.swift, toutes les entités marquées comme fichier-privé ne seraient pas accessibles dans l'extension, mais les entités privées seraient accessibles.

La mise en œuvre des méthodes d’aide est simple si vous connaissez le UserDefaults classe. Pour faciliter l’utilisation, nous stockons une référence à l’objet standard par défaut de l’utilisateur dans une constante nommée userDefaults. Dans le cas de loadItems (), nous demandons userDefaults pour l'objet associé à la clé "articles" et le downcast à un tableau facultatif de chaînes. Nous déroulons en toute sécurité l’option, ce qui signifie que nous stockons la valeur dans la constante articles si facultatif n'est pas néant, et attribuer la valeur à la articles propriété du contrôleur de vue.

Si la si déclaration semble déroutant, puis jetez un coup d’œil à une version simplifiée du loadItems () méthode dans l'exemple suivant. Le résultat est identique. la seule différence est la concision.

func privé loadItems () let userDefaults = UserDefaults.standard laisse stockéItems = userDefaults.object (forKey: "items") en tant que? [Chaîne] si let items = storageItems self.items = items

L'implémentation de loadCheckedItems () est identique à l'exception de la clé utilisée pour charger l'objet stocké dans la base de données par défaut de l'utilisateur. Mettons loadItems () et loadCheckedItems () utiliser en mettant à jour le viewDidLoad () méthode.

remplacer func viewDidLoad () super.viewDidLoad () // Définir le titre title = "À faire" // Remplir les éléments items = ["Acheter du lait", "Terminer le didacticiel", "Lire Minecraft"] // État de chargement loadItems () loadCheckedItems () // Classe de registre pour la réutilisation de cellules tableView.register (UITableViewCell.self, forCellReuseIdentifier: "TableViewCell")

Étape 2: État de sauvegarde

Pour sauver l'état, nous implémentons deux méthodes supplémentaires d'assistance privée, saveItems () et saveCheckedItems (). La logique est similaire à celle de loadItems () et loadCheckedItems (). La différence est que nous stockons des données dans la base de données par défaut de l'utilisateur. Assurez-vous que les clés utilisées dans le setObject (_: forKey :) les appels correspondent à ceux utilisés dans loadItems () et loadCheckedItems ().

private func saveItems () let userDefaults = UserDefaults.standard // Mise à jour des paramètres utilisateur userDefaults.set (items, forKey: "items") userDefaults.synchronize () private func saveCheckedItems () let userDefaults = UserDefaults.standard // Update Valeurs utilisateur userDefaults.set (vérifiéItems, forKey: "vérifiéItems") userDefaults.synchronize ()

le synchroniser() appeler n'est pas strictement nécessaire. Le système d'exploitation s'assurera que les données que vous stockez dans la base de données par défaut de l'utilisateur sont écrites sur le disque. à un moment donné. En invoquant synchroniser(), Cependant, vous indiquez explicitement au système d'exploitation d'écrire les modifications en attente sur le disque. Ceci est utile pendant le développement, car le système d'exploitation n'écrira pas vos modifications sur le disque si vous supprimez l'application. Il peut alors sembler que quelque chose ne fonctionne pas correctement.

Nous devons invoquer saveItems () et saveCheckedItems () dans un certain nombre de lieux. Pour commencer, appelez saveItems () lorsqu'un nouvel élément est ajouté à la liste. Nous faisons cela dans la méthode des délégués du AddItemViewControllerDelegate protocole.

// MARK: Ajouter un élément Vue Contrôleur délégué Méthodes func contrôleur (contrôleur _: addItemViewController, didAddItem: String) // Mise à jour de la source de données items.append (didAddItem) // Enregistrer l'état saveItems () // Recharger la vue de table ) // Refuser d'ajouter un élément Voir le contrôleur ignorer (animé: true)

Lorsque l'état d'un élément change dans le tableView (_: didSelectRowAt :), nous mettons à jour éléments vérifiés. C'est une bonne idée d'invoquer aussi saveCheckedItems () à ce moment.

// MARK: - Vue de table Méthodes de délégué func tableView (_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) tableView.deselectRow (à: indexPath, animated: true) // Récupération d'un élément, élément = éléments [indexPath.row] // Fetch Cell let cell = tableView.cellForRow (at: indexPath) // // Index de recherche d'élément let index = checkedItems.index (of: item) if let index = index checkedItems.remove (at: index) cell?? .AccessoryType =. none else celluleImérée.append (élément)?? typeAccessory = .checkmark // État d'enregistrement saveCheckedItems ()

Lorsqu'un élément est supprimé, les deux articles et éléments vérifiés sont mis à jour. Pour enregistrer ce changement, nous appelons les deux saveItems () et saveCheckedItems ().

func tableView (_ tableView: UITableView, commit éditionStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) si éditionStyle == .delete // Récupération d’objet item = éléments [indexPath.row] // if Eléments Item. .row) if let index = checkedItems.index (of: item) checkedItems.remove (at: index) // Mise à jour de la vue table tableView.deleteRows (à: [indexPath], avec: .right) // Save State saveItems () saveCheckedItems ()

C'est tout. Générez et exécutez l'application pour tester votre travail. Jouez avec l'application et forcez-la à la quitter. Lorsque vous relancez l'application, le dernier état connu doit être chargé et visible..

4. Observateurs de la propriété

L'expérience utilisateur de l'application manque un peu pour le moment. Lorsque chaque élément est supprimé ou lorsque l'application est lancée pour la première fois, l'utilisateur voit une vue sous forme de tableau vide. Ce n'est pas génial. Nous pouvons résoudre ce problème en affichant un message lorsqu'il n'y a aucun élément. Cela me donnera également l’occasion de vous montrer une autre fonctionnalité de Swift., observateurs immobiliers.

Étape 1: Ajout d'une étiquette

Commençons par ajouter une étiquette à l'interface utilisateur pour afficher le message. Déclarer un point de vente nommé messageLabel de type UILabel dans le ViewController classe ouverte Tableau principal, et ajouter une étiquette à la vue du contrôleur de vue.

@IBOutlet var messageLabel: UILabel!

Ajoutez les contraintes de mise en page nécessaires à l’étiquette et connectez-la à celle du contrôleur de vue. messageLabel sortie dans le Inspecteur de connexions. Définissez le texte de l'étiquette sur Vous n'avez aucune tâche à faire. et centrer le texte de l'étiquette dans le Inspecteur d'attributs.

Étape 2: Implémentation d'un Property Observer

Le libellé du message ne doit être visible que si articles ne contient aucun élément. Lorsque cela se produit, nous devrions également masquer la vue du tableau. Nous pourrions résoudre ce problème en ajoutant divers contrôles dans le ViewController classe, mais une approche plus pratique et élégante consiste à utiliser un observateur de la propriété.

Comme leur nom l'indique, les observateurs de propriété observent une propriété. Un observateur de propriété est appelé chaque fois qu'une propriété est modifiée, même lorsque la nouvelle valeur est identique à l'ancienne. Il y a deux types d'observateurs immobiliers.

  • willSet: invoqué avant que la valeur ait changé
  • didSet: invoqué après que la valeur ait changé

Pour ce faire, nous allons mettre en œuvre le didSet observateur pour articles propriété. Examinez la syntaxe dans l'extrait de code suivant..

var items: [String] = [] didSet let hasItems = items.count> 0 tableView.isHidden =! hasItems messageLabel.isHidden = hasItems

La construction peut sembler un peu étrange au début, alors laissez-moi vous expliquer ce qui se passe. Quand le didSet l’observateur de propriété est invoqué, après articles propriété a changé, nous vérifions si le articles propriété contient des éléments. Basé sur la valeur de la hasItems constante, nous mettons à jour l'interface utilisateur. C'est aussi simple que ça.

le didSet Observer reçoit un paramètre constant contenant la valeur de l'ancienne valeur de la propriété. Il est omis dans l'exemple ci-dessus, car nous n'en avons pas besoin dans notre mise en œuvre. L'exemple suivant montre comment il pourrait être utilisé.

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

le oldValue paramètre dans l'exemple n'a pas de type explicite, car Swift connaît le type du articles propriété. Dans l'exemple, nous ne mettons à jour l'interface utilisateur que si l'ancienne valeur diffère de la nouvelle valeur..

UNE willSet l'observateur travaille de la même manière. La principale différence est que le paramètre passé à la willSet observateur est une constante tenant la nouvelle valeur de la propriété. Lors de l'utilisation d'observateurs de propriétés, n'oubliez pas qu'ils ne sont pas appelés lorsque l'instance est initialisée..

Générez et exécutez l'application pour vous assurer que tout est connecté correctement. Bien que l’application ne soit pas parfaite et puisse utiliser quelques fonctionnalités supplémentaires, vous avez créé votre première application iOS à l’aide de Swift..

Conclusion

Au cours des trois dernières leçons de cette série, vous avez créé une application iOS fonctionnelle utilisant les fonctionnalités orientées objet de Swift. Si vous avez une expérience de la programmation et du développement d’applications, vous devez avoir remarqué que le modèle de données actuel présente quelques inconvénients, pour le dire légèrement..

Stocker des éléments en tant que chaînes et créer un tableau séparé pour stocker l'état d'un élément n'est pas une bonne idée si vous créez une application appropriée. Une meilleure approche consisterait à créer un Faire classe pour la modélisation des éléments et les stocker dans le bac à sable de l'application. Ce sera notre objectif pour la prochaine leçon de cette série..

En attendant, consultez certains de nos autres cours et tutoriels sur le développement iOS en langage Swift!