Wie schiebe ich das Fenster automatisch hinter der Tastatur heraus, wenn TextInput den Fokus hat?

90

Ich habe diesen Hack für native Apps gesehen, um automatisch durch das Fenster zu scrollen, habe mich aber gefragt, wie ich es in React Native am besten machen kann ... Wenn ein <TextInput>Feld scharf wird und tief in der Ansicht positioniert ist, verdeckt die Tastatur das Textfeld.

Sie können dieses Problem in der TextInputExample.jsAnsicht von UIExplorer sehen.

Hat jemand eine gute Lösung?

McG
quelle
3
Ich würde vorschlagen, dies als Problem auf dem Github-Tracker hinzuzufügen und zu prüfen, ob etwas daraus wird, da dies eine sehr häufige Beschwerde sein wird.
Colin Ramsay

Antworten:

83

2017 Antwort

Das KeyboardAvoidingViewist wahrscheinlich der beste Weg, um jetzt zu gehen. Schauen Sie sich die Dokumente hier an . Es ist wirklich einfach im Vergleich zu KeyboardModulen, die Entwicklern mehr Kontrolle beim Ausführen von Animationen geben. Spencer Carli hat in seinem mittleren Blog alle möglichen Wege aufgezeigt .

Antwort 2015

Die richtige Vorgehensweise react-nativeerfordert keine externen Bibliotheken, nutzt nativen Code und enthält Animationen.

Definieren Sie zunächst eine Funktion, die das onFocusEreignis für jede TextInput(oder jede andere Komponente, zu der Sie einen Bildlauf durchführen möchten) behandelt:

// 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);
}

Dann in Ihrer Renderfunktion:

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

Dies verwendet die RCTDeviceEventEmitterfür Tastaturereignisse und die Größe, misst die Position der Komponente mithilfe RCTUIManager.measureLayoutund berechnet die genaue Bildlaufbewegung, die in erforderlich ist scrollResponderInputMeasureAndScrollToKeyboard.

Möglicherweise möchten Sie mit dem additionalOffsetParameter herumspielen, um ihn an die Anforderungen Ihres spezifischen UI-Designs anzupassen.

Sherlock
quelle
5
Dies ist eine schöne Entdeckung, aber für mich war es nicht genug, denn während die ScrollView sicherstellt, dass der TextInput auf dem Bildschirm angezeigt wird, zeigte die ScrollView immer noch Inhalte unter der Tastatur an, zu denen der Benutzer nicht scrollen konnte. Durch Festlegen der ScrollView-Eigenschaft "keyboardDismissMode = on-drag" kann der Benutzer die Tastatur schließen. Wenn sich jedoch nicht genügend Bildlaufinhalt unter der Tastatur befindet, ist die Erfahrung etwas unangenehm. Wenn die ScrollView nur wegen der Tastatur scrollen muss und Sie das Bouncen deaktivieren, scheint es keine Möglichkeit zu geben, die Tastatur zu schließen und den folgenden Inhalt
anzuzeigen
2
@ miracle2k - Ich habe eine Funktion, die die Position der Bildlaufansicht zurücksetzt, wenn eine Eingabe unscharf ist, dh wenn die Tastatur geschlossen wird. Vielleicht könnte dies in Ihrem Fall helfen?
Sherlock
2
@Sherlock Wie sieht diese Funktion zum Zurücksetzen der unscharfen Bildlaufansicht aus? Tolle Lösung übrigens :)
Ryan McDermott
8
In neueren React Native-Versionen müssen Sie Folgendes aufrufen: * ReactNative aus 'react-native' importieren; * vor dem Aufruf * ReactNative.findNodeHandle () * Andernfalls stürzt die App ab
amirfl
6
Jetzt import {findNodeHandle} from 'react-native' stackoverflow.com/questions/37626851/…
antoine129
26

Facebook Open Sourcing KeyboardAvoidingView in reagieren native 0.29, um dieses Problem zu lösen. Dokumentation und Anwendungsbeispiel finden Sie hier .

Farwayer
quelle
32
Hüten Sie sich vor KeyboardAvoidingView, es ist einfach nicht einfach zu bedienen. Es verhält sich nicht immer so, wie Sie es erwarten. Dokumentation ist praktisch nicht vorhanden.
Renato Zurück
doc und Verhalten werden jetzt besser
antoine129
Das Problem, das ich habe, ist, dass das KeyboardAvoidingView die Tastaturhöhe auf meinem iPhone 6-Simulator als 65 misst und meine Ansicht daher immer noch hinter der Tastatur verborgen ist.
Marc
Ich konnte es nur durch einen Bottompadding-Ansatz verwalten, der vonDeviceEventEmitter.addListener('keyboardDidShow', this.keyboardDidShow.bind(this));
Marc,
12

