UIGestureRecognizer:теория,практика,кастомизация

ПредставляяпервыйiPhone于2007年年初发行,СтивДжобсапеллировалкустареваниюконцепциифизическойклавиатуру Сэтогомоментанаразвитиеустройствскачественнымитачскринами,позоои Теперьпалец – главныйинструментуправлениядевайсомимыможемтапатьпокнопкам,свайпатьсписки,пинчитьфотографии…Давайтеразберемсякакэтореализованососторонысофтаинаучимсяиспользоватьвсюмощьмеханизмараспознаванияжестов。

UITouchиегообработка

Нодавайтепопорядку。 Передтем,какначинатьраспознаватьжесты,надопонять,каквообщеприложениеполучаетинформациюокасаниях,свайпах,нажатияхнаэкраниктоихобрабатывает。

Чтопроисходит,когдапалецпользователякасаетсяэкрана? Системасоздаетобъекттипаевоетере Этозначит,чтонавремявсейцепочкисобытий

“палецкоснулсяэкрана→палецдвижетсяпоэкрану→палецоторвалсяотэкрана”

длякаждогопальца,касающегосяэкрана,существуетуникальныйобъектUITouch。 Далее,используямеханизм命中测试’,находитсясамаяглубокаявиирархииUIView,框架которойсодержиевсеб ПолученнаяUIViewстановитсяfirstResponder 使用UITouchипрокидыватьихдальшепоresponseerChain

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

  • touchesBegan(_ touches:设置,并带有事件:UIEvent?) —началокасания(экранакоснулисьпальцы)
  • touchesMoved(_ touches:Set ,事件:UIEvent?) —изменениепараметровкасания(позициянаэкране,сила(ForceTouch))
  • touchesEnded(_ touches:设置,带有事件:UIEvent?) —конецкасания(пальцыбылиубранысэкрана)
  • touchesCancelled(_ touches:Set ,事件:UIEvent?) —отменакасания(далеерассмотрим,чтоэтозначит)

ТаккакэкранiPhoneподдерживаетмультитач,товодинмоментвремениможетизменитьсясостояниесразу Например,еслипользователькоснетсяэкранадвумяпальцамиодновременно,системавызоветtouchesBegan лишьодинраз,новомножестве 设置 мыполучимдваобъекта—图库矢量图片。

Первыетриметода( touchesBegan,touchesMoved,touchesEnded)отвечаютза“нормальный”жизненныйциклUITouchивызываютсяприначалекасания,измененииегопараметров(позициянаэкране,силанажатия)иконцекасаниясоответсвенно。

Однако, 触摸 выбиваетсяизихряда已取消。 Насамомделе,концомжизненногоциклаUITouchможетбытьнетолькофизическийконецжеста,ноикакое-либопрограммноесобытие,вследствиекоторогосистемабудетвынужденапрерватьобработку данногокасания 。 Этоможетпроизойти,например,вслучае,еслипоявилсяинтерфейсвходящегозвонкаипродолжениеобработкикасаниябудетнекорректным – пользовательуженаходитсявконтекстедругогоприложения(телефон)。 Ожидается,чтоприполученииtouches已取消 приложениеотменитвседействиякоторыемоглибытьпротевреивведен。 Дляпониманиялогикиэтоготребованиярассмотримкнопку。 Пустьпользовательнажалнанее,ноещенеподнялпалецивэтовремяпроисходитвходящийзвонок。 ЕслиобработатьtouchesCancelledаналогичноtouchesEndedивызватьобработчикнажатиякнопки,топослеокончаниязвонкаивозвратавприложение будетпроизведенокакое -тодействие,котороепользовательмогнеожидать。

Знаякакобрабатыватьтач-события,мы,вообщеговоря,можемреализоватьскольугодносложнуюлогикуиначатьвычленятьизнихнечто,чтоможетнасинтересовать。 Давайте,например,попробуемобрабатыватьдолгоенажатиенаUIView。

UIGestureRecognizer和UIView

Главноечеопомнить— UIGestureRecognizerпервымполучаетправообработкиUITouchивентовUIGestureRecognizer ‘响应者链’ыстоятнемноговсторонеотобычногомеханизма’。 Система,определивчерезUITestвкоторуюпопалпалецпользователя,собиреевере Средисобранныхрекогнайзеровопределяетсямножествотех,которыебудуработатьипорядокихработы(этоповедениезадаетсячерезделегатарекогнайзераимырассмотримегочутьдалее)。 UIGestureRecognizer可以将UITouch转换为UITouch ,然后将UIView更改为узнает。 Болеетого,еслирекогнайзерперехватитUITouch,тоеноувидитвообщениоднаUIViewвreserChainвоеон

