जब TextInput ध्यान केंद्रित किया है, तो कीबोर्ड के पीछे से खिड़की को ऑटो-स्लाइड कैसे करें?


90

मैंने विंडो हैक करने के लिए देशी ऐप्स के लिए इस हैक को देखा है, लेकिन रिएक्ट नेटिव में इसे करने का सबसे अच्छा तरीका सोच रहा हूं ... जब कोई <TextInput>फ़ील्ड फोकस हो जाता है और दृश्य में कम स्थित होता है, तो कीबोर्ड टेक्स्ट फ़ील्ड को कवर करेगा।

आप इस मुद्दे को उदाहरण UIExplorer के TextInputExample.jsदृश्य में देख सकते हैं ।

क्या किसी के पास कोई अच्छा उपाय है?


3
मैं इसे जीथब ट्रैकर पर एक मुद्दे के रूप में जोड़ने का सुझाव दूंगा और देखूंगा कि क्या इसमें से कुछ भी आता है, क्योंकि यह एक बहुत ही आम शिकायत है।
कॉलिन रामसे

जवाबों:


83

2017 उत्तर

KeyboardAvoidingViewशायद अब जाने के लिए सबसे अच्छा तरीका है। यहां डॉक्स देखें । यह Keyboardमॉड्यूल की तुलना में वास्तव में सरल है जो डेवलपर को एनिमेशन प्रदर्शन करने के लिए अधिक नियंत्रण देता है। स्पेंसर कारली ने अपने मध्यम ब्लॉग पर सभी संभव तरीकों का प्रदर्शन किया ।

2015 उत्तर

ऐसा करने का सही तरीका react-nativeबाहरी पुस्तकालयों की आवश्यकता नहीं है, देशी कोड का लाभ उठाता है, और इसमें एनिमेशन भी शामिल हैं।

पहले एक फ़ंक्शन को परिभाषित करें जो onFocusप्रत्येक TextInput(या किसी अन्य घटक जिसे आप स्क्रॉल करना चाहते हैं) के लिए घटना को संभालेंगे :

// Scroll a component into view. Just pass the component ref string.
inputFocused (refName) {
  setTimeout(() => {
    let scrollResponder = this.refs.scrollView.getScrollResponder();
    scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
      React.findNodeHandle(this.refs[refName]),
      110, //additionalOffset
      true
    );
  }, 50);
}

फिर, अपने रेंडर फ़ंक्शन में:

render () {
  return (
    <ScrollView ref='scrollView'>
        <TextInput ref='username' 
                   onFocus={this.inputFocused.bind(this, 'username')}
    </ScrollView>
  )
}

यह RCTDeviceEventEmitterकीबोर्ड ईवेंट और साइज़िंग के लिए उपयोग करता है, उपयोग करने वाले घटक की स्थिति को मापता है RCTUIManager.measureLayout, और इसमें आवश्यक सटीक स्क्रॉल आंदोलन की गणना करता है scrollResponderInputMeasureAndScrollToKeyboard

आप additionalOffsetअपने विशिष्ट UI डिज़ाइन की आवश्यकताओं को पूरा करने के लिए पैरामीटर के साथ खेलना चाहते हैं ।


5
यह एक अच्छी खोज है, लेकिन मेरे लिए यह पर्याप्त नहीं था, क्योंकि जब स्क्रॉलव्यू यह सुनिश्चित करेगा कि स्क्रीन पर TextInput है, तब भी स्क्रॉलव्यू उस कीबोर्ड के नीचे की सामग्री दिखा रहा था जिसे उपयोगकर्ता स्क्रॉल नहीं कर सकता था। स्क्रॉलव्यू प्रॉपर्टी "कीबोर्डडिज़्ममोड = ऑन-ड्रैग" सेट करने से उपयोगकर्ता को कीबोर्ड को खारिज करने की अनुमति मिलती है, लेकिन अगर कीबोर्ड के नीचे पर्याप्त स्क्रॉल सामग्री नहीं है , तो अनुभव थोड़ा परेशान है। यदि स्क्रॉलव्यू को केवल पहली बार कीबोर्ड के कारण स्क्रॉल करना पड़ता है, और आप बाउंसिंग को अक्षम कर देते हैं, तो कीबोर्ड को खारिज करने और नीचे दी गई सामग्री को दिखाने का कोई तरीका नहीं लगता है
miracle2k

