Wie bestimmt das System, in welcher Ansicht die Berührung erfolgt ist?
sobes.tech KI
Antwort von AI
Das System verwendet zwei Schlüsselmechanismen: Hit-Testing (Trefferprüfung) und die Hierarchie der Ansichten.
-
Hit-Testing: Das UIKit-Framework verwendet die Methode
hitTest(_:with:)der Wurzelansicht (UIWindow), um zu bestimmen, welche Ansicht unter dem Berührungspunkt liegt. Diese Methode durchläuft rekursiv die Ansichtshierarchie, beginnend bei der Wurzel. -
Ansichtshierarchie:
- Die Methode
hitTest(_:with:)prüft zunächst, ob der Berührungspunkt innerhalb der Grenzen der aktuellen Ansicht liegt. Wenn nicht, gibt sienilzurück. - Ist der Punkt innerhalb der Grenzen, überprüft die Methode die Kindansichten in umgekehrter Reihenfolge (von der zuletzt hinzugefügten bis zur ersten). Für jede Kindansicht wird ihre eigene Methode
hitTest(_:with:)mit demselben Punkt (in die Koordinaten der Kindansicht umgewandelt) aufgerufen. - Die erste Kindansicht, deren
hitTest(_:with:)-Methode ein Nicht-nil-Ergebnis zurückgibt (d.h., sie oder eine ihrer Kindansichten enthält den Punkt), gilt als "getroffen" und dieses Ergebnis wird nach oben in der Hierarchie zurückgegeben. - Wenn keine Kindansicht den Punkt enthält und die aktuelle Ansicht den Punkt enthält und
userInteractionEnablednicht auffalsegesetzt ist, wirdselfzurückgegeben. - Wenn
userInteractionEnabledder aktuellen Ansicht auffalsegesetzt ist, oderhiddenauftrue, oderalphakleiner als 0.01 ist, gibt die Methodenilzurück, auch wenn der Punkt innerhalb der Grenzen liegt.
- Die Methode
Der Vorgang setzt sich nach unten in der Hierarchie fort, bis die unterste Ansicht gefunden wird, die den Berührungspunkt enthält. Diese Ansicht wird zur hit-test view oder hit-test result und erhält die weitere Ereignisverwaltung (Methoden touchesBegan, touchesMoved usw.).
Hier ist, wie man hitTest oder point(inside:with:) für benutzerdefiniertes Verhalten überschreibt:
import UIKit
class CustomView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// Prüfen, ob der Punkt innerhalb dieser Ansicht liegt.
if !self.point(inside: point, with: event) {
return nil
}
// Durchlaufen der Kindansichten in umgekehrter Reihenfolge.
for subview in subviews.reversed() {
// Punkt in die Koordinaten der Kindansicht umwandeln.
let convertedPoint = subview.convert(point, from: self)
// Rekursiv hitTest auf die Kindansicht aufrufen.
if let hitTestView = subview.hitTest(convertedPoint, with: event) {
// Wenn die Kindansicht oder ihre Nachkommen den Punkt enthalten, Rückgabe dieses Ergebnisses.
return hitTestView
}
}
// Wenn keine Kindansicht den Kontakt verarbeitet hat,
// und diese Ansicht interaktiv ist und den Punkt enthält, Rückgabe self.
return self
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
// Die Standardimplementierung prüft, ob der Punkt innerhalb der Grenzen liegt und ob die Ansicht interaktiv ist.
// return super.point(inside: point, with: event)
// Für benutzerdefinierte Formen oder Ränder kann man es überschreiben, z.B. die Berührungsfläche vergrößern.
let extendedBounds = bounds.insetBy(dx: -20, dy: -20) // Bereich um 20 Punkte vergrößern
return extendedBounds.contains(point)
}
}
Die Methode point(inside:with:) wird von hitTest(_:with:) aufgerufen, um schnell zu prüfen, ob der Punkt überhaupt innerhalb des Bereichs der Ansicht liegt, bevor die Kindansichten geprüft werden.
Letztendlich wird die unterste Ansicht in der Hierarchie gefunden, die den Berührungspunkt enthält und userInteractionEnabled auf true gesetzt hat, hidden auf false und alpha größer als 0.01 ist.