Как отключить сенсорный ввод для всех видов, кроме самого верхнего вида?
У меня есть представление с несколькими подпредставлениями. Когда пользователь нажимает на подпредставление, подпредставление увеличивается в размере, чтобы покрыть большую часть экрана, но некоторые другие подпредставления все еще видны внизу.
Я хочу, чтобы мое приложение игнорировало прикосновения к другим подпредставлениям, когда одно из подпредставлений "раскрывается" таким образом. Есть ли простой способ добиться этого? Я могу написать код, чтобы справиться с этим, но я надеялся, что есть более простой встроенный способ.
12 ответов
Надеюсь, это поможет...
[[yourSuperView subviews]
makeObjectsPerformSelector:@selector(setUserInteractionEnabled:)
withObject:[NSNumber numberWithBool:FALSE]];
который отключит userInteraction непосредственных подпредставлений представления. Затем передайте userInteraction единственному представлению, которое вы хотели
yourTouchableView.setUserInteraction = TRUE;
РЕДАКТИРОВАТЬ:
Кажется, в iOS отключение userInteraction в родительском представлении не отключает userInteraction для его дочерних элементов. Так что код выше (я имею в виду тот, который с makeObjectsPerformSelector:
)будет работать только для отключения userInteraction непосредственных подпредставлений родителей.
Посмотрите ответ пользователя madewulf, который рекурсивно получает все подпредставления и отключает взаимодействие с пользователем всех из них. Или, если вам нужно отключить userInteraction этого представления во многих местах проекта, вы можете классифицировать UIView, чтобы добавить эту функцию. Что-то вроде этого подойдет..
@interface UIView (UserInteractionFeatures)
-(void)setRecursiveUserInteraction:(BOOL)value;
@end
@implementation UIView(UserInteractionFeatures)
-(void)setRecursiveUserInteraction:(BOOL)value{
self.userInteractionEnabled = value;
for (UIView *view in [self subviews]) {
[view setRecursiveUserInteraction:value];
}
}
@end
Теперь вы можете позвонить
[yourSuperView setRecursiveUserInteraction:NO];
Кроме того, предложение пользователя @lxt о добавлении невидимого представления поверх всех представлений является еще одним способом сделать это..
Есть несколько способов сделать это. Вы можете перебрать все ваши другие подпредставления и установить userInteractionEnabled = NO
, но это не идеально, если у вас есть много других представлений (в конце концов, вам придется впоследствии их все арендовать).
Для этого я создаю невидимый UIView размером с весь экран, который "блокирует" все прикосновения от перехода к другим представлениям. Иногда это буквально невидимо, иногда я могу установить его на черный с альфа-значением 0,3 или около того.
Когда вы расширяете свое основное подпредставление, чтобы заполнить экран, вы можете добавить этот блокирующий UIView позади него (используя insertSubview: belowSubview:
). Когда вы минимизируете свое расширенное подпредставление, вы можете удалить невидимый UIView из вашей иерархии.
Так что не совсем встроенный, но я думаю, что самый простой подход. Не уверен, что об этом вы уже думали, надеюсь, это помогло.
Остерегайтесь кода, приведенного здесь Кришнабхадрой в качестве решения:
[[yourSuperView subviews]makeObjectsPerformSelector:@selector(setUserInteractionEnabled:) withObject:[NSNumber numberWithBool:FALSE]];
Это не будет работать во всех случаях, потому что [yourSuperView subviews] дает только прямые подвиды superview. Чтобы это сработало, вам придется рекурсивно повторять все подпредставления:
-(void) disableRecursivelyAllSubviews:(UIView *) theView
{
theView.userInteractionEnabled = NO;
for(UIView* subview in [theView subviews])
{
[self disableRecursivelyAllSubviews:subview];
}
}
-(void) disableAllSubviewsOf:(UIView *) theView
{
for(UIView* subview in [theView subviews])
{
[self disableRecursivelyAllSubviews:subview];
}
}
Теперь вызов disableAllSubviewsOf
будет делать то, что вы хотели сделать.
Если у вас глубокий стек представлений, решение lxt, вероятно, лучше.
Я бы сделал это, поместив пользовательскую прозрачную кнопку с той же рамкой, что и superView
, И тогда поверх этой кнопки я бы поставил вид, который должен принимать прикосновения пользователя. Кнопка будет глотать все прикосновения, и виды за ней не получат никаких событий касания, но вид сверху кнопки будет получать прикосновения нормально.
Что-то вроде этого:
- (void)disableTouchesOnView:(UIView *)view {
UIButton *ghostButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, view.frame.size.width, view.frame.size.height)];
[ghostButton setBackgroundColor:[UIColor clearColor]];
ghostButton.tag = 42; // Any random number. Use #define to avoid putting numbers in code.
[view addSubview:ghostButton];
}
И метод для включения parentView
,
- (void)enableTouchesOnView:(UIView *)view {
[[view viewWithTag:42] removeFromSuperview];
}
Итак, чтобы отключить все виды в parentViev
позади yourView
Я бы сделал это:
YourView *yourView = [[YourView alloc] initWithCustomInitializer];
// It is important to disable touches on the parent view before adding the top most view.
[self disableTouchesOnView:parentView];
[parentView addSubview:yourView];
Добавьте TapGestureRecognizer в свой "фоновый вид" (полупрозрачный, который "затеняет" ваш обычный интерфейс) и установите для него "Отмена касаний в представлении", без добавления действия.
let captureTaps = UITapGestureRecognizer()
captureTaps.cancelsTouchesInView = true
dimmedOverlay?.addGestureRecognizer(captureTaps)
Просто parentView.UserInteractionEnabled = NO
сделаю работу. Родительский вид отключит взаимодействие с пользователем для всех подпредставлений представления. Но включить его не позволяет включить все подпредставления (по умолчанию UIImageView не взаимодействует). Таким образом, простой способ - найти родительское представление и использовать приведенный выше код, и нет необходимости повторять все подпредставления для выполнения селектора.
Я дам свои 2 цента на эту проблему. Итеративный запуск userInteractionEnabled = false - это один из способов. Другой способ будет добавить UIView, как показано ниже.
EZEventEater.h
#import <UIKit/UIKit.h>
@interface EZEventEater : UIView
@end
EZEventEater.m
#import "EZEventEater.h"
@implementation EZEventEater
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.backgroundColor = [UIColor clearColor];
self.userInteractionEnabled = false;
}
return self;
}
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//EZDEBUG(@"eater touched");
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
}
В своем коде вы добавляете представление EZEventEater, чтобы охватить все представления, которые могут блокировать событие касания. Всякий раз, когда вы хотите заблокировать сенсорное событие для этих представлений, просто позвоните
eater.userInteractionEnabled = YES;
Надеюсь, что это полезно.
В Swift 5 я добился такого поведения, разместив представление справа вверху (выделенное) и установив:
myView.isUserInteractionEnabled = true
Это не позволяет прикосновениям проходить через него, таким образом игнорируя удары.
Простое решение. Добавьте фиктивный жест, который ничего не делает. Сделайте его многоразовым, добавив его в расширение, например:
extension UIView {
func addNullGesture() {
let gesture = UITapGestureRecognizer(target: self,
action: #selector(nullGesture))
addGestureRecognizer(gesture)
}
@objc private func nullGesture() {}
}
Для моего приложения, я думаю, будет достаточно отключить навигацию к другим вкладкам приложения (на ограниченный период времени, пока я выполняю некоторую обработку):
self.tabBarController.view.userInteractionEnabled = NO;
Кроме того, я отключил текущий вид контроллера
self.view.userInteractionEnabled = NO;
(И, кстати, предложенные здесь рекурсивные решения имели странные эффекты в моем приложении. Кажется, что отключение работает нормально, но повторное включение имеет странные эффекты - часть пользовательского интерфейса не была отключена).
setUserInteractionEnabled = NO
на вид, который вы хотите отключить
У меня была такая же проблема, но вышеуказанные решения не помогли. Затем я заметил, что зовет super.touchesBegan(...)
была проблема. После удаления этого события обрабатывался только самый верхний вид.
Надеюсь, это кому-нибудь поможет.