Спрятать клавиатуру в реагирующую


449

Если я нажимаю на ввод текста, я хочу иметь возможность нажать где-нибудь еще, чтобы снова закрыть клавиатуру (но не клавишу возврата). Я не нашел ни малейшей информации об этом во всех учебниках и постах блога, которые я прочитал.

Этот базовый пример до сих пор не работает для меня с реакцией родной 0.4.2 в симуляторе. Не могу попробовать это на моем iPhone еще.

<View style={styles.container}>
    <Text style={styles.welcome}>
      Welcome to React Native!
    </Text>
    <Text style={styles.instructions}>
      To get started, edit index.ios.js
    </Text>
    <Text style={styles.instructions}>
      Press Cmd+R to reload,{'\n'}
      Cmd+D or shake for dev menu
    </Text>
    <TextInput
      style={{height: 40, borderColor: 'gray', borderWidth: 1}}
      onEndEditing={this.clearFocus}
    />
  </View>

3
Попробуйте blur (): github.com/facebook/react-native/issues/113
herbertD

Правильный ответ должен быть от Эрика Кима ниже. Ответ ScrollView (установите для scrollable значение false) не идеален, если у вас несколько вводов текста, он не позволяет переходить от ввода текста к вводу текста без отклонения клавиатуры.
hippofluff

2
Для тех, кто хочет найти решение для всего приложения, см. Ответ @ Скоттмаса ниже (ссылка: stackoverflow.com/a/49825223/1138273 )
Hamed

Ответы:


564

Проблема, связанная с тем, что клавиатура не удаляется, становится более серьезной keyboardType='numeric', поскольку у вас нет возможности отменить ее.

Замена View на ScrollView не является правильным решением, так как если у вас есть несколько textInputs или buttons, нажатие на них, когда клавиатура поднята, приведет к отключению только клавиатуры.

Правильный способ заключается в том, чтобы инкапсулировать View TouchableWithoutFeedbackи вызыватьKeyboard.dismiss()

РЕДАКТИРОВАТЬ: теперь вы можете использовать ScrollViewс, keyboardShouldPersistTaps='handled'чтобы уволить клавиатуру только тогда, когда касание не обрабатывается дочерними (то есть, нажав на другие текстовые входы или кнопки)

Если у тебя есть

<View style={{flex: 1}}>
    <TextInput keyboardType='numeric'/>
</View>

Измените это на

<ScrollView contentContainerStyle={{flexGrow: 1}}
  keyboardShouldPersistTaps='handled'
>
  <TextInput keyboardType='numeric'/>
</ScrollView>

или

import {Keyboard} from 'react-native'

<TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
    <View style={{flex: 1}}>
        <TextInput keyboardType='numeric'/>
    </View>
</TouchableWithoutFeedback>

РЕДАКТИРОВАТЬ: Вы также можете создать компонент высшего порядка, чтобы закрыть клавиатуру.

import React from 'react';
import { TouchableWithoutFeedback, Keyboard, View } from 'react-native';

const DismissKeyboardHOC = (Comp) => {
  return ({ children, ...props }) => (
    <TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
      <Comp {...props}>
        {children}
      </Comp>
    </TouchableWithoutFeedback>
  );
};
const DismissKeyboardView = DismissKeyboardHOC(View)

Просто используйте это так

...
render() {
    <DismissKeyboardView>
        <TextInput keyboardType='numeric'/>
    </DismissKeyboardView>
}

ПРИМЕЧАНИЕ. accessible={false}Требуется, чтобы форма ввода оставалась доступной через VoiceOver. Люди с нарушениями зрения будут вам благодарны!


28
Это замечательно, единственный комментарий, который у меня есть, это то, что вы могли бы использовать официальный API-интерфейс Keyboard в RN и вызывать Keyboard.dismiss () вместо вызова некоторой внутренней утилиты RN dismissKeyboard (). Но оба работают нормально в настоящее время.
Павле Лекич

@PavleLekic Извините за задержку, я обновил ответ вместе с методом HOC
Эрик Ким,

