Sobes.tech
Middle

Comment le système détermine-t-il dans quelle vue le toucher est arrivé?

sobes.tech IA

Réponse de l'IA

Le système utilise deux mécanismes clés : le test de clic (hit-testing) et la hiérarchie des vues.

  1. Test de clic (Hit-testing) : Le framework UIKit utilise la méthode hitTest(_:with:) de la vue racine de la fenêtre (UIWindow) pour déterminer quelle vue se trouve sous le point de contact. Cette méthode parcourt récursivement la hiérarchie des vues, en commençant par la racine.

  2. Hiérarchie des vues :

    • La méthode hitTest(_:with:) vérifie d’abord si le point de contact est à l’intérieur des limites de la vue courante. Si ce n’est pas le cas, elle retourne nil.
    • Si le point est à l’intérieur des limites, la méthode examine les vues enfants en ordre inverse (de la dernière ajoutée à la première). Pour chaque vue enfant, elle appelle sa propre méthode hitTest(_:with:) avec le même point de contact (converti dans les coordonnées de la vue enfant).
    • La première vue enfant dont la méthode hitTest(_:with:) retourne un résultat non-nil (c’est-à-dire, elle ou une de ses vues enfants contient le point) est considérée comme "touchée" et ce résultat est renvoyé vers le haut dans la hiérarchie.
    • Si aucune vue enfant ne contient le point de contact, et que la vue courante contient le point et n’a pas userInteractionEnabled = false, le système considère que le contact a eu lieu dans la vue courante, et la méthode retourne self.
    • Si userInteractionEnabled de la vue courante est false, ou si hidden est true, ou si alpha est inférieur à 0.01, la méthode retourne nil, même si le point est à l’intérieur des limites.

Le processus continue en descendant dans la hiérarchie jusqu’à ce que la vue la plus basse contenant le point de contact soit trouvée. Cette vue devient la hit-test view ou hit-test result, et c’est elle (ou son ancêtre le plus proche approprié) qui recevra la gestion ultérieure de l’événement (méthodes touchesBegan, touchesMoved, etc.).

Voici comment redéfinir hitTest ou point(inside:with:) pour un comportement personnalisé de hit-testing :

import UIKit

class CustomView: UIView {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        // Vérifie si le point est à l’intérieur de cette vue.
        // Si ce n’est pas le cas, retourne nil.
        if !self.point(inside: point, with: event) {
            return nil
        }

        // Parcourt les vues enfants en ordre inverse.
        for subview in subviews.reversed() {
            // Convertit le point dans les coordonnées de la vue enfant.
            let convertedPoint = subview.convert(point, from: self)

            // Appelle récursivement hitTest sur la vue enfant.
            if let hitTestView = subview.hitTest(convertedPoint, with: event) {
                // Si la vue enfant ou ses descendants contiennent le point, retourne ce résultat.
                return hitTestView
            }
        }

        // Si aucune vue enfant n’a traité le contact,
        // et que cette vue est interactive et contient le point, retourne self.
        return self
    }

    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        // La mise en œuvre de base vérifie si le point est à l’intérieur des limites et si la vue est interactive.
        // return super.point(inside: point, with: event)

        // Peut être redéfini pour des formes personnalisées ou des marges, par exemple, augmenter la zone de contact.
        let extendedBounds = bounds.insetBy(dx: -20, dy: -20) // Agrandit la zone de 20pts dans toutes les directions
        return extendedBounds.contains(point)
    }
}

La méthode point(inside:with:) est appelée depuis hitTest(_:with:) pour vérifier rapidement si le point est à l’intérieur de la vue avant de vérifier ses vues enfants.

En fin de compte, la vue la plus basse dans la hiérarchie qui contient le point de contact et qui a userInteractionEnabled à true, hidden à false et alpha supérieur à 0.01 est celle qui sera considérée comme la hit-test view.