2
@ miracle2k - मेरे पास एक फ़ंक्शन है जो इनपुट धुंधला होने पर स्क्रॉल दृश्य स्थिति को रीसेट करता है, अर्थात जब कीबोर्ड बंद हो जाता है। शायद यह आपके मामले में मदद कर सकता है?
शर्लक

2
@Sherlock क्या है कि धुंधला स्क्रॉल दृश्य रीसेट फ़ंक्शन की तरह दिखता है? इस तरह से भयानक समाधान :)
रयान मैकडरमोट

8
नए प्रतिक्रियाशील मूल संस्करणों में आपको कॉल करने की आवश्यकता होगी: * 'प्रतिक्रिया-मूल' से ReactNative आयात करें; * कॉल करने से पहले * ReactNative.findNodeHandle () * अन्यथा ऐप क्रैश हो जाएगा
amirfl

6
अब import {findNodeHandle} from 'react-native' stackoverflow.com/questions/37626851/…
antoine129

26

फेसबुक ने इस समस्या को हल करने के लिए देशी 0.29 प्रतिक्रिया में खुलकर KeyboardAvoidingView बात की। दस्तावेज़ीकरण और उपयोग का उदाहरण यहां पाया जा सकता है


32
KeyboardAvoidingView से सावधान रहें, इसका उपयोग करना आसान नहीं है। यह हमेशा वैसा व्यवहार नहीं करता जैसा कि आपको उम्मीद करनी चाहिए। दस्तावेज़ीकरण व्यावहारिक रूप से कोई नहीं है।
रेनैटो बैक

डॉक्टर और व्यवहार अब बेहतर हो रहे हैं
antoine129

मेरे पास जो समस्या है, वह यह है कि KeyboardAvoidingView मेरे iPhone 6 सिम्युलेटर पर कीबोर्ड की ऊँचाई को 65 के रूप में मापता है और इसलिए मेरा दृश्य अभी भी कीबोर्ड के पीछे छिपा हुआ है।
मार्क

केवल जिस तरह से मैं प्रबंधक कर सकता था वह एक नीचे के दृष्टिकोण के माध्यम से थाDeviceEventEmitter.addListener('keyboardDidShow', this.keyboardDidShow.bind(this));
मार्क

12

हमने एक कुंजीपटलहैंडलर घटक बनाने के लिए कोड फॉर्म रिएक्ट-देशी-कीबोर्ड-स्पेसर और @Sherlock से कुछ कोड को मिलाया, जिसे TextInput तत्वों के साथ किसी भी दृश्य के चारों ओर लपेटा जा सकता है। एक जादू की तरह काम करता है! :-)

/**
 * Handle resizing enclosed View and scrolling to input
 * Usage:
 *    <KeyboardHandler ref='kh' offset={50}>
 *      <View>
 *        ...
 *        <TextInput ref='username'
 *          onFocus={()=>this.refs.kh.inputFocused(this,'username')}/>
 *        ...
 *      </View>
 *    </KeyboardHandler>
 * 
 *  offset is optional and defaults to 34
 *  Any other specified props will be passed on to ScrollView
 */
'use strict';

var React=require('react-native');
var {
  ScrollView,
  View,
  DeviceEventEmitter,
}=React;


var myprops={ 
  offset:34,
}
var KeyboardHandler=React.createClass({
  propTypes:{
    offset: React.PropTypes.number,
  },
  getDefaultProps(){
    return myprops;
  },
  getInitialState(){
    DeviceEventEmitter.addListener('keyboardDidShow',(frames)=>{
      if (!frames.endCoordinates) return;
      this.setState({keyboardSpace: frames.endCoordinates.height});
    });
    DeviceEventEmitter.addListener('keyboardWillHide',(frames)=>{
      this.setState({keyboardSpace:0});
    });

    this.scrollviewProps={
      automaticallyAdjustContentInsets:true,
      scrollEventThrottle:200,
    };
    // pass on any props we don't own to ScrollView
    Object.keys(this.props).filter((n)=>{return n!='children'})
    .forEach((e)=>{if(!myprops[e])this.scrollviewProps[e]=this.props[e]});

    return {
      keyboardSpace:0,
    };
  },
  render(){
    return (
      <ScrollView ref='scrollView' {...this.scrollviewProps}>
        {this.props.children}
        <View style={{height:this.state.keyboardSpace}}></View>
      </ScrollView>
    );
  },
  inputFocused(_this,refName){
    setTimeout(()=>{
      let scrollResponder=this.refs.scrollView.getScrollResponder();
      scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
        React.findNodeHandle(_this.refs[refName]),
        this.props.offset, //additionalOffset
        true
      );
    }, 50);
  }
}) // KeyboardHandler