3
Это прекрасно работает. Мне пришлось немного изменить синтаксис для определения функции стрелки, чтобы удалить неожиданную ошибку токена в RN: const DismissKeyboardHOC = (Comp) => {
jwinn

2
Я не могу получить onPressдля TouchableWithoutFeedbackстрельбы, независимо от того, что я пытаюсь
Брэд Райан

1
Зачем создавать HoC и просто добавить это в корень дерева приложений /
Дмитрий Коприва

248

Это только что было обновлено и задокументировано ! Нет больше скрытых трюков.

import { Keyboard } from 'react-native'

// Hide that keyboard!
Keyboard.dismiss()

https://github.com/facebook/react-native/pull/9925


4
Спасибо за добавление этого. Я надеюсь, что ваш ответ поднимется до вершины. Я почти пропустил это и использовал устаревшее решение.
Овчарка

2
Пинг @MrMuetze, чтобы изменить это как правильный ответ
jehna1

8
Это не должно быть выбрано как лучший ответ. Вопрос состоит в том, как убрать клавиатуру при постукивании за ее пределами. Этот ответ просто предоставляет API для этого, тогда как фактический лучший ответ обеспечивает работоспособную реализацию.
jskidd3

Вы можете использовать следующую библиотеку: KeyboardAwareScrollView
Алехандро Гонсалес

97

используйте это для таможенного увольнения

var dismissKeyboard = require('dismissKeyboard');

var TestView = React.createClass({
    render: function(){
        return (
            <TouchableWithoutFeedback 
                onPress={dismissKeyboard}>
                <View />
            </TouchableWithoutFeedback>
        )
    }
})

Это не задокументировано, но образцы в github repo-репозитории используют его несколько раз.
syarul

7
Интересно, для тех, кому интересно, откуда взялась эта библиотека Utility в React Native. Вот источник: github.com/facebook/react-native/blob/master/Libraries/…
Джошуа Пинтер

1
По какой-то причине это не сработало, когда я попыталсяreact-native-search-bar
Питер Дж

Это точный эквивалент Keyboard.dismiss, который является предпочтительным, поскольку задокументирован. github.com/facebook/react-native/blob/…
Рикардо Стювен,

88

Используйте React Native's Keyboard.dismiss()

Обновленный ответ

React Native предоставил статический dismiss()метод для Keyboard, поэтому обновленный метод:

import { Keyboard } from 'react-native'; 

Keyboard.dismiss()

Оригинальный ответ

Используйте React Native's dismissKeyboardLibrary.

У меня была очень похожая проблема, и я чувствовал, что я единственный, кто ее не получил.

ScrollViews

Если у вас есть ScrollViewили что-то, что унаследовано от него, например ListView, вы можете добавить опору, которая автоматически отклонит клавиатуру на основе событий нажатия или перетаскивания.

Опора есть keyboardDismissModeи может иметь значение none, interactiveили on-drag. Вы можете прочитать больше об этом здесь .

Регулярные просмотры

Если у вас есть что-то отличное от a, ScrollViewи вы хотите, чтобы какие-либо нажатия отклонили клавиатуру, вы можете использовать простую утилиту TouchableWithoutFeedbackи onPressиспользовать служебную библиотеку React Native, dismissKeyboardчтобы отклонить клавиатуру за вас.

В вашем примере вы можете сделать что-то вроде этого:

var DismissKeyboard = require('dismissKeyboard'); // Require React Native's utility library.

// Wrap your view with a TouchableWithoutFeedback component like so.

<View style={styles.container}>

  <TouchableWithoutFeedback onPress={ () => { DismissKeyboard() } }>

    <View>

      <Text style={styles.welcome}>
        Welcome to React Native!
      </Text>

      <Text style={styles.instructions}>
        To get started, edit index.ios.js
      </Text>

      <Text style={styles.instructions}>
        Press Cmd+R to reload,{'\n'}
        Cmd+D or shake for dev menu
      </Text>

      <TextInput style={{height: 40, borderColor: 'gray', borderWidth: 1}} />

    </View>

  </TouchableWithoutFeedback>

</View>

Примечание: TouchableWithoutFeedbackможет иметь только одного ребенка, поэтому вам нужно обернуть все под ним в один, Viewкак показано выше.


4
Реагировать Native воздействию статического dismiss()метода на клавиатуре, поэтому обновленный метод: import { Keyboard } from 'react-native'; Keyboard.dismiss().
Джошуа Пинтер

1
у меня есть клавиатура, которая висит вокруг, так как я сделал перезагрузку, сосредоточившись на поле ввода. в этом случае Keyboard.dismiss()ничего не делает, поскольку его реализация зависит от того, фокусируется ли он на вводе, которым я больше не являюсь.
pstanton

@pstanton Что ты должен был сделать, чтобы убрать клавиатуру?
Джошуа Пинтер

Я никак не мог найти, поэтому закрыл силой!
pstanton

41

Простой ответ - использовать ScrollView вместо View и установить для свойства scrollable значение false (хотя, возможно, потребуется изменить некоторые стили).

Таким образом, клавиатура отключается, как только я нажимаю куда-то еще. Это может быть проблемой с реакцией-нативной, но события касания, кажется, обрабатываются только с ScrollViews, что приводит к описанному поведению.

Редактировать: благодаря jllodra. Обратите внимание, что если вы нажмете непосредственно на другой ввод текста, а затем снаружи, клавиатура по-прежнему не будет скрыта.


1
Он работает с scrollview, но все же есть некоторые случаи, когда я могу нажать кнопку, чтобы изменить вид, используя навигатор, а клавиатура все еще остается в нижней части и приходится вручную нажимать клавишу возврата, чтобы закрыть ее :(
Piyush Chauhan

1
Клавиатура скрывается, когда вы нажимаете вне TextInput, но если (вместо нажатия снаружи) вы нажимаете на другой TextInput и, наконец, нажимаете снаружи, клавиатура не скрывается. Проверено на 0.6.0.
Джллодра

Сейчас я вижу другое поведение. Нажатие вне TextInput скрывает клавиатуру, даже если я нажимаю непосредственно на другой TextInput - что является проблемой, потому что вам нужно дважды нажать на другой TextInput, чтобы иметь возможность вводить его! Вздох. (с 0,19 RN)
Лейн Реттиг

1
Вы можете установить scrollable в true и использовать keyboardShouldPersistTaps = {'handled'} и keyboardDismissMode = {'on-drag'} для достижения того же эффекта
Эрик Винер

у меня работал только скроллвью, я не знаю почему, принятый ответ, когда я ввожу цифровую клавиатуру, отклоняется
Ивон Хейн

33

Вы можете импортировать keyboard из реактивной системы, как показано ниже:

import { Keyboard } from 'react-native';

и в вашем коде может быть что-то вроде этого:

render() {
    return (
      <TextInput
        onSubmit={Keyboard.dismiss}
      />
    );
  }

статическое увольнение ()

Отклоняет активную клавиатуру и убирает фокус.


Мне не нужно static dismiss(). Я только добавил Keyboard.dismiss()к своему методу onSubmit (гдеonSubmitEditing={() => {this.onSubmit()}})
SherylHohman

30

Я новичок в React, и столкнулся с той же проблемой при создании демонстрационного приложения. Если вы используете onStartShouldSetResponderопору (описанную здесь ), вы можете взять прикосновения к старому React.View. Любопытно услышать мнение более опытных Реакторов об этой стратегии / если есть лучшая, но вот что сработало для меня:

containerTouched(event) {
  this.refs.textInput.blur();
  return false;
}

render() {
  <View onStartShouldSetResponder={this.containerTouched.bind(this)}>
    <TextInput ref='textInput' />
  </View>
}

2 вещи, чтобы отметить здесь. Во- первых, как описано здесь , есть еще не способ закончить редактирование всех подвидов, поэтому мы должны обратиться к TextInputнепосредственно размывать его. Во-вторых, onStartShouldSetResponderон перехватывается другими сенсорными элементами управления поверх него. Таким образом, нажатие на и TouchableHighlightт. Д. (Включая другое TextInput) в представлении контейнера не вызовет событие. Тем не менее, нажатие на Imageвнутри контейнера будет по-прежнему отклонять клавиатуру.


Это определенно работает. Но, как вы сказали, мне тоже интересно, если это правильный путь. Надеюсь, они скоро это решат ( github.com/facebook/react-native/issues/113 )
Mutp

Отлично, это сработало для меня. Мой вид прокрутки не работал с сенсорными методами! Спасибо!
Джеймс Трики

24

Используйте ScrollViewвместо Viewи установите для keyboardShouldPersistTapsатрибута значение false.

<ScrollView style={styles.container} keyboardShouldPersistTaps={false}>
    <TextInput
        placeholder="Post Title"
        onChange={(event) => this.updateTitle(event.nativeEvent.text)}
        style={styles.default}/>
 </ScrollView>

Согласно документации, keyboardShouldPersistTapsатрибут по умолчанию имеет значение false при использовании ScrollView. Я только что обновил свою реактивную версию до последней версии, и проблема с переключением на секунду TextInputвсе еще сохраняется. Клавиатура тогда не запрещена. Вы нашли решение этой конкретной проблемы?
TurboFish

1
Документы были неверными, но теперь были обновлены, см. Этот PR: github.com/facebook/react-native/issues/2150
Райан МакДермотт

Что делает keyboardShouldPersistTaps? Почему это актуально здесь? Спасибо
Лейн Реттиг

1
Предупреждение: «keyboardShouldPersistTaps = {false}» устарела. Вместо этого используйте 'keyboardShouldPersistTaps = "never" "
Милан Ракос

13

Если кому-то нужен рабочий пример того, как отклонить многострочный ввод текста, то вы идете! Надеюсь, что это поможет некоторым людям, в документах вообще не описывается способ отклонения многострочного ввода, по крайней мере, не было конкретной ссылки на то, как это сделать. Тем не менее, новичок для фактической публикации здесь в стеке, если кто-то думает, что это должна быть ссылка на фактическую публикацию, для которой был написан этот фрагмент, дайте мне знать.

import React, { Component } from 'react'
import {
  Keyboard,
  TextInput,
  TouchableOpacity,
  View,
  KeyboardAvoidingView,
} from 'react-native'

class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      behavior: 'position',
    }
    this._keyboardDismiss = this._keyboardDismiss.bind(this)
  }

  componentWillMount() {
    this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
  }

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

  _keyboardDidHide() {
    Keyboard.dismiss()
  }

  render() {
    return (
      <KeyboardAvoidingView
        style={{ flex: 1 }}
        behavior={this.state.behavior}
      >
        <TouchableOpacity onPress={this._keyboardDidHide}>
          <View>
            <TextInput
              style={{
                color: '#000000',
                paddingLeft: 15,
                paddingTop: 10,
                fontSize: 18,
              }}
              multiline={true}
              textStyle={{ fontSize: '20', fontFamily: 'Montserrat-Medium' }}
              placeholder="Share your Success..."
              value={this.state.text}
              underlineColorAndroid="transparent"
              returnKeyType={'default'}
            />
          </View>
        </TouchableOpacity>
      </KeyboardAvoidingView>
    )
  }
}

11

Обновлено использование ScrollViewдляReact Native 0.39

<ScrollView scrollEnabled={false} contentContainerStyle={{flex: 1}} />

Хотя проблема с двумя TextInputящиками остается . например. Форма имени пользователя и пароля теперь будет отклонять клавиатуру при переключении между входами. Хотелось бы получить несколько советов, чтобы сохранить живую клавиатуру при переключении между TextInputsпри использовании ScrollView.


3
Похоже, что 0.40обновления keyboardShouldPersistTapsот a booleanк enumвозможному значению 'handled', которое должно исправить это.
Аншул Кока

11

Есть несколько способов, если вы управляете событием так, как onPressможете использовать:

import { Keyboard } from 'react-native'

onClickFunction = () => {
     Keyboard.dismiss()
}

если вы хотите закрыть клавиатуру при использовании прокрутки:

<ScrollView keyboardDismissMode={'on-drag'}>
     //content
</ScrollView>

Еще вариант, когда пользователь нажимает за пределами клавиатуры:

<KeyboardAvoidingView behavior='padding' style={{ flex: 1}}>
    //inputs and other content
</KeyboardAvoidingView>

1
Ребята, вопрос все еще актуален, но ему 4 года (сейчас конец 2019 года). RN теперь настолько прост и удобен в использовании. Мы должны рассмотреть все возможности, с помощью которых мы можем достичь решения этого вопроса. Позвольте проголосовать за этот комментарий!
Ссылка

@ Ссылка Привет, спасибо! Я абсолютно согласен
Idan

10
const dismissKeyboard = require('dismissKeyboard');
dismissKeyboard(); //dismisses it

Подход № 2;

Спасибо пользователю @ ricardo-stuven за указание на это, есть еще один лучший способ отклонить клавиатуру, который вы можете увидеть в примере в реагирующих нативных документах.

Простой импорт Keyboardи вызов методаdismiss()


1
Это точный эквивалент Keyboard.dismiss, который является предпочтительным, поскольку задокументирован. github.com/facebook/react-native/blob/…
Рикардо Стювен

В то время, когда я давал этот ответ, это не было задокументировано. Спасибо за упоминание этого. Я обновлю свой ответ.
Адель Имран,

10

Оборачивание ваших компонентов в TouchableWithoutFeedbackможет вызвать странное поведение прокрутки и другие проблемы. Я предпочитаю оборачивать свое Viewсамое верхнее приложение в onStartShouldSetResponderзаполненное свойство. Это позволит мне обработать все необработанные касания и затем отклонить клавиатуру. Важно отметить, что поскольку функция-обработчик возвращает false, событие касания распространяется как обычно.

 handleUnhandledTouches(){
   Keyboard.dismiss
   return false;
 }

 render(){
    <View style={{ flex: 1 }} onStartShouldSetResponder={this.handleUnhandledTouches}>
       <MyApp>
    </View>
  }

Спасибо за ваш ответ @ Scottmas. Я закончил тем, что использовал его вместо TouchableWithoutFeedback из-за вашего «странного поведения прокрутки и других проблем». Но если бы я не слепо доверял твоим словам, можешь ли ты уточнить свой комментарий? :)
Kuhr

8

Я только что проверил это, используя последнюю версию React Native (0.4.2), и клавиатура отключается при нажатии в другом месте.

И к вашему сведению: вы можете настроить функцию обратного вызова, которая будет выполняться при отклонении клавиатуры, назначив ее для «onEndEditing».


Я отлаживал обратный вызов onEndEditing, но он никогда не срабатывал раньше; Я собираюсь разобраться в этом с новой версией реагировать
нативно

7

Если я не ошибаюсь, то последняя версия React Native решила эту проблему, заключающуюся в том, чтобы отмахнуться от клавиатуры, нажав.


4
Вы могли бы указать, какая часть их кода / документа делает это? Я сталкиваюсь с той же самой проблемой, и я действительно ценю это, указывающее мне направление :)
Окадзаки Мияма Юта

Подтверждено, что это все еще проблема на 0,19 RN (последний).
Лейн Реттиг

Все еще проблема с RN 0.28
hippofluff

7

Как насчет размещения сенсорного компонента вокруг / рядом с TextInput?

var INPUTREF = 'MyTextInput';

class TestKb extends Component {
    constructor(props) {
        super(props);
    }

    render() {
        return (
            <View style={{ flex: 1, flexDirection: 'column', backgroundColor: 'blue' }}>
                <View>
                    <TextInput ref={'MyTextInput'}
                        style={{
                            height: 40,
                            borderWidth: 1,
                            backgroundColor: 'grey'
                        }} ></TextInput>
                </View>
                <TouchableWithoutFeedback onPress={() => this.refs[INPUTREF].blur()}>
                    <View 
                        style={{ 
                            flex: 1, 
                            flexDirection: 'column', 
                            backgroundColor: 'green' 
                        }} 
                    />
                </TouchableWithoutFeedback>
            </View>
        )
    }
}

6

Оберните весь ваш компонент с:

import { TouchableWithoutFeedback, Keyboard } from 'react-native'

<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
...
</TouchableWithoutFeedback>

Работал для меня



4

Модуль клавиатуры используется для управления событиями клавиатуры.

  • import { Keyboard } from 'react-native'
  • Добавьте ниже код в методе рендеринга.

    render() { return <TextInput onSubmitEditing={Keyboard.dismiss} />; }

Ты можешь использовать -

Keyboard.dismiss()

static dismiss () Отклоняет активную клавиатуру и удаляет фокус в соответствии с оригинальными документами.


