Как я могу нажать кнопку за прозрачным UIView?

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

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

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

Создайте собственное представление для своего контейнера и переопределите сообщение 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 } } 

objective 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 

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

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

 myView.userInteractionEnabled = NO; 

Нет необходимости в подclassе. Работает отлично.

От Apple:

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

Лучшая практика яблок :

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

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

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

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

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

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

GIF

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

Ссылка на class

Более простым способом является «Отменить проверку» взаимодействия с пользователем в конструкторе интерфейса. «если вы используете раскадровку»,

введите описание изображения здесь

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

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

Я создал категорию, чтобы сделать это.

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

Заголовок

 //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  @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 

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

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

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

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

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

Любые примеры кода нерабочего кода?

Принимая советы от других ответов и прочитав документацию Apple, я создал эту простую библиотеку для решения вашей проблемы:
https://github.com/natrosoft/NATouchThroughView
Это облегчает рисование представлений в Interface Builder, которые должны передавать штрихи в базовое представление.

Я думаю, что метод swizzling является чрезмерным и очень опасным для производственного кода, потому что вы непосредственно возитесь с базовой реализацией Apple и делаете изменения в приложении, что может привести к непредвиденным последствиям.

Существует демонстрационный проект, и, надеюсь, README хорошо справляется, объясняя, что делать. Чтобы обратиться к OP, вы измените прозрачный UIView, содержащий кнопки в classе NATouchThroughView в Interface Builder. Затем найдите ясный UIView, который накладывает меню, которое вы хотите использовать. Измените этот UIView на class NARootTouchThroughView в Interface Builder. Это может быть даже корневой UIView вашего controllerа вида, если вы намерены передать эти штрихи в основной controller представления. Просмотрите демонстрационный проект, чтобы узнать, как он работает. Это действительно очень просто, безопасно и неинвазивно

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 } 

Насколько я знаю, вы должны сделать это, переопределив метод hitTest:. Я попробовал, но не смог заставить его работать правильно.

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

Если вы не можете использовать категорию UIView категории или подclassа, вы также можете просто нажать кнопку вперед, чтобы она была перед прозрачным представлением. Это не всегда возможно в зависимости от вашего приложения, но это сработало для меня. Вы всегда можете вернуть кнопку обратно или скрыть ее.

Топ-голосованное решение не полностью работало для меня, я думаю, это было из-за того, что в иерархию был включен 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 } } 
  • Какова стоимость использования автореферата в cocoa?
  • Регулировка громкости проигрывателя AVPlayer
  • Разбор даты JSON на IPhone
  • Отправить уведомление в центр уведомлений о горном льве
  • Запустите NSRunLoop в командной строке программы Cocoa
  • iOS 5 не позволяет сохранять загруженные данные в папке «Документы»?
  • viewDidLoad в NSViewController?
  • Как загрузить локальный html-файл в UIWebView
  • Как вы можете читать файлы MIME-типа в объективе-c
  • всплывающее меню iPhone, как iPad popover?
  • Тип аксессуара UITableViewCell, проверенный на доступ и установка других непроверенных
  • Давайте будем гением компьютера.