Как я могу нажать кнопку за прозрачным UIView? - PullRequest
168 голосов
/ 15 июня 2010

Допустим, у нас есть контроллер представления с одним вложенным представлением.подпредставление занимает центр экрана с полями 100 пикселей со всех сторон.Затем мы добавляем кучу мелочей, чтобы щелкнуть внутри этого подпредставления.Мы используем подпредставление только для того, чтобы воспользоваться новым кадром (x = 0, y = 0 внутри подпредставления на самом деле 100,100 в родительском представлении).

Тогда представьте, что у нас есть что-то позади подпредставления, например, меню.Я хочу, чтобы пользователь мог выбрать любой «маленький материал» в подпредставлении, но если там ничего нет, я хочу, чтобы касания проходили через него (поскольку фон в любом случае ясен) к кнопкам позади него.

Как я могу это сделать?Похоже, прикосновения Беган проходит, но кнопки не работают.

Ответы [ 16 ]

298 голосов
/ 25 октября 2010

Создайте пользовательское представление для вашего контейнера и переопределите сообщение pointInside:: возвращать false, если точка не находится в допустимом дочернем представлении, например:

Swift:

class PassThroughView: UIView {
    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        for subview in subviews {
            if !subview.isHidden && subview.isUserInteractionEnabled && subview.point(inside: convert(point, to: subview), with: event) {
                return true
            }
        }
        return false
    }
}

Цель C:

@interface PassthroughView : UIView
@end

@implementation PassthroughView
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    for (UIView *view in self.subviews) {
        if (!view.hidden && view.userInteractionEnabled && [view pointInside:[self convertPoint:point toView:view] withEvent:event])
            return YES;
    }
    return NO;
}
@end

Использование этого представления в качестве контейнера позволит любому из его дочерних элементов получать прикосновения, но само представление будет прозрачным для событий.

95 голосов
/ 02 сентября 2011

Я также использую

myView.userInteractionEnabled = NO;

Нет необходимости создавать подклассы. Работает нормально.

18 голосов
/ 28 октября 2011

От Apple:

Переадресация событий - это метод, используемый некоторыми приложениями.Вы пересылаете события касания, вызывая методы обработки событий другого объекта респондента.Хотя это может быть эффективным методом, вы должны использовать его с осторожностью.Классы инфраструктуры UIKit не предназначены для получения касаний, которые не связаны с ними .... Если вы хотите условно пересылать касания другим респондентам в вашем приложении, все эти респонденты должны быть экземплярами ваших собственных подклассов UIView.

Лучшая практика Apple :

Не отправлять события явно по цепочке респондента (через nextResponder);вместо этого вызовите реализацию суперкласса и позвольте UIKit обрабатывать обход цепочки респондента.

Вместо этого вы можете переопределить:

-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event

в своем подклассе UIView и вернуть NO, если хотите, чтобыкоснитесь, чтобы отправить цепочку респондента (т. е. для просмотра за вашим представлением, в котором ничего нет).

7 голосов
/ 31 января 2016

Гораздо более простой способ - «Отключить» взаимодействие с пользователем, включенное в конструкторе интерфейсов. «Если вы используете раскадровку»

enter image description here

6 голосов
/ 01 апреля 2015

В последнее время я написал класс, который поможет мне именно с этим. Использование его в качестве пользовательского класса для UIButton или UIView передаст события касания, которые были выполнены на прозрачном пикселе.

Это решение несколько лучше, чем принятый ответ, потому что вы все равно можете нажать UIButton, который находится под полупрозрачным UIView, в то время как непрозрачная часть UIView все равно будет реагировать на сенсорные события. *

GIF

Как вы можете видеть в GIF, кнопка Giraffe представляет собой простой прямоугольник, но события касания на прозрачных областях передаются на желтый UIButton внизу.

Ссылка на класс

6 голосов
/ 02 февраля 2012

Основываясь на том, что опубликовал Джон, вот пример, который позволит сенсорным событиям проходить через все подпредставления представления, кроме кнопок:

-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    // Allow buttons to receive press events.  All other views will get ignored
    for( id foundView in self.subviews )
    {
        if( [foundView isKindOfClass:[UIButton class]] )
        {
            UIButton *foundButton = foundView;

            if( foundButton.isEnabled  &&  !foundButton.hidden &&  [foundButton pointInside:[self convertPoint:point toView:foundButton] withEvent:event] )
                return YES;
        }        
    }
    return NO;
}
4 голосов
/ 12 февраля 2018

Решение, получившее наибольшее количество голосов, не работало для меня полностью, я думаю, это потому, что у меня был TabBarController в иерархии (как указывает один из комментариев), он фактически передавал касания некоторым частям пользовательского интерфейса, но это было вмешиваться в способность моего tableView перехватывать события касания, что, в конце концов, переопределило hitTest в представлении, которое я хочу игнорировать касания, и позволить подпредставлениям этого представления обрабатывать их

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    UIView *view = [super hitTest:point withEvent:event];
    if (view == self) {
        return nil; //avoid delivering touch events to the container view (self)
    }
    else{
        return view; //the subviews will still receive touch events
    }
}
3 голосов
/ 16 января 2018