3

Первая импортная клавиатура

import { Keyboard } from 'react-native'

Затем внутри TextInputвы добавляете Keyboard.dismissв onSubmitEditingопору. У вас должно быть что-то похожее на это:

render(){
  return(
    <View>
      <TextInput 
        onSubmitEditing={Keyboard.dismiss}
       />
    </View>
  )  
}

1
Больше контекста было бы неплохо.
colidyre

2

Используя keyboardShouldPersistTapsв, ScrollViewвы можете передать «обработано», которое решает проблемы, которые, по словам людей, возникают при использовании ScrollView. Это то, что в документации говорится об использовании «handled»: the keyboard will not dismiss automatically when the tap was handled by a children, (or captured by an ancestor). вот где на него ссылаются.


Это сработало для меня! (однако я должен был добавить это в мою стороннюю библиотеку react-native-keyboard-aware-scroll-view).
Ник Грэли

1

в ScrollViewиспользовании

keyboardShouldPersistTaps="handled" 

Это сделает вашу работу.


1

Есть много способов, которыми вы могли бы справиться с этим, ответы выше не включают returnTypeв себя, так как он не был включен в реакцию на этот раз.

1: Вы можете решить эту проблему, обернув свои компоненты внутри ScrollView, по умолчанию ScrollView закрывает клавиатуру, если мы что-то нажимаем. Но если вы хотите использовать ScrollView, но отключите этот эффект. Вы можете использовать pointerEvent prop для scrollView pointerEvents = 'none' .

2: Если вы хотите закрыть клавиатуру нажатием кнопки, вы можете просто использовать Keyboardсreact-native

import { Keyboard } from 'react-native' and inside onPress of that button, you can useKeyboard.dismiss ()».

3: Вы также можете закрыть клавиатуру, когда нажмете клавишу возврата на клавиатуре. ПРИМЕЧАНИЕ: если у вас тип клавиатуры цифровой, у вас не будет клавиши возврата. Таким образом, вы можете включить его, предоставив ему команду returnKeyType done. или вы можете использовать onSubmitEditing={Keyboard.dismiss}, он вызывается всякий раз, когда мы нажимаем клавишу возврата. И если вы хотите отклонить клавиатуру при потере фокуса, вы можете использовать onBlur prop,onBlur = {Keyboard.dismiss}


0

Keyboard.dismiss()сделаю это. Но иногда он может потерять фокус, и клавиатура не сможет найти ссылку. Наиболее последовательный способ сделать это - поместить ref=_refв textInput, и делать, _ref.blur()когда вам нужно уволить, и _ref.focus()когда вам нужно вернуть клавиатуру.



0

Вот мое решение для отклонения клавиатуры и прокрутки к нажатой TextInput (я использую ScrollView с пропуском клавиатурыDismissMode):

import React from 'react';
import {
  Platform,
  KeyboardAvoidingView,
  ScrollView
} from 'react-native';

const DismissKeyboard = ({ children }) => {
  const isAndroid = Platform.OS === 'android';
  const behavior = isAndroid ? false : 'padding';

  return (
    <KeyboardAvoidingView
      enabled
      behavior={ behavior }
      style={{ flex: 1}}
    >
      <ScrollView
        keyboardShouldPersistTaps={'always'}
        keyboardDismissMode={'on-drag'}
      >
        { children }
      </ScrollView>
    </KeyboardAvoidingView>
  );
};

export default DismissKeyboard;

Применение:

render(){
   return(
     <DismissKeyboard>
       <TextInput
        style={{height: 40, borderColor: 'gray', borderWidth: 1}}
        onChangeText={(text) => this.setState({text})}
        value={this.state.text}
      />
     </DismissKeyboard>
   );
}


0

использовать этот пакет react-native-keyboard-aware-scroll-view

использовать этот компонент в качестве корневого компонента

так как этот пакет react-native-keyboard-aware-scroll-viewтакже имеет scrollView, вам нужно добавить это к нему:

<KeyboardAwareScrollView keyboardShouldPersistTaps="handled"> <ScrollView keyboardShouldPersistTaps="handled"></ScrollView> </KeyboardAwareScrollView>

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.