1# Gesture Blocking Enhancement
2
3Gesture blocking enhancement offers components the capability to block gestures. You can handle built-in gestures in parallel with gestures that have a higher priority as needed, and can dynamically control the triggering of gesture events.
4
5>  **NOTE**
6>
7>  The initial APIs of this module are supported since API version 12. Updates will be marked with a superscript to indicate their earliest API version.
8
9## shouldBuiltInRecognizerParallelWith
10
11shouldBuiltInRecognizerParallelWith(callback: ShouldBuiltInRecognizerParallelWithCallback): T
12
13Provides a callback to set the parallel relationship between built-in gestures and gestures of other components in the response chain.
14
15**Atomic service API**: This API can be used in atomic services since API version 12.
16
17**System capability**: SystemCapability.ArkUI.ArkUI.Full
18
19**Parameters**
20| Name       | Type                   | Mandatory | Description                         |
21| ---------- | -------------------------- | ------- | ----------------------------- |
22| callback      | [ShouldBuiltInRecognizerParallelWithCallback](#shouldbuiltinrecognizerparallelwithcallback) | Yes  |  Callback used to set the parallel relationship between built-in gestures and gestures of other components in the response chain. This callback is triggered during touch hit testing to form a gesture parallel relationship.|
23
24**Return value**
25
26| Type| Description|
27| -------- | -------- |
28| T | Current component.|
29
30## ShouldBuiltInRecognizerParallelWithCallback
31
32type ShouldBuiltInRecognizerParallelWithCallback = (current: GestureRecognizer, others: Array\<GestureRecognizer\>) => GestureRecognizer
33
34Represents the callback used to set the parallel relationship between built-in gestures and gestures of other components in the response chain.
35
36**Atomic service API**: This API can be used in atomic services since API version 12.
37
38**System capability**: SystemCapability.ArkUI.ArkUI.Full
39
40**Parameters**
41
42| Name  | Type                     | Mandatory| Description                                                        |
43| -------- | ------------------------- | ---- | ------------------------------------------------------------ |
44| current | [GestureRecognizer](#gesturerecognizer) | Yes  | Built-in gesture recognizer of the current component. Currently only a built-in gesture recognizer of the [PAN_GESTURE](ts-gesture-customize-judge.md#gesturejudgeresult11) type is supported.|
45| others | Array\<[GestureRecognizer](#gesturerecognizer)\> | Yes  | Gesture recognizers of the same type from other components with higher priority in the response chain.|
46
47**Return value**
48
49| Type    | Description       |
50| ------ | --------- |
51| [GestureRecognizer](#gesturerecognizer) | Gesture recognizer that is bound in parallel with the current recognizer.|
52
53## GestureRecognizer
54
55Defines a gesture recognizer object.
56
57### getTag
58
59getTag(): string
60
61Obtains the tag of this gesture recognizer.
62
63**Atomic service API**: This API can be used in atomic services since API version 12.
64
65**System capability**: SystemCapability.ArkUI.ArkUI.Full
66
67**Return value**
68
69| Type    | Description       |
70| ------ | --------- |
71| string | Tag of the current gesture recognizer.|
72
73### getType
74
75getType(): GestureControl.GestureType
76
77Obtains the type of this gesture recognizer.
78
79**Atomic service API**: This API can be used in atomic services since API version 12.
80
81**System capability**: SystemCapability.ArkUI.ArkUI.Full
82
83**Return value**
84
85| Type    | Description       |
86| ------ | --------- |
87| [GestureControl.GestureType](ts-gesture-customize-judge.md#gesturetype11) | Type of the current gesture recognizer.|
88
89### isBuiltIn
90
91isBuiltIn(): boolean
92
93Obtains whether this gesture recognizer is a built-in gesture.
94
95**Atomic service API**: This API can be used in atomic services since API version 12.
96
97**System capability**: SystemCapability.ArkUI.ArkUI.Full
98
99**Return value**
100
101| Type    | Description       |
102| ------ | --------- |
103| boolean | Whether this gesture recognizer is a built-in gesture.|
104
105### setEnabled
106
107setEnabled(isEnabled: boolean): void
108
109Sets the enabled state of this gesture recognizer.
110
111**Atomic service API**: This API can be used in atomic services since API version 12.
112
113**System capability**: SystemCapability.ArkUI.ArkUI.Full
114
115**Parameters**
116
117| Name    | Type                          | Mandatory  | Description |
118| ------- | ---------------------------------- | ---- | ----- |
119| isEnabled   | boolean         | Yes   | Enabled state to set.|
120
121### isEnabled
122
123isEnabled(): boolean
124
125Obtains the enabled state of this gesture recognizer.
126
127**Atomic service API**: This API can be used in atomic services since API version 12.
128
129**System capability**: SystemCapability.ArkUI.ArkUI.Full
130
131**Return value**
132
133| Type    | Description       |
134| ------ | --------- |
135| boolean | Enabled state of the gesture recognizer.|
136
137### getState
138
139getState(): GestureRecognizerState
140
141Obtains the state of this gesture recognizer.
142
143**Atomic service API**: This API can be used in atomic services since API version 12.
144
145**System capability**: SystemCapability.ArkUI.ArkUI.Full
146
147**Return value**
148
149| Type    | Description       |
150| ------ | --------- |
151| [GestureRecognizerState](#gesturerecognizerstate) | State of the gesture recognizer.|
152
153### getEventTargetInfo
154
155getEventTargetInfo(): EventTargetInfo
156
157Obtains the information about the component corresponding to this gesture recognizer.
158
159**Atomic service API**: This API can be used in atomic services since API version 12.
160
161**System capability**: SystemCapability.ArkUI.ArkUI.Full
162
163**Return value**
164
165| Type    | Description       |
166| ------ | --------- |
167| [EventTargetInfo](#eventtargetinfo) | Information about the component corresponding to the current gesture recognizer.|
168
169### isValid
170
171isValid(): boolean;
172
173Whether the current gesture recognizer is valid.
174
175**Atomic service API**: This API can be used in atomic services since API version 12.
176
177**System capability**: SystemCapability.ArkUI.ArkUI.Full
178
179**Return value**
180
181| Type    | Description       |
182| ------ | --------- |
183| boolean | Whether the current gesture recognizer is valid. Returns **false** if the component bound to this recognizer is destructed or if the recognizer is not on the response chain.|
184
185## GestureRecognizerState
186
187Enumerates the gesture recognizer states.
188
189**Atomic service API**: This API can be used in atomic services since API version 12.
190
191**System capability**: SystemCapability.ArkUI.ArkUI.Full
192
193| Name   | Value  | Description                              |
194| ------- | ---- | ---------------------------------- |
195| READY | 0    | Ready.|
196| DETECTING    | 1    | Detecting.|
197| PENDING    | 2    | Pending.|
198| BLOCKED    | 3    | Blocked.|
199| SUCCESSFUL    | 4    | Successful.|
200| FAILED    | 5    | Failed.|
201
202## EventTargetInfo
203
204Provides the information about the component corresponding to the gesture recognizer.
205
206### getId
207
208getId(): string
209
210Obtains the ID of this component.
211
212**Atomic service API**: This API can be used in atomic services since API version 12.
213
214**System capability**: SystemCapability.ArkUI.ArkUI.Full
215
216**Return value**
217
218| Type    | Description       |
219| ------ | --------- |
220| string | [ID](./ts-universal-attributes-component-id.md#id) of the current component.|
221
222## ScrollableTargetInfo
223
224Provides the information about the scrollable container component corresponding to the gesture recognizer. It inherits from [EventTargetInfo](#eventtargetinfo).
225
226### isBegin
227
228isBegin(): boolean
229
230Checks whether this scrollable container component is scrolled to the top. If it is a **Swiper** component in loop mode, **false** is returned.
231
232**Atomic service API**: This API can be used in atomic services since API version 12.
233
234**System capability**: SystemCapability.ArkUI.ArkUI.Full
235
236**Return value**
237
238| Type    | Description       |
239| ------ | --------- |
240| boolean | Whether the current scrollable container component is scrolled to the top.|
241
242### isEnd
243
244isEnd(): boolean
245
246Checks whether this scrollable container component is scrolled to the bottom. If it is a **Swiper** component in loop mode, **false** is returned.
247
248**Atomic service API**: This API can be used in atomic services since API version 12.
249
250**System capability**: SystemCapability.ArkUI.ArkUI.Full
251
252**Return value**
253
254| Type    | Description       |
255| ------ | --------- |
256| boolean | Whether the current scrollable container component is scrolled to the bottom.|
257
258## PanRecognizer
259
260Defines a pan gesture recognizer object. It inherits from [GestureRecognizer](#gesturerecognizer).
261
262### getPanGestureOptions
263
264getPanGestureOptions(): PanGestureOptions
265
266Obtains the properties of this pan gesture recognizer.
267
268**Atomic service API**: This API can be used in atomic services since API version 12.
269
270**System capability**: SystemCapability.ArkUI.ArkUI.Full
271
272**Return value**
273
274| Type    | Description       |
275| ------ | --------- |
276| [PanGestureOptions](./ts-basic-gestures-pangesture.md#pangestureoptions) | Properties of the current pan gesture recognizer.|
277
278## onGestureRecognizerJudgeBegin<sup>13+</sup>
279
280onGestureRecognizerJudgeBegin(callback: GestureRecognizerJudgeBeginCallback, exposeInnerGesture: boolean): T
281
282Binds a custom gesture recognizer judgment callback to the component.
283
284The **exposeInnerGesture** parameter indicates whether to expose the callback to the built-in components of ArkUI native composite components.<br>
285For scenarios where the callback does not need to be exposed to the built-in components of ArkUI native composite components, you are advised to use the [onGestureRecognizerJudgeBegin](#ongesturerecognizerjudgebegin) API. If the callback needs to be exposed to the built-in components of ArkUI native composite components, use this API and set **exposeInnerGesture** to **true**.
286
287**Atomic service API**: This API can be used in atomic services since API version 13.
288
289**System capability**: SystemCapability.ArkUI.ArkUI.Full
290
291**Parameters**
292| Name       | Type                   | Mandatory | Description                         |
293| ---------- | -------------------------- | ------- | ----------------------------- |
294| callback      | [GestureRecognizerJudgeBeginCallback](#gesturerecognizerjudgebegincallback) | Yes    |  Custom gesture recognizer judgment callback to bind to the component. This callback is triggered when the gesture bound to the component is accepted to obtain the result.|
295| exposeInnerGesture   | boolean         | Yes   | Whether to expose the internal gesture.<br>Default value: **false**<br>**NOTE**<br>For a composite component, setting this parameter to **true** exposes the internal gesture recognizer of the composite component in the **current** parameter callback.<br>Currently, only the [Tabs](ts-container-tabs.md) component is supported. Do not set this parameter for other components.<br>When this parameter is set to **false**, this API provides the same functionality as the [onGestureRecognizerJudgeBegin](#ongesturerecognizerjudgebegin) API.|
296
297## onGestureRecognizerJudgeBegin
298
299onGestureRecognizerJudgeBegin(callback: GestureRecognizerJudgeBeginCallback): T
300
301Binds a custom gesture recognizer judgment callback to the component.
302
303**Atomic service API**: This API can be used in atomic services since API version 12.
304
305**System capability**: SystemCapability.ArkUI.ArkUI.Full
306
307**Parameters**
308| Name       | Type                   | Mandatory | Description                         |
309| ---------- | -------------------------- | ------- | ----------------------------- |
310| callback      | [GestureRecognizerJudgeBeginCallback](#gesturerecognizerjudgebegincallback) | Yes    |  Custom gesture recognizer judgment callback to bind to the component. This callback is triggered when the gesture bound to the component is accepted to obtain the result.|
311
312**Return value**
313
314| Type| Description|
315| -------- | -------- |
316| T | Current component.|
317
318## GestureRecognizerJudgeBeginCallback
319
320type GestureRecognizerJudgeBeginCallback = (event: BaseGestureEvent, current: GestureRecognizer, recognizers: Array\<GestureRecognizer\>) => GestureJudgeResult
321
322Represents a custom gesture recognizer judgment callback.
323
324**Atomic service API**: This API can be used in atomic services since API version 12.
325
326**System capability**: SystemCapability.ArkUI.ArkUI.Full
327
328**Parameters**
329
330| Name  | Type                     | Mandatory| Description                                                        |
331| -------- | ------------------------- | ---- | ------------------------------------------------------------ |
332| event | [BaseGestureEvent](./ts-gesture-customize-judge.md#basegestureevent) | Yes  | Information about the current basic gesture event.|
333| current | [GestureRecognizer](#gesturerecognizer) | Yes  | Gesture recognizer object that is about to respond.|
334| others | Array\<[GestureRecognizer](#gesturerecognizer)\> | Yes  | Other gesture recognizer objects in the response chain.|
335
336**Return value**
337
338| Type    | Description       |
339| ------ | --------- |
340| [GestureJudgeResult](ts-gesture-customize-judge.md#gesturejudgeresult11) | Result of whether the gesture judgment is successful.|
341
342## Example
343
344### Example 1: Implementing Nested Scrolling
345
346This example demonstrates how to implement nested scrolling using **shouldBuiltInRecognizerParallelWith** and **onGestureRecognizerJudgeBegin**. The inner component takes precedence in responding to swipe gestures. When the inner component reaches the top or bottom, the outer component can then take over the scrolling.
347
348```ts
349// xxx.ets
350@Entry
351@Component
352struct FatherControlChild {
353  scroller: Scroller = new Scroller()
354  scroller2: Scroller = new Scroller()
355  private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
356  private childRecognizer: GestureRecognizer = new GestureRecognizer()
357  private currentRecognizer: GestureRecognizer = new GestureRecognizer()
358  private lastOffset: number = 0
359
360  build() {
361    Stack({ alignContent: Alignment.TopStart }) {
362      Scroll(this.scroller) { // Outer scrollable container.
363        Column() {
364          Text("Scroll Area")
365            .width('90%')
366            .height(150)
367            .backgroundColor(0xFFFFFF)
368            .borderRadius(15)
369            .fontSize(16)
370            .textAlign(TextAlign.Center)
371            .margin({ top: 10 })
372          Scroll(this.scroller2) { // Inner scrollable container.
373            Column() {
374              Text("Scroll Area2")
375                .width('90%')
376                .height(150)
377                .backgroundColor(0xFFFFFF)
378                .borderRadius(15)
379                .fontSize(16)
380                .textAlign(TextAlign.Center)
381                .margin({ top: 10 })
382              Column() {
383                ForEach(this.arr, (item: number) => {
384                  Text(item.toString())
385                    .width('90%')
386                    .height(150)
387                    .backgroundColor(0xFFFFFF)
388                    .borderRadius(15)
389                    .fontSize(16)
390                    .textAlign(TextAlign.Center)
391                    .margin({ top: 10 })
392                }, (item: string) => item)
393              }.width('100%')
394            }
395          }
396          .id("inner")
397          .width('100%')
398          .height(800)
399        }.width('100%')
400      }
401      .id("outer")
402      .height(600)
403      .scrollable(ScrollDirection.Vertical) // The scrollbar scrolls in the vertical direction.
404      .scrollBar(BarState.On) // The scrollbar is always displayed.
405      .scrollBarColor(Color.Gray) // The scrollbar color is gray.
406      .scrollBarWidth(10) // The scrollbar width is 10.
407      .edgeEffect(EdgeEffect.None)
408      .shouldBuiltInRecognizerParallelWith((current: GestureRecognizer, others: Array<GestureRecognizer>) => {
409        for (let i = 0; i < others.length; i++) {
410          let target = others[i].getEventTargetInfo();
411          if (target) {
412            if (target.getId() == "inner" && others[i].isBuiltIn() && others[i].getType() == GestureControl.GestureType.PAN_GESTURE) { // Find the recognizer to form a parallel gesture.
413              this.currentRecognizer = current; // Save the recognizer of the current component.
414              this.childRecognizer = others[i]; // Save the recognizer to form a parallel gesture.
415              return others[i]; // Return the recognizer to form a parallel gesture.
416            }
417          }
418        }
419        return undefined;
420      })
421      .onGestureRecognizerJudgeBegin((event: BaseGestureEvent, current: GestureRecognizer, others: Array<GestureRecognizer>) => { // When gesture recognition is about to be successful, set the recognizer's enabled state based on the current component state.
422        if (current) {
423          let target = current.getEventTargetInfo();
424          if (target) {
425            if (target.getId() == "outer" && current.isBuiltIn() && current.getType() == GestureControl.GestureType.PAN_GESTURE) {
426              if (others) {
427                for (let i = 0; i < others.length; i++) {
428                  let target = others[i].getEventTargetInfo() as ScrollableTargetInfo;
429                  if (target instanceof ScrollableTargetInfo && target.getId() == "inner") { // Identify the recognizer to work in parallel on the response chain.
430                    let panEvent = event as PanGestureEvent;
431                    if (target.isEnd()) { // Dynamically control the recognizer's enabled state based on the current component state and direction of movement.
432                      if (panEvent && panEvent.offsetY < 0) {
433                        this.childRecognizer.setEnabled(false)
434                        this.currentRecognizer.setEnabled(true)
435                      } else {
436                        this.childRecognizer.setEnabled(true)
437                        this.currentRecognizer.setEnabled(false)
438                      }
439                    } else if (target.isBegin()) {
440                      if (panEvent.offsetY > 0) {
441                        this.childRecognizer.setEnabled(false)
442                        this.currentRecognizer.setEnabled(true)
443                      } else {
444                        this.childRecognizer.setEnabled(true)
445                        this.currentRecognizer.setEnabled(false)
446                      }
447                    } else {
448                      this.childRecognizer.setEnabled(true)
449                      this.currentRecognizer.setEnabled(false)
450                    }
451                  }
452                }
453              }
454            }
455          }
456        }
457        return GestureJudgeResult.CONTINUE;
458      })
459      .parallelGesture( // Bind a pan gesture as a dynamic controller.
460        PanGesture()
461          .onActionUpdate((event: GestureEvent)=>{
462            if (this.childRecognizer.getState() != GestureRecognizerState.SUCCESSFUL || this.currentRecognizer.getState() != GestureRecognizerState.SUCCESSFUL) { // If neither recognizer is in the SUCCESSFUL state, no control is applied.
463              return;
464            }
465            let target = this.childRecognizer.getEventTargetInfo() as ScrollableTargetInfo;
466            let currentTarget = this.currentRecognizer.getEventTargetInfo() as ScrollableTargetInfo;
467            if (target instanceof ScrollableTargetInfo && currentTarget instanceof ScrollableTargetInfo) {
468              if (target.isEnd()) { // Adjust the enabled state of the gesture recognizers based on the current component state during movement.
469                if ((event.offsetY - this.lastOffset) < 0) {
470                  this.childRecognizer.setEnabled(false)
471                  if (currentTarget.isEnd()) {
472                    this.currentRecognizer.setEnabled(false)
473                  } else {
474                    this.currentRecognizer.setEnabled(true)
475                  }
476                } else {
477                  this.childRecognizer.setEnabled(true)
478                  this.currentRecognizer.setEnabled(false)
479                }
480              } else if (target.isBegin()) {
481                if ((event.offsetY - this.lastOffset) > 0) {
482                  this.childRecognizer.setEnabled(false)
483                  if (currentTarget.isBegin()) {
484                    this.currentRecognizer.setEnabled(false)
485                  } else {
486                    this.currentRecognizer.setEnabled(true)
487                  }
488                } else {
489                  this.childRecognizer.setEnabled(true)
490                  this.currentRecognizer.setEnabled(false)
491                }
492              } else {
493                this.childRecognizer.setEnabled(true)
494                this.currentRecognizer.setEnabled(false)
495              }
496            }
497            this.lastOffset = event.offsetY
498          })
499      )
500    }.width('100%').height('100%').backgroundColor(0xDCDCDC)
501  }
502}
503```
504
505### Example 2: Blocking Inner Container Gestures in Nested Scrolling
506
507This example demonstrates how to set the **exposeInnerGesture** parameter to **true** to enable a first-level **Tabs** container to intercept the swipe gestures of a nested second-level **Tabs** container, thereby triggering the swipe gestures of the built-in **Swiper** component of first-level **Tabs** container.
508You can define variables to record the index of the inner **Tabs** container and use this index to determine when to trigger the callback to block the swipe gestures of the outer **Tabs** container when the inner **Tabs** container reaches its boundaries.
509
510```ts
511// xxx.ets
512@Entry
513@Component
514struct Index {
515  @State currentIndex: number = 0
516  @State selectedIndex: number = 0
517  @State fontColor: string = '#182431'
518  @State selectedFontColor: string = '#007DFF'
519  innerSelectedIndex: number = 0 // Record the index of the inner Tabs container.
520  controller?: TabsController = new TabsController();
521  @Builder
522  tabBuilder(index: number, name: string) {
523    Column() {
524      Text(name)
525        .fontColor(this.selectedIndex === index ? this.selectedFontColor : this.fontColor)
526        .fontSize(16)
527        .fontWeight(this.selectedIndex === index ? 500 : 400)
528        .lineHeight(22)
529        .margin({ top: 17, bottom: 7 })
530      Divider()
531        .strokeWidth(2)
532        .color('#007DFF')
533        .opacity(this.selectedIndex === index ? 1 : 0)
534    }.width('100%')
535  }
536  build() {
537    Column() {
538      Tabs({ barPosition: BarPosition.Start, index: this.currentIndex, controller: this.controller }) {
539        TabContent() {
540          Column().width('100%').height('100%').backgroundColor(Color.Green)
541        }.tabBar(this.tabBuilder(0, 'green'))
542        TabContent() {
543          Tabs() {
544            TabContent() {
545              Column().width('100%').height('100%').backgroundColor(Color.Blue)
546            }.tabBar(new SubTabBarStyle('blue'))
547            TabContent() {
548              Column().width('100%').height('100%').backgroundColor(Color.Pink)
549            }.tabBar(new SubTabBarStyle('pink'))
550          }
551          .onAnimationStart((index: number, targetIndex: number) => {
552            console.info('ets onGestureRecognizerJudgeBegin child:' + targetIndex)
553            this.innerSelectedIndex = targetIndex
554          })
555          .onGestureRecognizerJudgeBegin((event: BaseGestureEvent, current: GestureRecognizer,
556            others: Array<GestureRecognizer>): GestureJudgeResult => { // When gesture recognition is about to be successful, set the recognizer's enabled state based on the current component state.
557            console.info('ets onGestureRecognizerJudgeBegin child')
558            if (current) {
559              let target = current.getEventTargetInfo();
560              if (target && current.isBuiltIn() && current.getType() == GestureControl.GestureType.PAN_GESTURE) {
561                console.info('ets onGestureRecognizerJudgeBegin child PAN_GESTURE')
562                let swiperTaget = target as ScrollableTargetInfo
563                if (swiperTaget instanceof ScrollableTargetInfo) {
564                  console.info('ets onGestureRecognizerJudgeBegin child PAN_GESTURE isEnd: ' + swiperTaget.isEnd() + ' isBegin: ' + swiperTaget.isBegin())
565                }
566                if (swiperTaget instanceof ScrollableTargetInfo &&
567                  ((swiperTaget.isEnd() || this.innerSelectedIndex === 1) || // Check whether swiperTarget.isEnd() or innerSelectedIndex === total number of inner Tabs - 1, indicating the inner Tabs container has reached the end.
568                    (swiperTaget.isBegin() || this.innerSelectedIndex === 0))) { // Check whether swiperTarget.isBegin() or innerSelectedIndex === 0, indicating the inner Tabs container has reached the beginning.
569                  let panEvent = event as PanGestureEvent;
570                  console.log('pan direction:' + panEvent.offsetX + ' begin:' + swiperTaget.isBegin() + ' end:' +
571                  swiperTaget.isEnd() + ' index:' + this.innerSelectedIndex)
572                  if (panEvent && panEvent.offsetX < 0 && (swiperTaget.isEnd() || this.innerSelectedIndex === 1)) {
573                    console.info('ets onGestureRecognizerJudgeBegin child reject end')
574                    return GestureJudgeResult.REJECT;
575                  }
576                  if (panEvent && panEvent.offsetX > 0 && (swiperTaget.isBegin() || this.innerSelectedIndex === 0)) {
577                    console.info('ets onGestureRecognizerJudgeBegin child reject begin')
578                    return GestureJudgeResult.REJECT;
579                  }
580                }
581              }
582            }
583            return GestureJudgeResult.CONTINUE;
584          }, true)
585        }.tabBar(this.tabBuilder(1, 'blue and pink'))
586        TabContent() {
587          Column().width('100%').height('100%').backgroundColor(Color.Brown)
588        }.tabBar(this.tabBuilder(2, 'brown'))
589      }
590      .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => {
591        // Triggered when the switching animation starts. The target tab shows an underline.
592        this.selectedIndex = targetIndex
593      })
594    }
595  }
596}
597```
598
599 ![example](figures/gesture_recognizer.gif)
600