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.
-
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. -
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 retournenil. - 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 retourneself. - Si
userInteractionEnabledde la vue courante estfalse, ou sihiddenesttrue, ou sialphaest inférieur à 0.01, la méthode retournenil, même si le point est à l’intérieur des limites.
- La méthode
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.