はじめに

NativeScript で入力範囲外エリアのタップでキーボードを非表示にする方法

TL;DR

  • Android: dismissSoftInput() を使う
  • iOS: tns-core-modules/application/ios を使って ios.nativeApp.sendActionToFromForEvent('resignFirstResponder', null, null, null)
  • ScrollView あたりに tap イベントのハンドラとして設定
  • iOS は以下の問題があるため使用に注意
    • ロングタップによる入力補助が上手く効かなくなる
    • iOS 12以下だと入力エリアへのフォーカスが効かなくなる
この記事が参考になった方
ここここからチャージや購入してくれると嬉しいです(ブログ主へのプレゼントではなく、ご自身へのチャージ)
欲しいもの / Wish list

目次

  1. はじめに
  2. TL;DR
  3. 環境・条件
  4. 詳細
    1. 対応方法
    2. 補足
  5. まとめ
  6. その他・メモ
    1. iOS シミュレータでキーボードが表示されない
    2. Android でキーボード表示時に入力エリアが隠れないようにしたい
  7. 参考文献

環境・条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.15.5
BuildVersion: 19F101

$ node -v
v12.7.0

$ npm -v
6.14.5

$ tns --version
6.7.8

$ grep -C1 version package.json
"tns-android": {
"version": "6.5.1"
},
"tns-ios": {
"version": "6.5.1"
}

$ tns plugin
Dependencies:
┌───────────────────────────────┬─────────────────────┐
│ Plugin │ Version │
│ @nativescript/theme │ ^2.2.1 │
│ @vue/devtools │ ^5.0.6 │
│ nativescript-socketio │ ^3.2.1 │
│ nativescript-toasty │ ^1.3.0 │
│ nativescript-vue │ ^2.4.0 │
│ nativescript-vue-devtools │ ^1.2.0 │
│ tns-core-modules │ ^6.0.0 │
└───────────────────────────────┴─────────────────────┘
Dev Dependencies:
┌────────────────────────────────────┬─────────┐
│ Plugin │ Version │
│ @babel/core │ ^7.0.0 │
│ @babel/preset-env │ ^7.0.0 │
│ babel-loader │ ^8.0.2 │
│ nativescript-dev-webpack │ ^1.0.0 │
│ nativescript-vue-template-compiler │ ^2.0.0 │
│ nativescript-worker-loader │ ~0.9.0 │
│ node-sass │ ^4.9.2 │
│ vue-loader │ ^15.4.0 │
└────────────────────────────────────┴─────────┘

詳細

何もしてないとキーボードが開きっぱなしになる(※)ので、範囲外をタップしたら閉じるようにしたい、というお話。

returnKeyType="done" にしておくと、改行(Enter)でキーボードを閉じることはできる。が、そうじゃなくてタップで閉じたいよね、という感じ。

対応方法

参考: [Feature] Helper to dismiss keyboard · Issue #4594 · NativeScript/NativeScript

Android は UtilsdismissSoftInput() で閉じることができる。

iOS は tns-core-modules/application/ios を使って ios.nativeApp.sendActionToFromForEvent('resignFirstResponder', null, null, null) を実行すると閉じることができる。


以下、NativeScript-Vue での実装例。共通的に使うだろうということで mixin として定義。

ScrollView に該当ハンドラを設定すると、iOS 12.x 系で入力エリアそのものへのフォーカスが効かなくなったので Platform モジュールでバージョンを意識。

app/mixins/Utilities.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { ad } from "tns-core-modules/utils/utils";
import { ios } from "tns-core-modules/application";
import { device } from "tns-core-modules/platform";

export default {
methods: {
dismissSoftInput() {
if (ios && Number(device.osVersion) >= 13.0) {
ios.nativeApp.sendActionToFromForEvent('resignFirstResponder', null, null, null);
} else {
ad.dismissSoftInput();
}
},
},
}

app/main.js

1
2
3
4
5
import Vue from 'nativescript-vue'
// 略
import Utilities from './mixins/Utilities'
Vue.mixin(Utilities);
// 略

xxxx.vue では、 ScrollView あたりに tap イベントのハンドラとして設定すれば良い。

1
2
3
4
5
6
7
8
9
<template>
<Page>
<ScrollView @tap="dismissSoftInput">
<StackLayout>
<TextField v-model="xxxx" />
</StackLayout>
</ScrollView>
</Page>
</template>

ちなみに Platform モジュールで取れる情報はこんな感じ。(iOS のみ取得)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { device } from "tns-core-modules/platform";
console.log({
model: device.model,
deviceType: device.deviceType,
os: device.os,
osVersion: device.osVersion,
sdkVersion: device.sdkVersion,
language: device.language,
manufacturer: device.manufacturer,
uuid: device.uuid,
region: device.region,
});

// === iOS ===
// {
// model: 'iPhone',
// deviceType: 'Phone',
// os: 'iOS',
// osVersion: '13.5',
// sdkVersion: '13.5',
// language: 'ja-US',
// manufacturer: 'Apple',
// uuid: '12345678-90AB-CDEF-1234-567890ABCDEF',
// region: 'US'
// }

補足

前述の対応方法には以下の問題があるので注意。
※Android は問題なかった。

  • iOS でロングタップによる入力テキストの選択などが上手く機能しなくなってしまう。
  • iOS 12 以下だと入力エリアへのフォーカスが効かなくなる

別の対応方法など見つけたら、追記/更新 する予定。とりあえず、以下を軽く試したが(少なくとも自分の環境では)ダメだった。


https://github.com/NativeScript/NativeScript/issues/4594#issuecomment-433736057

軽く試したけどダメだった。(クラッシュした)

1
2
3
4
utils.ios
.getter(UIApplication, UIApplication.sharedApplication)
.keyWindow
.endEditing(true);

https://stackoverflow.com/a/60269199

軽く試したけどダメだった。(クラッシュした)

1
2
import { frame } from "tns-core-modules/ui/frame";
frame.topmost().nativeView.endEditing(true);

https://stackoverflow.com/a/54670359

軽く試したけどダメだった。(クラッシュした)

1
2
3
<TextField
@blur="$event.object.dismissSoftInput()"
/>

まとめ

  • Android: dismissSoftInput() を使う
  • iOS: tns-core-modules/application/ios を使って ios.nativeApp.sendActionToFromForEvent('resignFirstResponder', null, null, null)
  • ScrollView あたりに tap イベントのハンドラとして設定
  • iOS は以下の問題があるため使用に注意
    • ロングタップによる入力補助が上手く効かなくなる
    • iOS 12以下だと入力エリアへのフォーカスが効かなくなる

その他・メモ

iOS シミュレータでキーボードが表示されない

参考: iOSシミュレータでキーボードが表示されない - paranitips

iOS シミュレータでキーボードを表示したい場合は、シミュレータを起動した後に↓を変更する。

Android でキーボード表示時に入力エリアが隠れないようにしたい

参考: javascript - Stop keyboard overlay when interacting with TextField in a NativeScript application - Stack Overflow

app/App_Resources/Android/src/main/AndroidManifest.xmlandroid:windowSoftInputMode="stateHidden | adjustPan" を追加する。
※何か副作用あると思われるので、自分のアプリに適切かは各自で判断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="__PACKAGE__"
android:versionCode="100xx"
android:versionName="1.0"
xmlns:tools="http://schemas.android.com/tools">
...
<application
android:name="com.tns.NativeScriptApplication"
android:allowBackup="true"
android:icon="@drawable/icon"
android:label="@string/app_name"
android:usesCleartextTraffic="true"
android:theme="@style/AppTheme"
+ android:windowSoftInputMode="stateHidden | adjustPan"
>
...
</application>
</manifest>

参考文献

関連記事

この記事が参考になった方
ここここからチャージや購入してくれると嬉しいです(ブログ主へのプレゼントではなく、ご自身へのチャージ)
欲しいもの / Wish list