module.exports=KeyboardHandler;

कुछ भी आसान / स्पष्ट है जो कीबोर्ड को आईओएस सिम्युलेटर में दिखाने से बचाए रखेगा?
14

1
क्या आपने कमांड + के (हार्डवेयर-> कीबोर्ड-> टॉगल सॉफ्टवेयर कीबोर्ड) की कोशिश की?
जॉन केंडल

इसके संशोधित संस्करण को यहां देखें: gist.github.com/dbasedow/f5713763802e27fbde3fc57a600adcd3 मेरा मानना ​​है कि यह बेहतर है क्योंकि यह किसी भी समय पर निर्भर नहीं करता है, जो मुझे लगता है कि नाजुक है।
कोडवर्ड 29

10

सबसे पहले आपको प्रतिक्रिया-देशी-कीबोर्डव्यू स्थापित करने की आवश्यकता है ।

  1. XCode में, प्रोजेक्ट नेविगेटर में, लाइब्रेरीज़ पर राइट क्लिक करें ode अपने प्रोजेक्ट के नाम में फ़ाइलें जोड़ें] नोड_मॉड्यूल्स पर जाएं-प्रतिक्रिया-देशी-कीबोर्डव्यूज़ और .xcodeproj फ़ाइल जोड़ें।
  2. XCode में, प्रोजेक्ट नेविगेटर में, अपने प्रोजेक्ट का चयन करें। अपने प्रोजेक्ट के बिल्ड चरणों में कीबोर्ड से प्रोजेक्ट के लिए लीब * .ए को जोड़ें। L लिंक बायनरी विथ लाइब्रेरीज़ पर क्लिक करें। प्रोजेक्टकोड नेविगेटर में आपके द्वारा जोड़े गए .xcodeproj फ़ाइल और बिल्ड सेटिंग्स टैब पर जाएं। सुनिश्चित करें कि 'सभी' ('मूल' के बजाय) पर टॉगल किया गया है। हेडर खोज पथ खोजें और सुनिश्चित करें कि इसमें $ (SRCROOT) /../ प्रतिक्रिया-मूलक / प्रतिक्रिया और $ (SRCROOT) /../..// प्रतिक्रिया दोनों शामिल हैं - दोनों को पुनरावर्ती के रूप में चिह्नित करें।
  3. अपना प्रोजेक्ट चलाएं (Cmd + R)

फिर वापस जावास्क्रिप्ट भूमि में:

आपको प्रतिक्रिया-मूल-कीबोर्डवेट आयात करने की आवश्यकता है।

var KeyboardEvents = require('react-native-keyboardevents');
var KeyboardEventEmitter = KeyboardEvents.Emitter;

फिर आपके विचार में, कीबोर्ड स्थान के लिए कुछ स्थिति जोड़ें और कीबोर्ड घटनाओं को सुनने से अपडेट करें।

  getInitialState: function() {
    KeyboardEventEmitter.on(KeyboardEvents.KeyboardDidShowEvent, (frames) => {
      this.setState({keyboardSpace: frames.end.height});
    });
    KeyboardEventEmitter.on(KeyboardEvents.KeyboardWillHideEvent, (frames) => {
      this.setState({keyboardSpace: 0});
    });

    return {
      keyboardSpace: 0,
    };
  },

अंत में, सब कुछ के नीचे अपने रेंडर फ़ंक्शन में स्पेसर जोड़ें, जब यह आकार बढ़ता है तो यह आपके सामान को ऊपर उठाता है।

<View style={{height: this.state.keyboardSpace}}></View>