УвидитлиUIViewэтисобытиязависитотреализациирекогнайзераи/илиегонастроек。 Опустивзависимостьотконкретнойреализации,рассмотримкакиепараметрывлияютнадоставкасобыти

  • cancelsTouchesInView:Bool (默认值:true)
  • delaysTouchesBegan:Bool (默认值:false)
  • delaysTouchesEnded:布尔型(默认值:true)

Разберемпо-порядку。

cancelsTouchesInViewUIViewUITouch ,которыеужебылипереданыкэтомумоментувUIView, но имеющиеотношениекжесту,будутотменены( touchesCancelled )。 UITouch的 UIView界面上的图标可能是UIView的外观,也可能是UIView的外观。

Дляпониманиярассмотримпример:ThreeFingerRecognizer,детектирующийкасаниетремяпальцами。 Переопределимметодыtouches {XXX},在 UIView上,在 добавиввнихprintипосмотримкакбудетменятьсявыводвзависимост上。

  1. Максимальноечислокасанийвжесте— 1(перетаскиваемоднимпальцем)。
  2. Сохраняемточкуначалажеста。
  3. ПрикаждомсдвигепальцаобновляемпозициюUIViewнаэкраненатребуемуювеличину。

Дальшепоспискузум— UIUI的иипковымжестомдвумяпальцамиизменятьразмер UIPinchGestureRecognizer 。 Общаяидеяаналогичнаподходусперетаскиванием。

Запустивприложениеможнозаметить,чтовповеденииестьизъян。 Чтобудет,еслиначатьдвигатьквадратоднимпальцем,апотом,коснувшисьэкранавторым,попытат UIPinchGestureRecognizer会在UIView界面上显示неменяется。 Инаоборот,еслиначатьзумитьквадратдвумяпальцами,тосистемауженедастегосдвинутьсместа。 Настаетвремяделегата!

Остаетсяразобратьсясповоротом。 Есливыдочиталидоэтойточки,тоувасужедостаточнознаний,чтобыреализоватьэтуфункциоельно。 Нуаеслинавашемпутивстретятсяпроблемы,товывсевлакполномукодупример。

Добавимещеоднотребованиекнашемуприложению。 ПустьпотапунаUIViewбудетменятьсяеёцвет,以及подвойномутапувозвращатьсякначальнымзначеира。 ДляобработкитаповбудемиспользоватьдваUITapGestureRecognizer’аинезабудемнастроитьпорядорееоее

  1. Ключевоймоментпримера—解决方案

Ноневсегдаобязательнописатьтакмногокодадляработысделегатами。 Есливоднойточкеимеетсядоступкобоим рекогнайзерам ,тореализоватьсхожееповедениеможночерезявноеопределение зависимостипосредствамметода 需要(toFail otherGestureRecognizer:UIGestureRecognizer)。

КастомныйUIGestureRecognizer

UIKitпредоставляемнамбогатыйнаборрекогнайзероввсехосновных“ iOSжестов”, Ночто,еслипоставленнаязадачавсежевыходитзарамкивозможностейстандартныхрекогнайзеров? Безусловноможнонаписатьсвоюреализацию!

Первымшагомреализациисобственногорекогнайзерадолжнобытьдобавление строчки 进口UIKit.UIGestureRecognizerSubclass,чтоподключиткатегориюклассаUIGestureRecognizerспереопределениемсвойства 状态 исделаетегодоступнымназапись。

Реализациясвоегорекогнайзерасводитсякпереопределениюужезнакомых намметодов 的touchesBegan /移动/端/取消 вкоторыхдолжнабытьсосредоточенавсялогикапо обработкесобытийивычлененияизнихтребуемыхжестов 。 Такженеобходимопереопределитьметодreset ()或которомнеобходимоочищатьсостояниерекогнайзера。 И,последнее,чтоможетнампригодится – метод 忽略(_触控:UITouch,事件:的UIEvent),посредствамкоторого,мыможемсообщитьсистеме,чтоконкретныйUITouchнеотноситсякнашемужестуимыне хотимдалееполучатьуведомленияоегоизменениях 。

Собираявсевоедино,давайтенапишемсвойнепрерывныйрекогнайзер,которыйбудетраспознаватьсильноенажатие(ForceTouch)и,еслисилапревысилазаданныйпорог,тоуведомлять目标оеёизменении。

Каквидите,кодсильнопт,чтомыписаливпримерепрораспознаваниедолгогонажатиянаUIView。 Нонадонезабыватьпрокорректнуюреализациюметодаreset ()

Вместозаключения

ИзэтойстатьивымоглиполучитьосновныезнаниядляработысUIGestureRecognizer’амииреализациисвоихсобств。

ПолныйкодпримеровможнонайтинаGitHub。

Крометогоникогданебываетлишнимпочитатьофициальнуюдокументацию:

  • UIGestureRecognizer
  • 手势识别器状态机
  • 实施自定义识别器