Menu Deroulant

Menu Deroulant

Simple exemple d'une liste qui s'affiche sous un bouton quand celui-ci est activé.

1- Storyboard

Créer deux boutons dans le storyboard. Ajouter les @IBOutlets et @IBActions pour chaque bouton dans le viewController.

2- Vue sombre

Dans ViewController.swift, créer une vue qui va assombrir la vue principale quand la liste s'affiche.

let darkenView = UIView()

Quand un bouton est pressé, la "darkenView" apparaît par dessus la vue des boutons, et la liste s'affichera sous le bouton sélectionné.

Nous ajoutons cette vue sombre avec une fonction :

func addDarkenView() {}

Cette vue sombre aura les mêmes dimensions que la vue courante.

La couleur noire lui est attribuée en couleur de fond, avec un alpha de 0.6

 func addDarkenView() {
        darkenView.frame = view.frame
        darkenView.backgroundColor = UIColor.black.withAlphaComponent(0.6)
        self.view.addSubview(darkenView)
}

3- Gestion des vues

Pour retirer cette vue sombre et revenir sur la vue des deux boutons, nous ajoutons un "tapGesture" sur la vue sombre, qui change son alpha à 0.

Pour ajouter de nouveau la "darkenView" lors de l'appui sur le second bouton, son alpha est redéfinit à 0.6 après le "tapGesture".