एनीमेशन एपीआई का उपयोग करना भी संभव है, लेकिन सादगी के लिए हम एनीमेशन के बाद ही समायोजित करते हैं।


1
कोड उदाहरण / एनीमेशन के बारे में कुछ और जानकारी देखने के लिए यह बहुत बढ़िया होगा। यह कूद काफी जानदार है, और केवल "शो" और "शो" के तरीकों के साथ काम करते हुए, मैं यह पता नहीं लगा सकता कि कीबोर्ड का एनीमेशन कितना लंबा होगा या "शो करेगा" से कितना लंबा है।
स्टीफन

2
प्रतिक्रिया-native@0.11.0-rc अब DeviceEventEmitter के माध्यम से कीबोर्ड ईवेंट्स (जैसे, "keyboardWillShow") भेजता है, जिससे आप इन ईवेंट के लिए श्रोताओं को पंजीकृत कर सकते हैं। जब एक ListView के साथ काम कर रहे हैं, हालांकि, मैंने पाया कि ListView के स्क्रॉलव्यू पर स्क्रॉलिंग () को कॉल करना बेहतर काम करता है: this.listView.getScrollResponder().scrollTo(rowID * rowHeight); इसे ऑन-लाइन ईवेंट प्राप्त होने पर एक पंक्ति के TextInput पर कॉल किया जाता है।
जेद लाउ

4
यह उत्तर अब मान्य नहीं है, क्योंकि RCTDeviceEventEmitter काम करता है।
शर्लक


6

इसे इस्तेमाल करे:

import React, {
  DeviceEventEmitter,
  Dimensions
} from 'react-native';

...

getInitialState: function() {
  return {
    visibleHeight: Dimensions.get('window').height
  }
},

...

componentDidMount: function() {
  let self = this;

  DeviceEventEmitter.addListener('keyboardWillShow', function(e: Event) {
    self.keyboardWillShow(e);
  });

  DeviceEventEmitter.addListener('keyboardWillHide', function(e: Event) {
      self.keyboardWillHide(e);
  });
}

...

keyboardWillShow (e) {
  let newSize = Dimensions.get('window').height - e.endCoordinates.height;
  this.setState({visibleHeight: newSize});
},

keyboardWillHide (e) {
  this.setState({visibleHeight: Dimensions.get('window').height});
},

...

render: function() {
  return (<View style={{height: this.state.visibleHeight}}>your view code here...</View>);
}

...

इसने मेरे लिए काम किया। कीबोर्ड प्रदर्शित होने पर दृश्य मूल रूप से सिकुड़ जाता है, और इसके छिपे होने पर फिर से बढ़ता है।


इसके अलावा, यह समाधान अच्छी तरह से काम करता है (आरएन 0.21.0) stackoverflow.com/a/35874233/3346628
pomo

इस का प्रयोग करें। स्वयं के बजाय .WillHide.bind (यह)
Animekun

DeviceEventEmitter के बजाय कीबोर्ड का उपयोग करें
Madura प्रदीप

4

सिर्फ जिक्र करना चाहता था, अब ए KeyboardAvoidingView आरएन में है। बस इसे आयात करें और इसे RN में किसी अन्य मॉड्यूल के रूप में उपयोग करें।

यहाँ RN पर कमिट का लिंक दिया गया है:

https://github.com/facebook/react-native/commit/8b78846a9501ef9c5ce9d1e18ee104bfae76af2e

यह 0.29.0 से उपलब्ध है

उन्होंने UIExplorer पर एक उदाहरण भी शामिल किया है।


4

शायद देर हो गई है, लेकिन सबसे अच्छा समाधान एक मूल पुस्तकालय, IQKeyboardManager का उपयोग करना है

बस खींचें और IQKeyboardManager निर्देशिका को डेमो प्रोजेक्ट से अपने iOS प्रोजेक्ट पर छोड़ दें। बस। इसके अलावा आप कुछ वेलस को सेटअप कर सकते हैं, जैसा कि टूलबार सक्षम है, या AppDelegate.m फाइल में टेक्स्ट इनपुट और कीबोर्ड के बीच का स्थान है। अनुकूलन के बारे में अधिक जानकारी GitHub पृष्ठ लिंक में है जिसे मैंने जोड़ा है।


