Как системата определя в коя изглед е направено докосването?
sobes.tech AI
Отговор от AI
Системата използва два ключови механизма: hit-testing (тест за удар) и йерархията на изгледите.
-
Hit-testing (Тест за удар): Framework-ът UIKit използва метода
hitTest(_:with:)на кореновия изглед (UIWindow), за да определи кой изглед е под точката на докосване. Този метод рекурсивно преминава през йерархията на изгледите, започвайки от корена. -
Йерархия на изгледите:
- Методът
hitTest(_:with:)първо проверява дали точката на докосване е вътре в границите на текущия изглед. Ако не, връщаnil. - Ако е вътре, методът преглежда децата на изгледа в обратен ред (от последно добавеното към първото). За всяко дете се извиква неговият собствен
hitTest(_:with:)с същата точка на докосване (преобразувана към координатите на детето). - Първото дете, което връща резултат различен от
nil(т.е. съдържа точката), се счита за "засечено" и този резултат се връща нагоре по йерархията. - Ако никое дете не съдържа точката, а текущият изглед я съдържа и
userInteractionEnabledеtrue, тогава системата счита, че докосването е станало в текущия изглед, и методът връщаself. - Ако
userInteractionEnabledна текущия изглед еfalse, илиhiddenеtrue, илиalphaе по-малко от 0.01, методът връщаnil, дори ако точката е вътре в границите.
- Методът
Процесът продължава надолу по йерархията, докато не бъде намерен най-долният изглед, съдържащ точката на докосване. Този изглед става hit-test view или hit-test result, и именно на него (или на неговия най-близък предшественик) ще бъде делегирано управлението на събитието.
Ето как може да се препише hitTest или point(inside:with:) за персонализирано поведение:
import UIKit
class CustomView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// Проверяваме дали точката е вътре в нашия изглед.
if !self.point(inside: point, with: event) {
return nil
}
// Обхождаме децата в обратен ред.
for subview in subviews.reversed() {
let convertedPoint = subview.convert(point, from: self)
if let hitTestView = subview.hitTest(convertedPoint, with: event) {
return hitTestView
}
}
return self
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
// Стандартната проверка е дали точката е вътре в bounds и дали изгледът е активен.
let extendedBounds = bounds.insetBy(dx: -20, dy: -20) // Разширява границите с 20 точки
return extendedBounds.contains(point)
}
}