В этой статье пойдет речь о том, как быть, если приложение «крэшится» когда QA с ехидным оскалом барабанит пальцами по экрану девайса. Будем рассматривать в основном бизнес-приложения(ибо из них вынесен этот опыт). Здесь налицо ситуация, когда на некоторые кнопки нажимать одновременно нельзя или, скажем, когда приложение анимирует(некрасивый термин, но что поделать) от одного экрана к другому, а пользователь уже успел нажать кнопку еще на старом экране. Или, скажем, у QA(а потенциально и у пользователя) большой (или маленький) палец — тогда он или не может попасть по кнопке или не может не попасть по кнопке. В таких случаях может потребоваться расширить рабочую область кнопки или сузить ее. Т.е. можно выделить 3 проблемы:

1. «secret combos». Есть UI элементы(кнопки, поля ввода и другие контролы) нажатие на которые вообще не совместимо. Соответственно нужно блокировать их .

2. «Intermediate states». Есть промежуточные, нестабильные состояния приложения, в которые с приложением не стоит взаимодействовать изза «переходных процессов» внутри. В этом случае нужно обождать до перехода в стабильное положение и тогда разрешать взаимодействие.

3. «custom hit area». Т.е. можно сделать чувствительную область кнопки или любого другого контрола круглой, квадратной, звездочкой — на что хватит воображения.

Для начала рассмотрим способы регуляции доставки «тачей» (UITouch) приложению. В доках выделили следующие способы:

  • Отключить доставку тачей для элемента при помощи userInteractionEnabled property. В этом случае тачи  этому контролу не будут доставляться вообще. Это свойство находится в UIView.
  • Отключить доставку тачей глобально для всего приложения при помощи beginIgnoringInteractionEvents и  endIgnoringInteractionEvents. Это свойство находится в классе UIApplication.
  • Отключить мультитач для компонента (несколько пальцев на компоненте одновременно) при помощи multipleTouchEnabled свойства UIView.
  • Метод «эксклюзивного тача» — если компонент нажат и не отпущен — тачи в другие компоненты игнорируются. Это делается при помощи exclusiveTouch свойства UIView.
  • Hit-Testing. Этот метод позволяет вручную решать, попадает ли этот тач в этот компонент или нет. полезен для расширениясужения чувствительных областей компонентов.

Теперь рассмотрим, как можно решить те 3 проблемы, обозначенные вначале с помощью «методов тачерегуляции».

Secret Combos

В UI-ном неигровом приложении чаще всего не нужно чтобы можно было нажимать на несколько кнопок одновременно. Нажал на кнопку — чтото отработалось. Нажал на вторую — еще чтонибудь сделалось. Но нажимать на вторую, удерживая первую — не типичный случай. Так вот, запретить нажатия на все остальные кнопки, когда уже чтонибудь нажато можно с помощью эксклюзивного тача (exclusiveTouch). Устанавливаем его всем кнопкам — и наслаждаемся. Теперь, если мы нажали на один компонент и удерживаем палец на нем — все остальные нажатия на другие компоненты игнорируются.

Intermediate states

В переходных состояниях(смена экранов, анимирование переориентации, чтото анимированно появляется или исчезает, показываем модальный контроллер) лучше всего вообще отключать доставку тачей с помощью beginIgnoringInteractionEvents и  endIgnoringInteractionEvents.

Сustom Hit Area

Система, чтобы определить, по чему нажал пользователь вызывает у самого главного UIView приложения метод hitTest:withEvent:. Этот метод полчает на вход CGPoint в своих координатах(bounds)

Система использует метод hitTest:withEvent: у UIView для определения контрола, которому принадлежит эта точка, чтобы доставить ему это событие. Если hitTest возвращает какойто свой subview — то остальные ветки иерархии subviews отбрасываются и hitTest вызывается у этого subview — и так до тех пор пока hitTest у uiview не вернет свою же uiview. Для определения, принадлежит ли этот CGPoint этому uiview — hitTest вызывает pointInside:(CGPoint)point у каждого из своих subview. Если метод возвращает YES — значит принадлежит.

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

Пример

Теперь рассмотрим тестовый проект в котором есть реализации этих случаев. UI выглядит так:

PushMe и PushMeToo кнопки и рядом переключатели которые вклчают exclusive touch для каждой из них раздельно.

Ниже — кнопка у которой расширена чувствительная область вправо и влево на 30 px и вверхвниз на 20 px. Это можно наблюдать нажимая рядом с ней, а не на ней(меняется подпись). Это можно попробовать даже в симуляторе с зажатыми alt а потом shift.

Еще ниже есть кнопка Start animation — по нажатию на нее кнопка i am flying! начинает двигаться. Двигаться она будет 5 секунд. И в то время,  когда она движется — отключена доставка тачей при помощи beginIgnoringInteractionEvents и  endIgnoringInteractionEvents. Так же можно по свойству UIApplication

- (BOOL)isIgnoringInteractionEvents

определить, игнорируются тачи или нет в данный момент. Но с этим вариантом нужно быть аккуратным, так как, если по каким-либо причинам мы не смогли включить обратно endIgnoringInteractionEvents — приложение перестанет реагировать на пользователя вообще.

Теперь посмотрим исходный код.

С exclusiveTouch все и так понятно — установил в YESNO  — и все дела. А вот для кнопки I am big! нужно наследоваться от UIButton и перегрузить pointsInside следующим образом:

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

CGRect bounds = self.bounds;
//Calculate offsets from buttons bounds
bounds = CGRectMake(bounds.origin.x - 30.0,
bounds.origin.y - 20.0,
bounds.size.width + 60.0,
bounds.size.height + 40.0);
return CGRectContainsPoint(bounds, point);
}

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

Отключение интерфеса и анимация сделаны следующим образом:

-(IBAction)startAnimation:(id)sender{

[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
[UIView animateWithDuration:5.0 animations:^{
_flyingButton.center = CGPointMake(248.0, 408.0);
} completion:^(BOOL finished) {
_flyingButton.center = CGPointMake(76.0, 408.0);
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
}];
}

Работающий проект можно взять тут

Проект создавался в XCode 4.2. Для пользования блоков при анимации нужно iOS 4.0 +. Тестирование multitouch в симуляторе можно сделать при помощи мышки, alt и shift.

Материал предоставлен компанией Softeq Development, FLLC

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *