Как да отхвърлите клавиатурата с реагиране-навигация в приложенията React Native

Показването и отхвърлянето на клавиатурата изглежда като тривиално нещо, което трябва да се направи в мобилните приложения, но може да бъде сложно автоматично да я отхвърлите, когато се съчетае с реакция-навигация и модално представяне. Поне това е според първоначалното ми предположение. Тази статия има за цел да опише подробно какво научих за работа с клавиатурата и как да избегнете допълнително докосване при работа с TextInput Ще има и много проверка на код, благодарение на това, че всички библиотеки са с отворен код. Версията на React Native, която използвам по време на писането, е 0.57.5

Вграденият компонент TextInput

React Native се предлага с куп основни компоненти, един от тях е TextInput за въвеждане на текст в приложението чрез клавиатура.

import React, {Component} от 'react';
import {AppRegistry, TextInput} от 'react-native';
експорт по подразбиране клас UselessTextInput разширява компонент {
  конструктор (подпори) {
    супер (подпори);
    this.state = {текст: 'безполезен заместител'};
  }
render () {
    връщане (
       this.setState ({текст})}
        стойност = {this.state.text}
      />
    );
  }
}

Това е всичко, когато щракнем върху въвеждането на текст, клавиатурата се появява, позволяваща ни да въвеждаме стойности. За да изхвърлите клавиатурата чрез натискане навсякъде на екрана, лесното решение е TouchableWithoutFeedback заедно с клавиатурата. Това е подобно на това да имате UITapGestureRecognizer в iOS UIView и извикване за извикване.endEditing

import {Keyboard} от „реагира-роден“

Keyboard.dismiss ()

TextInput вътре в ScrollView

Обикновено трябва да имаме някои въвеждания на текст вътре в превъртащ се компонент, в React Native, който е най-вече ScrollView, за да можем да обработваме дълъг списък от съдържание и да избягваме клавиатурата. Ако TextInput е вътре в ScrollView, тогава начинът на освобождаване на клавиатурата се държи малко по-различно и зависи от клавиатуратаShouldPersistTaps

Определя кога клавиатурата трябва да остане видима след докосване.

  • „никога“ (по подразбиране), докосване извън фокусираното въвеждане на текст, когато клавиатурата е нагоре, отхвърля клавиатурата. Когато това се случи, децата няма да получат крана.
  • „винаги“, клавиатурата няма да се отхвърли автоматично и изгледът на превъртане няма да хваща кранове, но децата от изгледа на превъртане могат да хващат кранове.
  • „обработено“, клавиатурата няма да се отхвърли автоматично, когато кранът е обработен от деца (или заловен от прародител).

Режимът никога не трябва да е желаното поведение в повечето случаи, като щракнете навсякъде извън фокусираното въвеждане на текст, трябва да отхвърлите клавиатурата.

В моето приложение има някои въвеждания на текст и бутон за действие. Сценарият е, че потребителите въвеждат някаква информация и след това натискат този бутон, за да регистрират данни. С режим никога не трябва да натискаме бутона два пъти, един за изхвърляне на клавиатурата и два за натискане на бутона. Така че решението е да се използва винаги режим. По този начин Бутонът винаги получава първо събитие в пресата.

ScrollView се грижи за клавиатурата

В родния клас RCTScrollView, който реагира на захранването на родния ScrollView има код за работа с режим на отхвърляне

RCT_SET_AND_PRESERVE_OFFSET (setKeyboardDismissMode, клавиатураDismissMode, UIScrollViewKeyboardDismissMode)

Избираната от него опция е свойството UIScrollViewKeyboardDismissMode за клавиатураDismissMode

Начинът, по който клавиатурата се отхвърля при започване на влачене в екрана за превъртане.

Както можете да видите, възможните режими са onDrag и интерактивни. И реагирайте родния излага точка за персонализиране за това чрез клавиатуратаShouldPersistTaps

case none Клавиатурата не се отхвърля с влачене.

case onDrag Клавиатурата се отхвърля при започване на влачене.

случай интерактивна Клавиатурата следва плъзгащия се сензорен екран и може да бъде изтеглена отново нагоре, за да анулира отхвърлянето.

ScrollView в Modal

Но това не работи, когато ScrollView е вътре в Modal. Под Modal имах предвид компонента Modal в React Native. Единствената библиотека, която използвам, е реагиране-навигация и поддържа отваряне на модал на цял екран също, но по този начин ние декларираме модал в реакция-навигация изглежда като стек и е объркващо, така че по-скоро не бих го използвал. Използвам Modal в реагиращ род и това работи доста добре.

Така че, ако имаме TextInput в ScrollView вътре в Modal, тогава клавиатуратаShouldPersistTaps не работи. Изглежда, че Modal е наясно с родителския ScrollView, така че трябва да декларираме клавиатуратаShouldPersistTaps = 'винаги' на всеки родител ScrollView. В React Native FlatList и SectionList използва ScrollView под капака, така че трябва да сме наясно с всички тези компоненти на ScrollView.

Говореща реакция-навигация

Тъй като моето приложение разчита до голяма степен на реакция-навигация, е добре да разберем подробно неговите компоненти, така че да се уверим къде е проблемът. По-долу съм писал за реакционно-навигационната структура.

Както всяко традиционно мобилно приложение, моето приложение се състои от много навигатори на стекове в навигатора на раздели. В iOS това означава много UINavigationViewController вътре UITabbarController. В реакция-навигация използвам createMaterialTopTabNavigator вътре createBottomTabNavigator

import {createMaterialTopTabNavigator} от „реагиране-навигация“
import {createBottomTabNavigator, BottomTabBar} от „react-navigation-tabs“

