TextInput에 포커스가있을 때 키보드 뒤에서 창을 자동으로 슬라이드하는 방법은 무엇입니까?


90

네이티브 앱이 창을 자동으로 스크롤하는이 해킹을 보았지만 React Native에서 가장 좋은 방법이 궁금합니다. <TextInput>필드가 포커스를 받고 뷰에서 낮은 위치 에 놓이면 키보드가 텍스트 필드를 덮을 것입니다.

이 문제는 예제 UIExplorer의 TextInputExample.js보기 에서 볼 수 있습니다.

누구에게 좋은 해결책이 있습니까?


3
나는 이것을 Github 추적기에 문제로 추가하고 그것이 오는 것이 있는지 확인하는 것이 좋습니다. 이것은 매우 일반적인 불만이 될 것이기 때문입니다.
Colin Ramsay

답변:


83

2017 년 답변

KeyboardAvoidingView아마 지금 가야하는 가장 좋은 방법입니다. 여기 에서 문서를 확인 하십시오 . Keyboard개발자에게 애니메이션을 수행하는 데 더 많은 제어 권한을 부여 하는 모듈에 비해 정말 간단 합니다. Spencer Carli그의 매체 블로그 에서 가능한 모든 방법을 시연 했습니다 .

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
이것은 좋은 발견이지만, ScrollView가 TextInput이 화면에 있는지 확인하는 동안 ScrollView는 여전히 사용자가 스크롤 할 수없는 콘텐츠를 키보드 아래에 표시하고 있었기 때문에 충분하지 않았습니다. ScrollView 속성을 "keyboardDismissMode = on-drag"로 설정하면 사용자가 키보드를 닫을 수 있지만 키보드 아래에 스크롤 내용이 충분 하지 않으면 경험이 약간 혼란스러워집니다. ScrollView가 처음에 키보드 때문에 스크롤 만해야하고 바운싱을 비활성화하면 키보드를 닫고 아래 내용을 표시 할 방법이없는 것 같습니다
miracle2k

2
@ miracle2k-입력이 흐려질 때 (예 : 키보드가 닫힐 때) 스크롤보기 위치를 재설정하는 기능이 있습니다. 아마도 이것이 귀하의 경우에 도움이 될 수 있습니까?
Sherlock

2
@Sherlock 블러 스크롤 뷰 재설정 기능은 어떻게 생겼습니까? 그건 그렇고 굉장한 솔루션 :)
Ryan McDermott

8
최신 React Native 버전에서는 다음을 호출해야합니다. * import ReactNative from 'react-native'; * 호출하기 전에 * 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로 측정하므로 내 시야가 여전히 키보드 뒤에 숨겨져 있다는 것입니다.
Marc

내가 그것을 관리 할 수있는 유일한 방법은DeviceEventEmitter.addListener('keyboardDidShow', this.keyboardDidShow.bind(this));
Marc

12

우리는 React-native-keyboard-spacer 코드 형태와 @Sherlock의 코드를 결합하여 TextInput 요소가있는 모든 뷰를 감싸는 KeyboardHandler 구성 요소를 만들었습니다. 매력처럼 작동합니다! :-)

