Взаимодействие за пределами UIView

Возможно ли, чтобы UIButton (или любой другой элемент управления) получал события касания, когда кадр UIButton находится за рамкой своего родителя? Причина, когда я пытаюсь это сделать, мой UIButton, похоже, не может получать какие-либо события. Как мне обойти это?

Да. Вы можете переопределить метод hitTest:withEvent: чтобы возвращать представление для большего набора точек, чем этот вид содержит. См. Ссылку на class UIView .

Изменить: Пример:

 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { CGFloat radius = 100.0; CGRect frame = CGRectMake(-radius, -radius, self.frame.size.width + radius, self.frame.size.height + radius); if (CGRectContainsPoint(frame, point)) { return self; } return nil; } 

Редактировать 2: (После уточнения 🙂 Чтобы гарантировать, что кнопка рассматривается как находящаяся в границах родителя, вам необходимо переопределить pointInside:withEvent: в родительском pointInside:withEvent: включить фрейм кнопки.

 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { if (CGRectContainsPoint(self.view.bounds, point) || CGRectContainsPoint(button.view.frame, point)) { return YES; } return NO; } 

Обратите внимание, что код для переопределения pointInside не совсем корректен. Как объясняет Summon ниже, сделайте следующее:

 -(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { if ( CGRectContainsPoint(self.oversizeButton.frame, point) ) return YES; return [super pointInside:point withEvent:event]; } 

Обратите внимание, что вы, скорее всего, сделаете это с помощью self.oversizeButton как IBOutlet в этом подclassе UIView; то вы можете просто перетащить кнопку «негабаритного типа», о которой идет речь, к специальному виду, о котором идет речь. (Или, если по какой-то причине вы делали это много в проекте, у вас был бы специальный подclass UIButton, и вы могли бы просмотреть свой список подпрограмм для этих classов.) Надеюсь, что это поможет.

@jnic, я работаю над iOS SDK 5.0 и для того, чтобы заставить ваш код работать правильно, мне пришлось это сделать:

 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { if (CGRectContainsPoint(button.frame, point)) { return YES; } return [super pointInside:point withEvent:event]; } 

Вид контейнера в моем случае – это UIButton, и все дочерние элементы также являются UIButtons, которые могут перемещаться за пределы родительского UIButton.

Лучший

В родительском представлении вы можете переопределить метод теста попадания:

 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { CGPoint translatedPoint = [_myButton convertPoint:point fromView:self]; if (CGRectContainsPoint(_myButton.bounds, translatedPoint)) { return [_myButton hitTest:translatedPoint withEvent:event]; } return [super hitTest:point withEvent:event]; } 

В этом случае, если точка попадает в пределы вашей кнопки, вы переадресовываете вызов; если нет, верните исходную реализацию.

Swift:

Где targetView – это представление, которое вы хотите получить, (которое может быть полностью или частично расположено вне frameworks его родительского представления).

 override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { let pointForTargetView: CGPoint = targetView.convertPoint(point, fromView: self) if CGRectContainsPoint(targetView.bounds, pointForTargetView) { return closeButton } return super.hitTest(point, withEvent: event) } 

В моем случае у меня был подclass UICollectionViewCell , содержащий UIButton . Я отключил clipsToBounds и кнопка была видна вне границ ячейки. Однако кнопка не получала события касания. Я смог обнаружить события касания на кнопке, используя ответ Джека: https://stackoverflow.com/a/30431157/3344977

Вот версия Swift:

 override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { let translatedPoint = button.convertPoint(point, fromView: self) if (CGRectContainsPoint(button.bounds, translatedPoint)) { print("Your button was pressed") return button.hitTest(translatedPoint, withEvent: event) } return super.hitTest(point, withEvent: event) } 

Почему это происходит?

Это связано с тем, что, когда ваше подчинение находится за пределами границ вашего супервизора, сенсорные события, которые на самом деле происходят в этом подвью, не будут доставлены в это подвью. Тем не менее, он ДОЛЖЕН быть доставлен в его супервизор.

Независимо от того, визуализируются ли подзоны визуально, сенсорные события всегда уважают прямоугольник границ супервизора целевого представления. Другими словами, сенсорные события, происходящие в части представления, которая находится за пределами прямоугольника границ супервизора, не доставляются в это представление. Ссылка

Что тебе необходимо сделать?

Когда ваш супервизор получит событие касания, упомянутое выше, вам нужно будет явно указать UIKit, что мой подсмотр должен быть тем, кто получит это событие касания.

Как насчет кода?

В вашем супервизоре реализуете func hitTest(_ point: CGPoint, with event: UIEvent?)

 override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { if isHidden || alpha == 0 || clipsToBounds { return nil } // convert the point into subview's coordinate system let subviewPoint = self.convert(point, to: subview) // if the converted point lies in subview's bound, tell UIKit that subview should be the one that receives this event if ! subview.isHidden && subview.bounds.contains(subviewPoint) { return subview } return nil } 

Для меня (как и для других) ответ заключался не в переопределении метода hitTest , а в методе pointInside . Я должен был сделать это только в двух местах.

Superview Это представление о том, что если бы у clipsToBounds установлено значение clipsToBounds true, все это исчезло бы. Итак, вот мой простой UIView в Swift 3:

 class PromiscuousView : UIView { override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { return true } } 

Subview Ваш пробег может отличаться, но в моем случае мне также пришлось перезаписать метод pointInside для подсмотра, который был определен, что прикосновение было вне пределов. Мой находится в Objective-C, поэтому:

 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { WEPopoverController *controller = (id) self.delegate; if (self.takeHitsOutside) { return YES; } else { return [super pointInside:point withEvent:event]; } } 

Этот ответ (и несколько других вопросов по тому же вопросу) может помочь вам разобраться в вашем понимании.

  • Как кодировать Base64 на iPhone
  • View-based NSTableView с строками с динамическими высотами
  • NSMutableArray - заставить массив удерживать только определенный тип объекта
  • Преобразование NSArray в NSDictionary
  • Есть ли способ удалить разделительную линию из UITableView?
  • iOS8 - ограничения двусмысленно указывают на высоту нуля
  • UIDatePicker, устанавливающий максимальные и минимальные даты на основе сегодняшней даты
  • objective-C - Когда использовать «я»,
  • Создайте случайную буквенно-цифровую строку в Cocoa
  • Невозможно добавить элементы в NSMutableArray ivar
  • Как передать объект с помощью NSNotificationCenter
  • Interesting Posts

    Как объединить два сетевых подключения в одной сети?

    $ lookup для ObjectId’s в массиве

    Как изменить диакритические символы на недиакритические

    Почему на клавиатуре есть два набора клавиш Ctrl, Alt и Shift?

    Как удалить обновления в Live Tile в Windows 8?

    Элемент HTML5 на Android

    .NET преобразует число в строковое представление (от 1 до одного, от 2 до два и т. Д.)

    Совместимость Linux с Samsung серии 7 Chronos

    Диалоговое окно с пустым полем, присвоенным переменной в терминале Mac?

    Как нарисовать точку (на mouseclick) на QGraphicsScene?

    доступ к защищенным членам суперclassа в C ++ с помощью шаблонов

    Как бы вы превратили планшет в клавиатуру + простой в использовании сенсорный экран (зеркало на рабочем столе) (а не тачпад) для настольного ПК?

    Windows не может установить требуемые файлы. Файл может быть поврежден или отсутствует. (0x80070570)

    Сгенерировать фиктивную переменную

    “Общая ошибка Не удается открыть раздел реестра Временный (изменчивый) …” из Access ODBC

    Давайте будем гением компьютера.