Екранът, с който имам проблем с клавиатурата, е модал, представен от втория екран в един от стековите навигатори, така че нека разгледаме всички възможни ScrollView в йерархията. Този процес включва много четене на код и по този начин обичам отворен код.

Първо нека започнем с createBottomTabNavigator, който използва createTabNavigator заедно със собствения си TabNavigationView

клас TabNavigationView разширява React.PureComponent 
експортиране по подразбиране createTabNavigator (TabNavigationView);

Навигаторът на раздели има изглед на лентата с раздели под ScreenContainer, който се използва, за да съдържа изглед. ScreenContainer е от естествени екрани „Този ​​проект има за цел да разкрие нативните компоненти на навигационния контейнер на React Native“. По-долу е как работи навигаторът на раздели.

render () {
  const {навигация, renderScene, lazy} = this.props;
  const {route} = navigation.state;
  const {load} = this.state
  връщане (
    <Преглед на стил = {styles.container}>
      
        {route.map ((маршрут, индекс) => {
          ако (мързелив &&! load.includes (index)) {
            // Не изобразявайте екран, ако никога не сме преминавали към него
            връща нула;
          
          const isFocused = navigation.state.index === индекс
          връщане (
            
              {renderScene ({route})}
            
          );
        })}
      
      {This._renderTabBar ()}
    
  );
}

Лента с раздели се изобразява с помощта на BottomTabBar във функцията _renderTabBar. Поглеждайки кода, целият навигатор на раздела няма нищо общо с ScrollView.

Така че в списъка с подозрения е оставен само createMaterialTopTabNavigator. Използвам го в приложението с swipeEnabled: true. И като погледнем вноса, навигаторът в горните раздели има

импортирайте MaterialTopTabBar, {тип TabBarOptions,} от '../views/MaterialTopTabBar';

MaterialTopTabBar има импортиране от реакция-native-tab-view

import {TabBar} от 'react-native-tab-view';

който има ScrollView

<Преглед на стил = {styles.scroll}>
  

Свойствената клавиатураShouldPersistTaps първоначално беше зададена винаги, след това се върна към обработена, за да избегнем грешката, която не можем да натиснем нито един бутон в лентата с раздели, докато клавиатурата е отворена https://github.com/react-native-community/react-native -tab виждане / въпроси / 375

Но този TabBar няма нищо с нашия проблем, защото е само за съдържане на бутони на лентата с раздели.

Прекарване на пръст в createMaterialTopTabNavigator

След като погледнем още един createMaterialTopTabNavigator, виждаме още внос от изглед на реакцията-native-tab-view

import {TabView, PagerPan} от 'react-native-tab-view';

TabView премина с пръстEnabled премина в

връщане (
  
);

и той прави PagerDefault, който от своя страна използва PagerScroll за iOS

import {Platform} от 'react-native';
нека Пейджър;
превключвател (Platform.OS) {
  случай 'android':
    Pager = изисквам ('./ PagerAndroid'). По подразбиране;
    прекъсване;
  случай 'ios':
    Pager = изисквам ('./ PagerScroll'). По подразбиране;
    прекъсване;
  по подразбиране:
    Pager = изисквам ('./ PagerPan'). По подразбиране;
    прекъсване;
}
експортиране по подразбиране Pager;

Така PagerScroll използва ScrollView, за да обработва превъртането, за да съответства на стила на материала, който потребителят може да превърта между страници, и има клавиатураShouldPersistTaps = "винаги", което трябва да е правилно.

връщане (
  

Така че нищо не изглежда подозрително при реагиране-навигация, което ме подтиква да погледна код от моя проект.

Отстраняване на грешки FlatList, SectionList и ScrollView

Както казах в началото на тази статия, основният проблем е, че трябва да декларираме клавиатуратаShouldPersistTaps за всички родители ScrollView в йерархията. Това означава да се грижите за всеки FlatList, SectionList и ScrollView

За щастие има react-devtools, който показва дърво на всички рендерирани компоненти в приложението за реакция, и това също се ръководи в раздела Debugging на реагиращия нативен.

Можете да използвате самостоятелната версия на React Developer Tools за отстраняване на грешки в йерархията на компонентите React. За да го използвате, инсталирайте пакета reakct-devtools в световен мащаб:

npm инсталирайте -g react-devtools

Така че след търсене разбрах, че има секционен списък на йерархията, който трябва да има клавиатураShouldPersistTaps = 'винаги', докато не беше.

Като разгледах задълбочено кода, разбрах, че Modal се задейства от елемент SectionList. Вече знаем, че задействането на Modal в реагиращото native означава, че да вградим този Modal в йерархията на изгледа и да контролираме неговата видимост чрез състояние. Така че по отношение на изглед и компонент, този модал е вътре в секция Списък. И за ваш интерес, ако се потопите дълбоко в реагиращия естествен код, SectionList в моя случай е просто VirtualizedSectionList, който е VirtualizedList, който използва ScrollView

Така че след като декларирам keyShouldPersistTaps = 'винаги' в този раздел за списъка, проблемът е решен. Потребителят може просто да въведе някои стойности във въвеждането на текст, след което натисне веднъж на бутона за изпращане, за да изпрати данни. Бутонът заснема първо докосване на събития, вместо преглед на превъртане.

Къде да отида от тук

Решението за това за щастие е просто, тъй като включва фиксиране на нашия код, без да се налага да променяме реакционно-навигационния код. Но е добре да погледнете библиотечния код, за да знаете какво прави и да проследите откъде произхожда проблемът. Благодаря, че последвате толкова дълго проучване и се надявам да научите нещо.

Ако ви харесва тази публикация, помислете за посещение на другите ми статии и приложения