Cum determină sistemul în ce vizualizare a avut loc atingerea?
sobes.tech AI
Răspuns de la AI
Sistemul utilizează două mecanisme cheie: testarea de atingere (hit-testing) și ierarhia vizualizărilor.
-
Testarea de atingere (Hit-testing): Framework-ul UIKit folosește metoda
hitTest(_:with:)a viziunii rădăcină (UIWindow) pentru a determina care vizualizare se află sub punctul de atingere. Această metodă parcurge recursiv ierarhia vizualizărilor, începând cu rădăcina. -
Ierarhia vizualizărilor:
- Metoda
hitTest(_:with:)verifică mai întâi dacă punctul de atingere se află în interiorul limitei vizualizării curente. Dacă nu, returneazănil. - Dacă punctul este în interior, metoda examinează vizualizările copil în ordine inversă (de la ultima adăugată la prima). Pentru fiecare vizualizare copil, se apelează propria metodă
hitTest(_:with:)cu același punct de atingere (convertit în coordonatele vizualizării copil). - Prima vizualizare copil pentru care metoda
hitTest(_:with:)returnează un rezultat diferit denil(adică ea sau unul dintre descendenții săi conține punctul) este considerată "atinsă" și acest rezultat este returnat în sus pe ierarhie. - Dacă nici o vizualizare copil nu conține punctul de atingere, iar vizualizarea curentă conține punctul și nu are
userInteractionEnabled = false, sistemul consideră că atingerea a avut loc în vizualizarea curentă, și metoda returneazăself. - Dacă
userInteractionEnabledal vizualizării curente estefalse, sauhiddenestetrue, saualphaeste mai mic decât 0.01, metoda returneazănil, chiar dacă punctul se află în interiorul limitei.
- Metoda
Procesul continuă în jos pe ierarhie până se găsește cea mai joasă vizualizare care conține punctul de atingere. Această vizualizare devine hit-test view sau hit-test result, și anume acesteia (sau părintelui său cel mai apropiat) i se va delega gestionarea ulterioară a evenimentului (metodele touchesBegan, touchesMoved etc.).
Iată cum se poate suprascrie hitTest sau point(inside:with:) pentru comportament personalizat de hit-testing:
import UIKit
class CustomView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// Verificăm dacă punctul se află în interiorul vizualizării noastre.
// Dacă nu, returnăm nil.
if !self.point(inside: point, with: event) {
return nil
}
// Parcurgem vizualizările copil în ordine inversă adăugării.
for subview in subviews.reversed() {
// Convertim punctul din coordonatele părintelui în cele ale vizualizării copil.
let convertedPoint = subview.convert(point, from: self)
// Apelăm recursiv hitTest pentru vizualizarea copil.
if let hitTestView = subview.hitTest(convertedPoint, with: event) {
// Dacă vizualizarea copil sau descendenții săi conține punctul, returnăm rezultatul.
return hitTestView
}
}
// Dacă nimeni dintre copiii vizualizării nu a gestionat atingerea,
// iar vizualizarea curentă este interactivă și conține punctul, returnăm self.
return self
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
// Implementarea de bază verifică dacă punctul se află în limitele vizualizării
// și dacă vizualizarea este activă (userInteractionEnabled, alpha > 0.01, hidden == false).
// return super.point(inside: point, with: event)
// Poți suprascrie pentru forme personalizate sau margini, de exemplu, pentru a mări zona de atingere
let extendedBounds = bounds.insetBy(dx: -20, dy: -20) // Mărim zona cu 20pts în toate direcțiile
return extendedBounds.contains(point)
}
}
Metoda point(inside:with:) este apelată de hitTest(_:with:) pentru verificarea rapidă dacă punctul se află în interiorul zonei vizualizării, înainte de a verifica vizualizările copil.
În final, se găsește cea mai joasă vizualizare interactivă din ierarhie, a cărei geometrie conține punctul de atingere și care are userInteractionEnabled setat la true, hidden la false și alpha mai mare decât 0.01.