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



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

737   8  

8 ответов:

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

Edit: пример:

- (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;
}

Edit 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 не совсем корректно. Как Повестка объясняет ниже, сделайте это:

-(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; затем вы можете просто перетащить "кнопку oversize", о которой идет речь, в специальный вид, о котором идет речь. (Или, если по какой-то причине вы делали это много в проекте, у вас был бы специальный подкласс UIButton, и вы могли бы просмотреть ваш список подвидов для этих классов.) Надеюсь, это поможет.

@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-это представление, которое вы хотите получить событие (которое может быть полностью или частично расположено за пределами кадра его родительского представления).

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)
}

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

вот быстрая версия:

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 метод. Я должен был сделать это только в двух местах.

В Суперпанель Это мнение, что если бы clipsToBounds установите значение true, это заставит всю эту проблему исчезнуть. Так вот мой простой UIView в Swift 3:

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

Окне Ваш пробег может отличаться, но в моем случае мне также пришлось перезаписать 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];
    }
}

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

swift 4.1 обновлено

чтобы получить сенсорные события в том же UIView вам просто нужно добавить следующий метод переопределения, он определит конкретное представление, нажатое в UIView, которое помещается вне bound

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)
}

Comments

    Ничего не найдено.