func addDarkenView() {
...
let tapgesture = UITapGestureRecognizer(target: self, action: #selector(removeDarkenView))
        darkenView.addGestureRecognizer(tapgesture)
        self.darkenView.alpha = 0.6
}

@objc func removeDarkenView() {
         self.darkenView.alpha = 0
    }

4- Test

En appelant la fonction "addDarkenView()" dans chaque "action button", la vue sombre apparaît et disparaît.

Pour avoir un affichage plus doux des vues, nous pouvons utiliser une animation.

func addDarkenView() {
        darkenView.frame = view.frame
        darkenView.backgroundColor = UIColor.black.withAlphaComponent(0.6)
        self.view.addSubview(darkenView)

        let tapgesture = UITapGestureRecognizer(target: self, action: #selector(removeDarkenView))
        darkenView.addGestureRecognizer(tapgesture)
        darkenView.alpha = 0
        UIView.animate(withDuration: 0.4, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: {
            self.darkenView.alpha = 0.6
        }, completion: nil)
    }

    @objc func removeDarkenView() {
        UIView.animate(withDuration: 0.4, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: {
            self.darkenView.alpha = 0
        }, completion: nil)
    }

5- Une TableView pour la liste

Nous ajoutons une tableView :

let tableView = UITableView()

La position de la tableView dépendra de la position des boutons. Un paramètre "frames" est ajouter à la méthode "addDarkenView()", pour récupérer les "frames" du bouton concerné.

func addDarkenView(frames: CGRect) {}

Et mettons à jour les appels au niveau des actions buttons pour passer en paramètre les frames du bouton sélectionné.

 @IBAction func paysButtonPressed(_ sender: Any) {
        addDarkenView(frames: paysButton.frame)
    }

 @IBAction func villeButtonPressed(_ sender: Any) {
        addDarkenView(frames: villeButton.frame)
    }

Nous pouvons définir dans la fonction "addDarkenView() la position et les dimensions de la tableView.

tableView.frame = CGRect(x: frames.origin.x, y: frames.origin.y + frames.height, width: frames.width, height: 0)

. x: frames.origin.x : le bord gauche de la table sera aligné verticalement avec celui du bouton

. y: frames.origin.y + frames.height : la table débutera juste sous le bouton (nous ajoutons la hauteur du bouton : frames.height). Nous pouvons ajouter un intervalle : "y: frames.origin.y + frames.height + 5"

. width: frames.width : la table aura la même largeur que le bouton

. height: 0 : pour l'instant il n'y a rien à afficher, donc hauteur nulle

Ajoutons la table dans la vue, avec un arrondi :

self.view.addSubview(tableView)

tableView.layer.cornerRadius = 5

6- Sélection des boutons

Pour éviter toutes confusions dans les dimensions des boutons, nous ajoutons une propriété bouton sélectionné :

var selectedButton = UIButton()

Et lui attribuons en valeur le bouton concerné.

@IBAction func paysButtonPressed(_ sender: Any) {
        selectedButton = paysButton
        addDarkenView(frames: paysButton.frame)
    }

    @IBAction func villeButtonPressed(_ sender: Any) {
        selectedButton = villeButton
        addDarkenView(frames: villeButton.frame)
    }

7- Hauteur de la tableView

Nous avons démarré la table avec une hauteur nulle, car il n'y a pas de données à afficher.

Quand nous revenons sur la vue des deux boutons sans la vue sombre, avec la fonction func removeDarkenView(), la table disparaîtra également. Nous pouvons préciser une table avec des valeurs à 0 dans cette méthode.

@objc func removeDarkenView() {

        UIView.animate(withDuration: 0.4, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: {
            self.darkenView.alpha = 0
            tableView.frame = CGRect(x: 0, y: 0, width: 0, height: 0)
        }, completion: nil)
    }

8- Test 2

Pour voir la table à l'écran, nous lui allouons une hauteur de "200" Dans la méthode func addDarkenView() , nous précisons après l'alpha, les caractéristiques de la table :

func addDarkenView(frames: CGRect) {
        darkenView.frame = view.frame
        darkenView.backgroundColor = UIColor.black.withAlphaComponent(0.6)
        self.view.addSubview(darkenView)

        tableView.frame = CGRect(x: frames.origin.x, y: frames.origin.y + frames.height, width: frames.width, height: 0)
        self.view.addSubview(tableView)
        tableView.layer.cornerRadius = 5

        let tapgesture = UITapGestureRecognizer(target: self, action: #selector(removeDarkenView))
        darkenView.addGestureRecognizer(tapgesture)

        self.darkenView.alpha = 0.6
        tableView.frame = CGRect(x: frames.origin.x, y: frames.origin.y + frames.height + 5, width: frames.width, height: 200)
    }

Sous chaque bouton sélectionné, apparaît maintenant une liste, vide pour l'instant.

9- Gestion des données à afficher

Nous ajoutons le "delegate" et le "DataSource" dans viewDidLoad() :

override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self
        tableView.dataSource = self
}

Créer un tableau pour les données,

var dataSource = [String]()

Pour ce simple exemple, nous ajoutons des données pour chaque bouton. Mais nous pourrions avoir un tableau avec un tableau de catégories pour le premier bouton, et un tableau de données pour le second.

@IBAction func onClickSelectCountry(_ sender: Any) {
        dataSource = ["France", "Portugual", "Allemagne"]
...
@IBAction func onClickSelectCity(_ sender: Any) {
        dataSource = ["Paris", "Bordeaux", "Lyon", "Marseille", "Lille", "Nantes"]

10- La cellule

Créer une classe pour les "tableViewCell"

import UIKit

class CellClass: UITableViewCell { 
}

class ViewController: UIViewController {
....

Affecter la cellule à la table dans la méthode "viewDidLoad()"

tableView.register(CellClass.self, forCellReuseIdentifier: "Cell")

11- Protocole

On se conforme au protocole en implémentant, en extension, les méthodes nécessaires

extension ViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataSource.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        cell.textLabel?.text = dataSource[indexPath.row]
        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 50
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        selectedButton.setTitle(dataSource[indexPath.row], for: .normal)
        removeDarkenView()
    }
}

Il y a deux tableaux de données, donc on recharge la table entre chaque affichage. Dans la méthode addDarkenView(), ajouter

tableView.reloadData()

12- Spécification de la hauteur de la liste

La hauteur de la liste est définit par le nombre de cellule et la hauteur de la cellule :

dataSource.count * 50

Remplacer la hauteur de la liste, précédemment définit à 200, par :

tableView.frame = CGRect(x: frames.origin.x, y: frames.origin.y + frames.height + 5, width: frames.width, height: CGFloat(self.dataSource.count * 50))

Capture d’écran 2021-04-02 à 10.44.11.png

Dans cet exemple les boutons ont des largeurs différentes et sont centrés. Il est imaginable d'avoir un bouton dans la barre de navigation pour faire apparaître un menu.

Retrouver ce simple code sur : GitHub