Как отладить, кто ест мои прикосновения в UIKit?
Я не могу понять, почему не работает нажатие на текстовые поля и кнопки в моем представлении. Я проверил все очевидные вещи, например, установлено ли для userInteractionEnabled значение YES, установлен ли распознаватель жестов и есть ли невидимое представление на переднем плане.
Есть ли лучшая практика в iOS для отслеживания прикосновения от того, когда оно впервые появляется, где оно потребляется?
ОБНОВИТЬ:
Оба ответа были полезны. В ходе моего расследования я узнал, что если подпредставление находится за пределами родительских границ, даже если родитель не ограничивает подпредставления, подпредставление не будет получать события. Я распечатал цепочку суперпредставлений из текстового поля, в которой не было прикосновений, и увидел, что один из этих видов имел высоту 0. Я наложил некоторые ограничения, чтобы растянуть его, и моя проблема была решена.
3 ответа
Вы можете подкласс UIWindow
и переопределить -[UIWindow sendEvent]:
тогда, когда это называется, используйте -[UIWindow hitTest:withEvent:]
чтобы проверить, какой вид получит событие.
Вы можете позвонить -[UIView recursiveDescription]
распечатка некоторой отладочной информации поможет вам понять, почему это представление получило событие касания.
Не забудьте позвонить [super sendEvent:]
когда вы сделали.
Для тех, кто использует раскадровку и хочет знать, как мы можем изменить класс основного окна приложения:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow? = TestWindow()
// ....
}
Просто укажите значение по умолчанию для AppDelegate
"s window
вар. Когда приложение запускается UIApplicationMain
создаст экземпляр делегата и попросит его window
, Если window
является nil
он создаст новый автоматически. Но если мы предоставим здесь значение по умолчанию, оно будет использоваться во всем приложении.
Вы можете отладить это, используя символическую точку останова:
-[UIWindow sendEvent:]
& po $arg3
<UITouchesEvent: 0x6000026fa6d0> timestamp: 179462 touches: {(
<UITouch: 0x7f84d6f10380> phase: Began tap count: 1 force: 0.000
window: <UIWindow: 0x7f84d6d0ad10; frame = (0 0; 375 812); autoresize = W+H;
gestureRecognizers = <NSArray: 0x600001aa8870>; layer = <UIWindowLayer: 0x6000014bd7e0>> view: <UIView: 0x7f84d6d0bff0; frame = (0 0; 375 812);
autoresize = W+H; layer = <CALayer: 0x6000014bdc60>> location in window: {165.66665649414062, 232.33332824707031} previous location in window:
{165.66665649414062, 232.33332824707031} location in view: {165.66665649414062,
232.33332824707031} previous location in view: {165.66665649414062,232.33332824707031}
)}
Не прямой ответ на вопрос, но очень распространенная причина исчезновения касаний заключается в том, что элемент управления находится в пользовательском просмотре, который имеет меньший размер, чем этот элемент управления, но не ограничивает его границы, поэтому вы не увидите, что родительский элемент вид меньше (или даже, возможно, нулевого размера).
Parent UIView size=0x0, clipToBounds=false
Child UIButton size=100x50
=> Детская кнопка не получит никаких сенсорных событий.
Вы можете использовать новую отладку интерактивного просмотра Xcode6, и, повернув иерархию видов в 3D, вы сможете увидеть, какие виды находятся выше тех, которые вам нужны, и осмотреть их.
Мой полный Swift Пример, следующий за @Bryan Chen и @kelin Ответ...
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow? = TestWindow()
// ....
}
...
class TestWindow:UIWindow {
override func sendEvent(_ event: UIEvent) {
print(event)
//<UITouchesEvent: 0x6000008b1f80> timestamp: 366545 touches: {(
// <UITouch: 0x7fa056d37dc0> phase: Ended tap count: 1 force: 0.000
//window: <zollundpost.TestWindow: 0x7fa056c020a0; baseClass = UIWindow;
//frame = (0 0; 375 812); gestureRecognizers = <NSArray: 0x6000034f9530>;
//layer = <UIWindowLayer: 0x600003abd940>> view: <UITableViewCellContentView:
//0x7fa059a55090; frame = (0 0; 375 72); opaque = NO; gestureRecognizers =
//<NSArray: 0x6000034293e0>; layer = <CALayer: 0x600003ac8320>> location in
//window: {165, 358.33332824707031} previous location in window:
//{144.33332824707031, 358.66665649414062} location in view: {165,
//44.999992370605469} previous location in view: {144.33332824707031,
//45.333320617675781}
// )}
super.sendEvent(event)
}
}
Чтобы получить более подробную информацию о том, какие типы распознавателей жестов являются источником проблемы из ответа @Peter, я распечатал массив gestureRecognizer:
class TestWindow:UIWindow {
override func sendEvent(_ event: UIEvent) {
event.allTouches?.forEach() { touch in
print("touch")
print(touch.gestureRecognizers ?? "")
/// [<UILongPressGestureRecognizer: 0x7fb205964ab0; state = Possible; view = <UIView 0x7fb205964940>; target= <(action=_handleMenuGesture: ....
}
super.sendEvent(event)
}
}
Основной способ увидеть, что потребляет события касания, - просто добавить представление отладки и переопределить hitTest(:event:)
метод. Присоедините этот класс к любому представлению в раскадровке.
class DebugView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let view = super.hitTest(point, with: event)
print("View")
print(view)
return view
}
}
Прикрепив это к корневому представлению контроллера, вы получите доступ к представлению, которое использует событие касания. Обратите внимание, что метод hitTest будет вызываться дважды при каждом событии касания.