Hoe bepaalt het systeem op welke view de aanraking heeft plaatsgevonden?
sobes.tech AI
Antwoord van AI
Het systeem gebruikt twee belangrijke mechanismen: hit-testing en de hiërarchie van views.
-
Hit-testing: Het UIKit-framework gebruikt de methode
hitTest(_:with:)van de root-view van het venster (UIWindow) om te bepalen welke view onder het aanrakingpunt ligt. Deze methode doorloopt recursief de hiërarchie van views, beginnend bij de root. -
Hiërarchie van views:
- De methode
hitTest(_:with:)controleert eerst of het aanrakingpunt binnen de grenzen van de huidige view ligt. Zo niet, retourneert hetnil. - Als het punt binnen de grenzen ligt, bekijkt de methode de kind-views in omgekeerde volgorde (van laatst toegevoegde tot eerste). Voor elke kind-view wordt zijn eigen methode
hitTest(_:with:)aangeroepen met hetzelfde punt (omgezet naar de coördinaten van de kind-view). - De eerste kind-view waarvan
hitTest(_:with:)een niet-nilresultaat retourneert (d.w.z. zij of een van haar kind-views bevat het punt), wordt als "getroffen" beschouwd en dat resultaat wordt naar boven in de hiërarchie geretourneerd. - Als geen enkele kind-view het punt bevat en de huidige view het punt bevat en
userInteractionEnabledniet opfalsestaat, wordtselfgeretourneerd. - Als
userInteractionEnabledvan de huidige view opfalsestaat, ofhiddenoptrue, ofalphakleiner is dan 0.01, retourneert de methodenil, zelfs als het punt binnen de grenzen ligt.
- De methode
Het proces gaat door naar beneden in de hiërarchie totdat de laagste view die het aanrakingpunt bevat gevonden is. Deze view wordt de hit-test view of hit-test result, en aan haar (of haar dichtstbijzijnde geschikte voorouder) wordt het verdere beheer van het evenement gedelegeerd (touchesBegan, touchesMoved, etc.).
Hier is hoe je hitTest of point(inside:with:) kunt overschrijven voor aangepast gedrag:
import UIKit
class CustomView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// Controleer of het punt binnen deze view ligt.
if !self.point(inside: point, with: event) {
return nil
}
// Loop door de kind-views in omgekeerde volgorde.
for subview in subviews.reversed() {
// Converteer het punt naar de coördinaten van de kind-view.
let convertedPoint = subview.convert(point, from: self)
// Roep recursief hitTest aan op de kind-view.
if let hitTestView = subview.hitTest(convertedPoint, with: event) {
// Als de kind-view of haar descendanten het punt bevatten, retourneer dat resultaat.
return hitTestView
}
}
// Als geen enkele kind-view het punt heeft afgehandeld,
// en deze view interactief is en het punt bevat, retourneer self.
return self
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
// De basisimplementatie controleert of het punt binnen de bounds ligt en of de view interactief is.
// return super.point(inside: point, with: event)
// Kan overschreven worden voor aangepaste vormen of marges, bijvoorbeeld het vergroten van het aanrakinggebied.
let extendedBounds = bounds.insetBy(dx: -20, dy: -20) // Verhoog het gebied met 20pts rondom
return extendedBounds.contains(point)
}
}
De methode point(inside:with:) wordt aangeroepen vanuit hitTest(_:with:) om snel te controleren of het punt überhaupt binnen het gebied van de view ligt, voordat de kind-views gecontroleerd worden.
Uiteindelijk wordt de laagste view in de hiërarchie gevonden die het aanrakingpunt bevat en userInteractionEnabled op true heeft, hidden op false en alpha groter dan 0.01, en wordt deze als hit-test view beschouwd.