/**
 * 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;

iOS 시뮬레이터에서 키보드가 표시되지 않도록하는 쉽고 분명한 것이 있습니까?
seigel

1
Command + K (하드웨어-> 키보드-> 소프트웨어 Keboard 토글)를 사용해 보셨습니까?
John kendall 2015

여기에서 수정 된 버전을 사용해보십시오 : gist.github.com/dbasedow/f5713763802e27fbde3fc57a600adcd3 나는 이것이 깨지기 쉬운 imo라고 생각하는 시간 제한에 의존하지 않기 때문에 이것이 더 낫다고 생각합니다.
CoderDave

10

먼저 react-native-keyboardevents 를 설치해야합니다 .

  1. XCode의 프로젝트 탐색기에서 Libraries ➜ Add Files to [your project 's name]을 마우스 오른쪽 버튼으로 클릭하고 node_modules ➜ react-native-keyboardevents로 이동 한 다음 .xcodeproj 파일을 추가합니다.
  2. XCode의 프로젝트 탐색기에서 프로젝트를 선택합니다. keyboardevents 프로젝트의 lib * .a를 프로젝트의 빌드 단계에 추가합니다. ➜ 바이너리를 라이브러리와 연결하기 전에 프로젝트 탐색기에서 추가 한 .xcodeproj 파일을 클릭하고 빌드 설정 탭으로 이동합니다. '기본'대신 '모두'가 켜져 있는지 확인합니다. 헤더 검색 경로를 찾아서 $ (SRCROOT) /../ react-native / React 및 $ (SRCROOT) /../../ React를 모두 포함하는지 확인하십시오. 둘 다 재귀로 표시하십시오.
  3. 프로젝트 실행 (Cmd + R)

그런 다음 자바 스크립트 땅으로 돌아갑니다.

react-native-keyboardevents를 가져와야합니다.

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>

애니메이션 API를 사용하는 것도 가능하지만 간단하게하기 위해 애니메이션 후에 조정합니다.


1
코드 예제 / 애니메이션을 수행하는 방법에 대한 추가 정보를 보는 것은 멋질 것입니다. 점프가 꽤 불안정하고 "will show"및 "did show"방법으로 만 작업하면 키보드 애니메이션이 얼마나 오래 걸릴지 또는 "will show"에서 얼마나 높은지 추측하는 방법을 알 수 없습니다.
Stephen

2
react-native@0.11.0-rc는 이제 DeviceEventEmitter를 통해 키보드 이벤트 (예 : "keyboardWillShow")를 전송하므로 이러한 이벤트에 대한 리스너를 등록 할 수 있습니다. 그러나 ListView를 다룰 때 ListView의 scrollview에서 scrollTo ()를 호출하는 것이 더 잘 작동한다는 것을 알았습니다 this.listView.getScrollResponder().scrollTo(rowID * rowHeight); . onFocus 이벤트를 수신하면 행의 TextInput에서 호출됩니다.
Jed Lau

4
RCTDeviceEventEmitter가 작업을 수행하므로이 답변은 더 이상 유효하지 않습니다.
Sherlock


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

...

그것은 나를 위해 일했습니다. 보기는 기본적으로 키보드가 표시 될 때 축소되고 숨겨지면 다시 자랍니다.


또한이 솔루션은 잘 작동합니다 (RN 0.21.0) stackoverflow.com/a/35874233/3346628
pomo

self 대신 this.keyboardWillHide.bind (this) 사용
animekun

DeviceEventEmitter 대신 키보드 사용
Madura Pradeep


4

늦었을 수도 있지만 최상의 솔루션은 기본 라이브러리 인 IQKeyboardManager 를 사용하는 것입니다.

데모 프로젝트에서 iOS 프로젝트로 IQKeyboardManager 디렉토리를 드래그 앤 드롭하면됩니다. 그게 다야. 또한 isToolbar가 활성화 된 일부 valus 또는 AppDelegate.m 파일에서 텍스트 입력과 키보드 사이의 공간을 설정할 수 있습니다. 사용자 지정에 대한 자세한 내용은 내가 추가 한 GitHub 페이지 링크에 있습니다.


1
이것은 훌륭한 옵션입니다. ReactNative 용으로 래핑 된 버전 은 github.com/douglasjunior/react-native-keyboard-manager 를 참조하십시오 . 설치가 쉽습니다.
loevborg

3

TextInput.onFocus 및 ScrollView.scrollTo를 사용했습니다.

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

2

스티븐

높이가 키보드가 나타나는 것과 똑같은 속도로 움직이지 않아도 괜찮다면 LayoutAnimation을 사용하여 최소한 높이가 제자리에 들어 가지 않도록 할 수 있습니다. 예 :

react-native에서 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,
      },
    },
  },
};

최신 정보:

아래 @sherlock의 답변을 참조하십시오. react-native 0.11부터 키보드 크기 조정은 내장 기능을 사용하여 해결할 수 있습니다.


2

몇 가지 방법을 좀 더 간단한 것으로 결합 할 수 있습니다.

입력에 onFocus 리스너 연결

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

이것은 우리의 스크롤 뷰 (ref를 추가하는 것을 잊지 마십시오)에 초점을 맞춘 입력의 위치 인 200 (대략 키보드 크기)까지 스크롤하도록 지시합니다.

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

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

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

여기에서 스크롤 뷰를 다시 맨 위로 재설정합니다.

여기에 이미지 설명 입력


2
@render () 메서드를 제공해 주시겠습니까?
valerybodak

0

더 간단한 방법을 사용하고 있지만 아직 애니메이션이 적용되지 않았습니다. 기본적으로 0으로 설정된 "bumpedUp"이라는 구성 요소 상태가 있지만 textInput이 포커스를 받으면 다음과 같이 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 스타일이 시작되어 콘텐츠를 위로 이동합니다.

약간 해키하고 여백은 하드 코딩되어 있지만 작동합니다. :)


0

내 스크롤 뷰의 하단을 올리기 위해 brysgo 답변을 사용합니다. 그런 다음 onScroll을 사용하여 scrollview의 현재 위치를 업데이트합니다. 그런 다음이 React Native 를 찾았 습니다. 텍스트 입력의 위치를 ​​얻기 위해 요소 의 위치 가져 오기. 그런 다음 입력이 현재보기에 있는지 알아 내기 위해 간단한 수학을 수행합니다. 그런 다음 scrollTo를 사용하여 최소 금액과 여백을 이동합니다. 꽤 부드럽습니다. 스크롤 부분에 대한 코드는 다음과 같습니다.

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.