Возможно ли для UIButton (или любого другого элемента управления в этом отношении) получать события касания, когда кадр UIButton находится вне его родительского кадра? Потому что, когда я пытаюсь это сделать, мой UIButton, похоже, не может получать никаких событий. Как мне обойти это?
94
Ответы:
Да. Вы можете переопределить
hitTest:withEvent:
метод, чтобы вернуть представление для большего набора точек, чем содержит это представление. См. Справку по классам 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:
в родительском элементе, чтобы включить рамку кнопки.- (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 в этом подклассе UIView; затем вы можете просто перетащить нужную «кнопку увеличения размера» на соответствующий вид. (Или, если по какой-то причине вы часто делали это в проекте, у вас был бы специальный подкласс UIButton, и вы могли бы просмотреть свой список подпредставлений для этих классов.) Надеюсь, это поможет.источник
pointInside:withEvent:
чтобы включить рамку кнопки. Я добавлю пример кода к своему ответу выше.hitTest:withEvent:
иpointInside:withEvent:
область, которая не вызывается для меня, когда я нажимаю кнопку, которая находится за пределами родительских границ. Что может быть не так?@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, и все дочерние элементы также являются UIButton, которые могут перемещаться за пределы родительского 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]; }
В этом случае, если точка попадает в пределы вашей кнопки, вы переадресовываете вызов туда; если нет, вернитесь к исходной реализации.
источник
В моем случае у меня был
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) }
Swift 4:
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { let translatedPoint = button.convert(point, from: self) if (button.bounds.contains(translatedPoint)) { print("Your button was pressed") return button.hitTest(translatedPoint, with: event) } return super.hitTest(point, with: event) }
источник
Почему это происходит?
Это связано с тем, что, когда ваше подчиненное представление находится за пределами границ вашего супервизора, события касания, которые на самом деле происходят в этом подчиненном представлении, не будут доставлены в это подчиненное представление. Тем не менее, он БУДЕТ передан его супервизору.
Что тебе необходимо сделать?
Когда ваш супервизор получит упомянутое выше событие касания, вам нужно будет явно указать UIKit, что мое подчиненное представление должно получать это событие касания.
А как насчет кода?
В вашем супервизоре реализовать
func hitTest(_ point: CGPoint, with event: UIEvent?)
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { if isHidden || alpha == 0 || clipsToBounds { return super.hitTest(point, with: event) } // 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 super.hitTest(point, with: event) }
Захватывающий готчя: вы должны перейти в "высший слишком-маленький супервизор"
Вы должны подняться «вверх» к «самому высокому» обзору, за пределами которого находится вид проблемы.
Типичный пример:
Скажем, у вас есть экран S с представлением контейнера C. Вид контроллера представления контейнера - V. (Напомним, что V будет находиться внутри C и иметь такой же размер.) V имеет подпредставление (возможно, кнопку) B. B - проблема вид, который фактически находится за пределами В.
Но обратите внимание, что B также находится за пределами C.
В этом примере вы должны применить решение
override hitTest
на самом деле С, не к V . Если применить его к V - ничего не произойдет.источник
Swift:
Где targetView - это представление, которое вы хотите получить (которое может быть полностью или частично расположено за пределами кадра своего родительского представления).
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) }
источник
Для меня (как и для других) ответ заключался не в том, чтобы переопределить
hitTest
метод, а скорее вpointInside
методе. Мне пришлось сделать это всего в двух местах.Супервизор. Это представление, которое, если бы оно было
clipsToBounds
установлено в истинное значение, устраняло бы всю эту проблему. Итак, вот мой простойUIView
пример в Swift 3:class PromiscuousView : UIView { override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { return true } }
Subview Ваш опыт может отличаться, но в моем случае мне также пришлось перезаписать
pointInside
метод для subview, который решил, что прикосновение было вне пределов. Мой находится в 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]; } }
Этот ответ (и несколько других ответов на тот же вопрос) может помочь вам прояснить ваше понимание.
источник
swift 4.1 обновлен
Чтобы получать события касания в том же UIView, вам просто нужно добавить следующий метод переопределения, он будет идентифицировать конкретный вид, задействованный в UIView, который помещается за пределы
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { let pointforTargetview:CGPoint = self.convert(point, to: btnAngle) if btnAngle.bounds.contains(pointforTargetview) { return self } return super.hitTest(point, with: event) }
источник