Swift 3

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    for subview in subviews {
        if subview.frame.contains(point) {
            return true
        }
    }
    return false
}
2 голосов
/ 20 июня 2012

Я создал категорию для этого.

маленький метод извергает и вид золотой.

Заголовок

//UIView+PassthroughParent.h
@interface UIView (PassthroughParent)

- (BOOL) passthroughParent;
- (void) setPassthroughParent:(BOOL) passthroughParent;

@end

Файл реализации

#import "UIView+PassthroughParent.h"

@implementation UIView (PassthroughParent)

+ (void)load{
    Swizz([UIView class], @selector(pointInside:withEvent:), @selector(passthroughPointInside:withEvent:));
}

- (BOOL)passthroughParent{
    NSNumber *passthrough = [self propertyValueForKey:@"passthroughParent"];
    if (passthrough) return passthrough.boolValue;
    return NO;
}
- (void)setPassthroughParent:(BOOL)passthroughParent{
    [self setPropertyValue:[NSNumber numberWithBool:passthroughParent] forKey:@"passthroughParent"];
}

- (BOOL)passthroughPointInside:(CGPoint)point withEvent:(UIEvent *)event{
    // Allow buttons to receive press events.  All other views will get ignored
    if (self.passthroughParent){
        if (self.alpha != 0 && !self.isHidden){
            for( id foundView in self.subviews )
            {
                if ([foundView alpha] != 0 && ![foundView isHidden] && [foundView pointInside:[self convertPoint:point toView:foundView] withEvent:event])
                    return YES;
            }
        }
        return NO;
    }
    else {
        return [self passthroughPointInside:point withEvent:event];// Swizzled
    }
}

@end

Вам нужно будет добавить мои Swizz.h и Swizz.m

находится Здесь

После этого вы просто импортируете UIView + PassthroughParent.h в свой файл {Project} -Prefix.pch, и у каждого представления будет эта возможность.

каждый вид будет принимать точки, но ни один из пробелов не будет.

Я также рекомендую использовать чистый фон.

myView.passthroughParent = YES;
myView.backgroundColor = [UIColor clearColor];

РЕДАКТИРОВАТЬ

Я создал свой собственный пакет свойств, который ранее не был включен.

Заголовочный файл

// NSObject+PropertyBag.h

#import <Foundation/Foundation.h>

@interface NSObject (PropertyBag)

- (id) propertyValueForKey:(NSString*) key;
- (void) setPropertyValue:(id) value forKey:(NSString*) key;

@end

Файл реализации

// NSObject+PropertyBag.m

#import "NSObject+PropertyBag.h"



@implementation NSObject (PropertyBag)

+ (void) load{
    [self loadPropertyBag];
}

+ (void) loadPropertyBag{
    @autoreleasepool {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            Swizz([NSObject class], NSSelectorFromString(@"dealloc"), @selector(propertyBagDealloc));
        });
    }
}

__strong NSMutableDictionary *_propertyBagHolder; // Properties for every class will go in this property bag
- (id) propertyValueForKey:(NSString*) key{
    return [[self propertyBag] valueForKey:key];
}
- (void) setPropertyValue:(id) value forKey:(NSString*) key{
    [[self propertyBag] setValue:value forKey:key];
}
- (NSMutableDictionary*) propertyBag{
    if (_propertyBagHolder == nil) _propertyBagHolder = [[NSMutableDictionary alloc] initWithCapacity:100];
    NSMutableDictionary *propBag = [_propertyBagHolder valueForKey:[[NSString alloc] initWithFormat:@"%p",self]];
    if (propBag == nil){
        propBag = [NSMutableDictionary dictionary];
        [self setPropertyBag:propBag];
    }
    return propBag;
}
- (void) setPropertyBag:(NSDictionary*) propertyBag{
    if (_propertyBagHolder == nil) _propertyBagHolder = [[NSMutableDictionary alloc] initWithCapacity:100];
    [_propertyBagHolder setValue:propertyBag forKey:[[NSString alloc] initWithFormat:@"%p",self]];
}

- (void)propertyBagDealloc{
    [self setPropertyBag:nil];
    [self propertyBagDealloc];//Swizzled
}

@end
2 голосов
/ 16 июня 2010

Согласно «Руководству по программированию приложений iPhone»:

Отключение доставки событий касания.По умолчанию представление получает сенсорные события, но вы можете установить его свойство userInteractionEnabled в NO, чтобы отключить доставку событий. Представление также не получает события, если оно скрыто или прозрачно .

http://developer.apple.com/iphone/library/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/EventHandling/EventHandling.html

Обновлено : Удаленный пример -перечитайте вопрос ...

Есть ли у вас какие-либо жесты обработки представлений, которые могут обрабатывать касания до того, как кнопка их получит?Работает ли кнопка, когда у вас нет прозрачного вида над ней?

Какие-нибудь примеры кода нерабочего кода?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...