1
यह एक उत्कृष्ट विकल्प है। इसके अलावा प्रतिक्रिया के लिए लिपटे एक संस्करण के लिए github.com/douglasjunior/react-native-keyboard-manager देखें - इसे स्थापित करना आसान है।
लोवॉर्ग

3

मैंने TextInput.onFocus और ScrollView.scrollTo का उपयोग किया।

...
<ScrollView ref="scrollView">
...
<TextInput onFocus={this.scrolldown}>
...
scrolldown: function(){
  this.refs.scrollView.scrollTo(width*2/3);
},

2

@Stephen

यदि आपको कोई आपत्ति नहीं है कि कीबोर्ड जिस दर पर दिखाई दे रहा है ठीक उसी दर पर, तो आप सिर्फ LayoutAnimation का उपयोग कर सकते हैं, ताकि कम से कम ऊँचाई जगह पर न उछले। जैसे

प्रतिक्रिया-मूल से LayoutAnimation आयात करें और अपने घटक में निम्न विधियाँ जोड़ें।

getInitialState: function() {
    return {keyboardSpace: 0};
  },
   updateKeyboardSpace: function(frames) {
    LayoutAnimation.configureNext(animations.layout.spring);
    this.setState({keyboardSpace: frames.end.height});
  },

  resetKeyboardSpace: function() {
    LayoutAnimation.configureNext(animations.layout.spring);
    this.setState({keyboardSpace: 0});
  },

  componentDidMount: function() {
    KeyboardEventEmitter.on(KeyboardEvents.KeyboardDidShowEvent, this.updateKeyboardSpace);
    KeyboardEventEmitter.on(KeyboardEvents.KeyboardWillHideEvent, this.resetKeyboardSpace);
  },

  componentWillUnmount: function() {
    KeyboardEventEmitter.off(KeyboardEvents.KeyboardDidShowEvent, this.updateKeyboardSpace);
    KeyboardEventEmitter.off(KeyboardEvents.KeyboardWillHideEvent, this.resetKeyboardSpace);
  },

कुछ उदाहरण एनिमेशन हैं (मैं ऊपर स्प्रिंग का उपयोग कर रहा हूं):

var animations = {
  layout: {
    spring: {
      duration: 400,
      create: {
        duration: 300,
        type: LayoutAnimation.Types.easeInEaseOut,
        property: LayoutAnimation.Properties.opacity,
      },
      update: {
        type: LayoutAnimation.Types.spring,
        springDamping: 400,
      },
    },
    easeInEaseOut: {
      duration: 400,
      create: {
        type: LayoutAnimation.Types.easeInEaseOut,
        property: LayoutAnimation.Properties.scaleXY,
      },
      update: {
        type: LayoutAnimation.Types.easeInEaseOut,
      },
    },
  },
};

अपडेट करें:

नीचे दिए गए @ शर्लक के उत्तर को देखें, प्रतिक्रिया-देशी 0.11 के रूप में कीबोर्ड का आकार बदलना कार्यक्षमता में निर्मित का उपयोग करके हल किया जा सकता है।


2

आप कुछ सरल में कुछ तरीकों को जोड़ सकते हैं।

अपने इनपुट पर एक ऑनफोकस श्रोता संलग्न करें

<TextInput ref="password" secureTextEntry={true} 
           onFocus={this.scrolldown.bind(this,'password')}
/>

हमारी स्क्रॉल डाउन विधि कुछ इस प्रकार है:

scrolldown(ref) {
    const self = this;
    this.refs[ref].measure((ox, oy, width, height, px, py) => {
        self.refs.scrollView.scrollTo({y: oy - 200});
    });
}

यह हमारे स्क्रॉल दृश्य को बताता है (याद करने के लिए एक रेफरी जोड़ने के लिए) स्क्रॉल करने के लिए नीचे हमारे केंद्रित इनपुट की स्थिति में - 200 (यह लगभग कीबोर्ड का आकार है)

componentWillMount() {
    this.keyboardDidHideListener = Keyboard.addListener(
      'keyboardWillHide', 
      this.keyboardDidHide.bind(this)
    )
}

componentWillUnmount() {
    this.keyboardDidHideListener.remove()
}