Wir haben einen Teil des Codeformulars "React-Native-Keyboard-Spacer" und den Code von @Sherlock kombiniert, um eine KeyboardHandler-Komponente zu erstellen, die mit TextInput-Elementen um jede Ansicht gewickelt werden kann. Klappt wunderbar! :-)

/**
 * 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;
John Kendall
quelle
Gibt es etwas Einfaches / Offensichtliches, das die Tastatur davon abhält, in einem iOS-Simulator angezeigt zu werden?
Seigel
1
Haben Sie Command + K (Hardware-> Tastatur-> Software-Keboard umschalten) ausprobiert?
John Kendall
Probieren Sie die modifizierte Version hier aus: gist.github.com/dbasedow/f5713763802e27fbde3fc57a600adcd3 Ich glaube, das ist besser, weil es keine Zeitüberschreitungen gibt, die ich für fragil halte.
CoderDave
10

Zuerst müssen Sie React-Native-Keyboardevents installieren .

  1. Klicken Sie in XCode im Projektnavigator mit der rechten Maustaste auf Bibliotheken ➜ Dateien zu [dem Namen Ihres Projekts hinzufügen]. Gehen Sie zu node_modules ➜ React-Native-Keyboardevents und fügen Sie die .xcodeproj-Datei hinzu
  2. Wählen Sie in XCode im Projektnavigator Ihr Projekt aus. Fügen Sie die lib * .a aus dem keyboardevents-Projekt zu den Build-Phasen Ihres Projekts hinzu. ➜ Binär mit Bibliotheken verknüpfen Klicken Sie auf die zuvor im Projektnavigator hinzugefügte .xcodeproj-Datei und wechseln Sie zur Registerkarte Build-Einstellungen. Stellen Sie sicher, dass 'Alle' aktiviert ist (anstelle von 'Basic'). Suchen Sie nach Header-Suchpfaden und stellen Sie sicher, dass sie sowohl $ (SRCROOT) /../ react-native / React als auch $ (SRCROOT) /../../ React enthalten - markieren Sie beide als rekursiv.
  3. Führen Sie Ihr Projekt aus (Cmd + R)

Dann zurück in Javascript Land:

Sie müssen die React-Native-Keyboardevents importieren.

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

Fügen Sie dann in Ihrer Ansicht einen Status für den Tastaturbereich hinzu und aktualisieren Sie ihn, nachdem Sie die Tastaturereignisse abgehört haben.

  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,
    };
  },

Fügen Sie Ihrer Renderfunktion schließlich unter allem einen Abstandshalter hinzu, damit Ihre Inhalte bei zunehmender Größe beschädigt werden.

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

Es ist auch möglich, die Animations-API zu verwenden, aber der Einfachheit halber passen wir sie einfach nach der Animation an.

Brysgo
quelle
1
Es wäre fantastisch, ein Codebeispiel / weitere Informationen zur Animation zu sehen. Der Sprung ist ziemlich ruckelig und wenn ich nur mit den Methoden "will show" und "did show" arbeite, kann ich nicht genau herausfinden, wie lange die Tastaturanimation dauern wird oder wie hoch sie von "will show" ist.
Stephen
2
[email protected] sendet jetzt Tastaturereignisse (z. B. "keyboardWillShow") über den DeviceEventEmitter, sodass Sie Listener für diese Ereignisse registrieren können. Beim Umgang mit einer ListView stellte ich jedoch fest, dass das Aufrufen von scrollTo () in der Scrollview der ListView besser funktioniert: this.listView.getScrollResponder().scrollTo(rowID * rowHeight); Dies wird im TextInput einer Zeile aufgerufen, wenn ein onFocus-Ereignis empfangen wird.
Jed Lau
4
Diese Antwort ist nicht mehr gültig, da der RCTDeviceEventEmitter die Aufgabe erledigt.
Sherlock
6

Versuche dies:

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>);
}

...

Es hat bei mir funktioniert. Die Ansicht wird grundsätzlich verkleinert, wenn die Tastatur angezeigt wird, und wächst wieder nach, wenn sie ausgeblendet ist.

pomo
quelle
Auch diese Lösung funktioniert gut (RN 0.21.0) stackoverflow.com/a/35874233/3346628
pomo
benutze this.keyboardWillHide.bind (this) anstelle von self
animekun
Verwenden Sie stattdessen Tastatur DeviceEventEmitter
Madura Pradeep
4

Vielleicht ist es zu spät, aber die beste Lösung ist die Verwendung einer nativen Bibliothek, IQKeyboardManager

Ziehen Sie das IQKeyboardManager-Verzeichnis einfach per Drag & Drop aus dem Demo-Projekt in Ihr iOS-Projekt. Das ist es. Sie können auch einen Wert festlegen, wenn isToolbar aktiviert ist, oder den Abstand zwischen Texteingabe und Tastatur in der Datei AppDelegate.m. Weitere Details zur Anpassung finden Sie auf dem GitHub-Seitenlink, den ich hinzugefügt habe.

Adrian Zghibarta
quelle
1
Dies ist eine ausgezeichnete Option. Unter github.com/douglasjunior/react-native-keyboard-manager finden Sie auch eine für ReactNative verpackte Version - die Installation ist einfach.
Loevborg
2

Ich habe TextInput.onFocus und ScrollView.scrollTo verwendet.

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

@ Stephen

Wenn es Ihnen nichts ausmacht, wenn die Höhe nicht genau mit der Geschwindigkeit animiert wird, mit der die Tastatur angezeigt wird, können Sie einfach LayoutAnimation verwenden, damit zumindest die Höhe nicht an ihren Platz springt. z.B

Importieren Sie LayoutAnimation aus React-Native und fügen Sie Ihrer Komponente die folgenden Methoden hinzu.

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);
  },

Einige Beispielanimationen sind (ich verwende die Feder oben):

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,
      },
    },
  },
};

AKTUALISIEREN:

Siehe die Antwort von @ sherlock weiter unten. Ab reaktionsfähigem 0.11 kann die Größenänderung der Tastatur mithilfe der integrierten Funktionalität gelöst werden.

Deanmcpherson
quelle
2

Sie können einige der Methoden zu etwas Einfacherem kombinieren.

Schließen Sie einen onFocus-Listener an Ihre Eingänge an

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

Unsere Scroll-Down-Methode sieht ungefähr so ​​aus:

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

Dies weist unsere Bildlaufansicht (denken Sie daran, einen Verweis hinzuzufügen) an, bis zur Position unserer fokussierten Eingabe zu scrollen - 200 (es entspricht ungefähr der Größe der Tastatur).

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

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

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

Hier setzen wir unsere Bildlaufansicht wieder nach oben zurück,

Geben Sie hier die Bildbeschreibung ein

Nath
quelle
2
@ Könnten Sie bitte Ihre render () -Methode angeben?
Valerybodak
0

Ich verwende eine einfachere Methode, aber sie ist noch nicht animiert. Ich habe einen Komponentenstatus namens "dumpedUp", den ich standardmäßig auf 0 setze, aber auf 1 setze, wenn der textInput den Fokus erhält, wie folgt:

Auf meinem textInput:

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

Ich habe auch einen Stil, der dem Verpackungsbehälter von allem auf diesem Bildschirm einen unteren Rand und einen negativen oberen Rand gibt, wie folgt:

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

Und dann habe ich auf dem Verpackungsbehälter die Stile wie folgt eingestellt:

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

Wenn also der Status "dumpedUp" auf 1 gesetzt wird, wird der Bumpedcontainer-Stil aktiviert und der Inhalt nach oben verschoben.

Ein bisschen hacky und die Ränder sind hartcodiert, aber es funktioniert :)

Stirman
quelle
0

Ich benutze brysgo answer, um den unteren Rand meiner Bildlaufansicht anzuheben. Dann verwende ich den onScroll, um die aktuelle Position der Bildlaufansicht zu aktualisieren. Ich fand dann diese React Native: Die Position eines Elements ermitteln , um die Position der Texteingabe zu ermitteln. Ich rechne dann einfach nach, um herauszufinden, ob sich die Eingabe in der aktuellen Ansicht befindet. Dann benutze ich scrollTo, um den Mindestbetrag plus einen Rand zu verschieben. Es ist ziemlich glatt. Hier ist der Code für den Bildlaufabschnitt:

            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 );
                        }
                     );
                };
            },
aintnorest
quelle
0

Ich treffe auch diese Frage. Schließlich löse ich es, indem ich die Höhe jeder Szene definiere, wie zum Beispiel:

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

Außerdem verwende ich ein Drittanbieter-Modul https://github.com/jaysoo/react-native-extra-dimensions-android , um die tatsächliche Höhe zu ermitteln.

Renguang Dong
quelle