keyboardDidHide(e) {
    this.refs.scrollView.scrollTo({y: 0});
}

यहां हम अपने स्क्रॉल दृश्य को शीर्ष पर वापस सेट करते हैं,

यहां छवि विवरण दर्ज करें


2
@ क्या आप कृपया अपनी रेंडर () विधि प्रदान करते हैं?
वल्लरीबोडक

0

मैं एक सरल विधि का उपयोग कर रहा हूं, लेकिन यह अभी तक एनिमेटेड नहीं है। मेरे पास एक घटक स्थिति है जिसे "bumpedUp" कहा जाता है, जिसे मैं 0 में डिफ़ॉल्ट करता हूं, लेकिन 1 पर सेट होने पर टेक्स्टइन्पुट इस तरह केंद्रित हो जाता है:

मेरे textInput पर:

onFocus={() => this.setState({bumpedUp: 1})}
onEndEditing={() => this.setState({bumpedUp: 0})}

मेरे पास शैली भी है जो उस स्क्रीन पर हर चीज़ के रैपिंग कंटेनर को एक निचला मार्जिन और नकारात्मक शीर्ष मार्जिन देता है, जैसे:

mythingscontainer: {
  flex: 1,
  justifyContent: "center",
  alignItems: "center",
  flexDirection: "column",
},
bumpedcontainer: {
  marginBottom: 210,
  marginTop: -210,
},

और फिर रैपिंग कंटेनर पर, मैंने शैलियों को इस तरह सेट किया:

<View style={[styles.mythingscontainer, this.state.bumpedUp && styles.bumpedcontainer]}>

तो, जब "bumpedUp" स्थिति 1 पर सेट हो जाती है, तो bumpedcontainer शैली सामग्री को अंदर ले जाती है और ऊपर जाती है।

Kinda हैकी और मार्जिन हार्डकोड हैं, लेकिन यह काम करता है :)


0

मैं अपने स्क्रोलव्यू के निचले हिस्से को ऊपर उठाने के लिए ब्रिस्गो उत्तर का उपयोग करता हूं। फिर मैं स्क्रोलव्यू की वर्तमान स्थिति को अपडेट करने के लिए onScroll का उपयोग करता हूं। मैंने तब यह रिएक्ट नेटिव पाया : टेक्स्टिनपुट की स्थिति प्राप्त करने के लिए एक तत्व की स्थिति प्राप्त करना। मैं तब कुछ सरल गणित करता हूं ताकि पता चल सके कि इनपुट वर्तमान दृश्य में है। फिर मैं न्यूनतम राशि और एक मार्जिन को स्थानांतरित करने के लिए स्क्रोल्टो का उपयोग करता हूं। यह बहुत चिकनी है। स्क्रॉलिंग भाग के लिए कोड का उपयोग करता है:

            focusOn: function(target) {
                return () => {
                    var handle = React.findNodeHandle(this.refs[target]);
                    UIManager.measureLayoutRelativeToParent( handle, 
                        (e) => {console.error(e)}, 
                        (x,y,w,h) => {
                            var offs = this.scrollPosition + 250;
                            var subHeaderHeight = (Sizes.width > 320) ? Sizes.height * 0.067 : Sizes.height * 0.077;
                            var headerHeight = Sizes.height / 9;
                            var largeSpace = (Sizes.height - (subHeaderHeight + headerHeight));
                            var shortSpace = largeSpace - this.keyboardOffset;
                            if(y+h >= this.scrollPosition + shortSpace) {
                                this.refs.sv.scrollTo(y+h - shortSpace + 20);
                            }
                            if(y < this.scrollPosition) this.refs.sv.scrollTo(this.scrollPosition - (this.scrollPosition-y) - 20 );
                        }
                     );
                };
            },

0

मैं भी इस सवाल को पूरा करता हूं। अंत में, मैं प्रत्येक दृश्य की ऊंचाई को परिभाषित करके इसे हल करता हूं, जैसे:

<Navigator
    ...
    sceneStyle={{height: **}}
/>

और, मैं वास्तविक ऊंचाई प्राप्त करने के लिए एक तृतीय-पक्ष मॉड्यूल https://github.com/jaysoo/react-native-extra-dimensions-android का उपयोग करता हूं ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.