著者アーカイブ 村上

村上 著者:村上

【Android】ActionBarのタイトルとボタンの色を白色に変更する

前回に引き続き、今回も Android の ActionBar についての記事です。
今回は、ActionBar の戻るボタンやタイトルの色を白色に変更する方法です。
ActionBar を濃い色で指定しているので、戻るボタンの色を白に変えたかったのですが、どこを直すのかがわかりにくかったのでまとめました。

参考にさせていただいた記事はこちらから。

android – Changing ActionBar Text and Button color when using Theme.AppCompat.Light – Stack Overflow
https://stackoverflow.com/questions/30720873/changing-actionbar-text-and-button-color-when-using-theme-appcompat-light

 

修正するのは、プロジェクトの res > values ディレクトリ内にある styles.xml ファイルです。
このファイル内にある AppTheme という名前のタグの parentTheme.AppCompat.Light.DarkActionBar に変更します。
具体的には下記のとおりです。

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    ......
</style>

変更はこれだけで、実機で確認したところ、きちんと白色に変更されていました。

他にも、タグ内に下記を追加するという方法もあるようでしたが、私の環境では上手くいきませんでした。

<item name="android:textColorPrimary">#fff</item>

そのため、他に影響が出ないようでしたら、parent を変更する方法をおすすめします。

 

以上、Android の ActionBar のボタン・タイトルの色を白色に変更する方法でした。
ご参考になれば幸いです。

村上 著者:村上

【Android】setTitle()で指定したActionBarのTitleが表示されない時の対処法

凄く初歩的ですが、今後も同じミスをやらかしそうな予感がしたので、未来の自分のためにまとめ。
タイトル通り、.setTitle() を使って ActionBar の Title を指定したのに表示されなかった時の対処法です。

今回参考にさせていただいた記事はこちらから。

Android getActionBar().setTitle does not work – Stack Overflow
https://stackoverflow.com/questions/26045431/android-getactionbar-settitle-does-not-work

ちなみに5年前の投稿でしたので、結構古いです。
が、私の環境ではこちらの方法で解決可能でした。

 

対処法ですが、getSupportActionBar().setDisplayShowTitleEnabled(true); を追加するだけでした。
具体的には下記のとおりです。

getSupportActionBar().setDisplayShowTitleEnabled(true);  // この行を追加
getSupportActionBar().setTitle("[タイトル]");

実際に実機(Android 7.0 と 8.0)で試したところ、問題なくタイトルが表示されていました!
念のため、他の端末でも確認する予定ですが、多分問題はないはずです。

 

以上、Android の ActionBar のタイトルが表示されない時の対処法でした。
ご参考になれば幸いです。

村上 著者:村上

【Android】「Only fullscreen opaque activities can request orientation」エラーでアプリがクラッシュする時の対処法

タイトル通り、今回は Android で発生したエラーの対処法についてです。
以前に開発したアプリの修正を行っていたところ、確か現在の Activity から別の ActivityIntent を使って遷移した際に「java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation」というエラーと共にアプリが強制終了しました。

参考にさせていただいた記事はこちらから。

java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation – Qiita
https://qiita.com/oxsoft/items/042aaa6e9c7ae828e967

全く同じ状況だったので、大変参考になりました…!

 

参考サイトによると、クラッシュ原因は下記の 4つを満たすと発生するそうなので、どれか一つでも対処すればOKです。

  1. Android 8.0 の端末
  2. targetSdkVersion が 27 以上
  3. 背景を透過にしている
  4. 画面の向きを固定している

私は 4番目の「画面の向きを固定している」を変更することで対応しました。
…というか、これを変更する以外の選択肢がないのでは…?

画面の向きを固定しない方法については、下記のサイトを参考にさせていただきました。

Y.A.M の 雑記帳: Android 画面の向きを設定
http://y-anz-m.blogspot.com/2010/09/android.html

設定方法は AndroidManifest.xml 内にある、画面向きを固定しないアクティビティの android:screenOrientationbehind に設定するだけです。
サンプルコードは下記のとおりです。

<activity
    android:name=".[アクティビティ名]"
    android:label="@string/app_name"
    android:screenOrientation="behind" />

対処方法としては以上です。
再度アプリをビルド・実行したところ、クラッシュすることなく問題なく動作しました!

 

以上、「java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation」エラーでアプリがクラッシュする時の対処法でした。
ご参考になれば幸いです。

村上 著者:村上

【Windows】「You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.」警告の対処法

昨日に引き続き、本日も webpack で発生したエラー・警告の対処法についてです。
昨日は「Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.」というエラーの対処法でしたが、今回の警告はこちらのエラーを解消した後に発生したものです。

警告なので、別に修正が必須ではないのですが、気になるので対応しました。
警告文は「You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.」というものです。

参考にさせていただいた記事はこちらから。

Webpack2のパフォーマンス警告を制御する – Qiita
https://qiita.com/terrierscript/items/f840b5ccff0c0be7420a

ビルドした際にファイルサイズがデフォルト設定よりも大きいです!という警告のようです。

 

対応策としては、下記の方法があります。

  1. 警告そのものを表示させないように設定する
  2. ファイルサイズ警告の閾値を変更する

私は 2の方を採用しました。
webpack.config.js に 下記を追加します。

module.exports = {
  ...
  performance: {
    maxEntrypointSize: [ファイルサイズの許容値],
    maxAssetSize: [ファイルサイズの許容値]
  }
}

どちらの値もデフォルト値は 250,000(=250kb)です。
警告になったファイルサイズによって適宜変更してください。
なお、カンマは不要ですのでお気を付けください。
あとは、npm run build を実行すればOKです。
閾値を超えなければ、警告は発生しません。

 

以上、npm run build を実行した際の「You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.」警告の対処方法でした。
ご参考になれば幸いです。

村上 著者:村上

【Windows】「Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.」エラーの対処法

Windows 環境で、Cordova アプリをビルドしようと npm run build を実行しようとしたところ、「Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.」というエラーに遭遇したのでその解決方法についてです。
調べたところ、こちらのエラーは webpack で発生しているとのこと。

参考にさせていただいた記事はこちら。

webpack4に更新した時にこけた所まとめ – Qiita
https://qiita.com/shota_abe/items/fbd6d988188442a4d11c

上記のサイトによると、今回のエラーは webpack を 4 に更新したタイミングで発生したとのことでした。

 

解決方法ですが、webpack.config.jslodersrules に変更するだけでした!

module.exports = {
  ...

  module: {
    rules: [  // ここを loders から rules に変更
      ...
    ]
  },
  ...
}

あとは、念のため npm install を再度実行後、npm run build を実行したところ、エラーが解消されました!

ただし、今度は「The ‘mode’ option has not been set, webpack will fallback to ‘production’ for this value. Set ‘mode’ option to ‘development’ or ‘production’ to enable defaults for each environment.」というエラーが発生したので、今度はこの対策も必要です…。
こちらのエラーについては、次回に解決方法をご紹介させていただければと思います。

 

以上、「Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.」エラーが発生した時の対処法でした。
ご参考になれば幸いです。

村上 著者:村上

【JavaScript】配列から検索条件に一致する全ての要素のインデックスを取得する方法

重複している要素を含む配列を検索した時、その全ての要素のインデックスを取得する方法です。
あまり綺麗な方法ではないかもしれませんが…とりあえず求めている結果は取得できました。

filter メソッドの使い方について参考にさせていただいたサイトはこちら。

【JavaScript入門】filterで配列のデータを抽出する方法 | 侍エンジニア塾ブログ(Samurai Blog) – プログラミング入門者向けサイト
https://www.sejuku.net/blog/21887

 

サンプルコードはこちら。

let data = [検索したい配列];
var indexs = [];
data.filter(function(value, index, array) {
    if (検索条件) {
        indexs.push(index);
    }
}

indexOf メソッドだと、一番最初に一致した要素のインデックスのみしか取得できないので、filter メソッドを使っています。
そして、検索条件に一致した要素のインデックスは、indexs という配列に追加されるので、後は任意の処理でお使いください。

…が、これ filter メソッドではなく、 map メソッドを使うべきな気が…。
参考サイトによると、「filter メソッドは条件に一致した要素だけを抽出して新しい配列として格納する」とあるので、正しい使い方ではないかも?

ということで、map メソッドを使って書くなら下記の通りです。

let data = [検索したい配列];
var indexs = [];
data.map(function(value, index, array) {
    if (検索条件) {
        indexs.push(index);
    }
}

どちらでも正しい結果は得られるので、後は好みの問題な気もしますね…。
ただ、使い方からすると、正しいのはおそらく map メソッドを使った方法だと思います。

 

以上、配列から検索条件に一致する全ての要素のインデックスを取得する方法でした。
ご参考になれば幸いです。

村上 著者:村上

【Swift】UITextField入力後に画面をタップしてキーボードを非表示にする

Swift で実装しとくと便利かな?な機能その2。
今回は、文字入力時に表示されたソフトウェアキーボードを、TextField や キーボード以外のばしょをタップして消す方法です。
確か Android は特に指定しなくても入力欄外をタップすればキーボードは非表示になったと思うのですが…iOS はわざわざ実装しないと消えないんですね…。

今回参考にさせていただいたサイトはこちらから。

[Swift] resignFirstResponder 以外でキーボードを隠す – Qiita
https://qiita.com/takecian/items/ebcdd3cc37c90a054020

 

実装方法は下記の通りです。
画面がタップされた時に呼ばれる touchesBegan をオーバーライドして、その中にキーボードを閉じる処理を追加します

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    // キーボードを閉じる
    view.endEditing(true)
}

他にも、UITextFieldresignFirstResponder メソッドを使う方法もありますが、こちらの方法だと、TextField が複数あった場合、全ての TextField でメソッドを実行しなければいけないので面倒です。
かく言う私も、最初は resignFirstResponder を使っていたのですが、endEditing を知り、速攻でこちらに書き換えました。
アプリの挙動にも全く問題ないので、よほどのことがない限りは、view.endEditing(true) を使った方が簡単だと思います!

 

以上、UITextField 入力後に、入力欄外やキーボード以外の画面をタップしてソフトウェアキーボードを非表示にする方法でした。
ご参考になれば幸いです。

なお、Swift で実装しとくと便利かな?な機能その1 はこちらです。

【Swift】UITextField入力後にキーボードのリターンキーで次のUITextFieldに移動する
https://cpoint-lab.co.jp/article/201911/12629/

必須ではないですが、こちらもあると便利かもしれません。

村上 著者:村上

【Cordova】「imgcache.js」でキャッシュした画像のパスを取得する方法

以前に紹介した、画像をキャッシュするライブラリ imgcache.js で、キャッシュした画像のURLを取得する方法についてです。
前回の記事を投稿した時点では「WARN: No source given to getCachedFileName INFO: File undefined not in cache」というエラーが発生して画像が取得できなかったのですが、ようやく取得方法がわかりました。

なお、以前の記事はこちら。

【Cordova】アプリで表示する画像を「imgcache.js」を使ってキャッシュする
https://cpoint-lab.co.jp/article/201910/12435/

ライブラリのインストールから、画像をキャッシュに保存するまでの手順を紹介しています。

 

で、方法ですが、.getCachedFileURL() メソッドを使いました。
こちらは、キャッシュした画像の URL を取得できます。
サンプルコードはこちら。

ImgCache.isCached([キャッシュしたい画像のURL], function(path, success) {
  if (success) {
    ImgCache.getCachedFileURL(path, function(originalUrl, cacheUrl) {
      // 任意の処理を実行
      // cacheUrl にキャッシュした画像のURLが代入されている
    }, function(error) {
      console.log(error);
    });
  } else {
    ImgCache.cacheFile(path, function () {
      ImgCache.getCachedFileURL(path, function(originalUrl, cacheUrl) {
        // 任意の処理を実行
        // cacheUrl にキャッシュした画像のURLが代入されている
      }, function(error) {
        console.log(error);
      });
    });
  }
});

まず 1行目で、画像がキャッシュ済みかを調べます。
結果は success に格納され、キャッシュ済みだったら、3行目で画像のパスを取得しています。
コメントにも記載している通り、cacheUrl にキャッシュした画像のパスが保存されているので、そちらを使って任意の処理を実行してください。
なお、pathoriginalUrl には、キャッシュされる前のオリジナルのURLが格納されています。
指定した画像がまだキャッシュされていなかった場合は、10行目で画像をキャッシュし、11行目でキャッシュした画像の URL を取得しています。
処理としては以上です!

私の環境では .useCachedFile() メソッドが動作しなかったのと、キャッシュされた画像の URL を取得できた方が処理的にも便利だったので、 .getCachedFileURL() を使いました。

 

また、前回の記事で「node_modules > imgcache ディレクトリ内にある imgcache.js を任意の場所にコピーし、それをインポートします。」と書きましたが、よくよく確認したところ、node_modules 内の imgcache ライブラリのディレクトリの名前が imgcache.js になっていました。
こちらを imgcache に変更し、import ImgCache from 'imgcache'; と指定したところ、問題なくインポートできましたので、ご参考いただければと思います。
ディレクトリ名に拡張子が入ってしまっていたせいで、正常に読み込めなかったんですね…。

 

以上、imgcache.js を使ってキャッシュした画像の URL を取得する方法でした。
ご参考になれば幸いです。

村上 著者:村上

【Swift】UITextField入力後にキーボードのリターンキーで次のUITextFieldに移動する

実装しとくと便利かな?な機能その1。
今回は、TextField にテキストなどを入力後、ソフトウェアキーボードのリターンキーで次の TextField に移動する方法です。
ログインや会員登録などのフォーム画面に実装されていることが多いと思います。

今回参考にさせていただサイトはこちら。

UITextFieldのキーボード操作で使えるTips – Goalist Developers Blog
http://developers.goalist.co.jp/entry/2017/03/16/190535

 

サンプルコードはこちら。
なお、今回 Storyboard は使用せず、全てコードのみで実装しています。

まずは、テキスト入力欄を作成します。
今回は、ログインフォームをイメージして、名前とパスワードの入力欄を追加しました。

// 名前の入力欄を作成
var name: UITextField = UITextField(frame: CGRect(x: 0, y: 0, width: 200, height: 40))
name.delegate = self
name.layer.position = CGPoint(x: self.view.bounds.width/2, y: 200)
name.returnKeyType = .next
name.tag = 0
self.view.addSubview(name)

// パスワードの入力欄を作成
var password: UITextField = UITextField(frame: CGRect(x: 0, y: 0, width: 200, height: 40))
password.delegate = self
password.layer.position = CGPoint(x: self.view.bounds.width/2, y: 300)
password.isSecureTextEntry = true
password.returnKeyType = .Done
password.tag = 1
self.view.addSubview(password)

ポイントは、4行目と12行目の .returnKeyType と、5行目と13行目の .tag です。
まず、.returnKeyType でソフトウェアキーボードのリターンキーを「Next」と「Done」の文字に変更しています。

次に、.tag で それぞれの UITextField のタグを指定しています。
なお .tag には数値しか指定できないのでご注意ください。

次に、リターンキーを押した時の処理を追加します。

// リターンキーを押した時の処理
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    switch textField.tag {
    case 0:
        // タグが 0 なら password にフォーカスを当てる
        password.becomeFirstResponder()
        break
    case 1:
        // タグが 1 ならキーボードを閉じる
        password.resignFirstResponder()
        break
    default:
        break
    }
    return true
}

コメントに書いてある通りですが、UITextField のタグが 0、つまり名前入力欄だったら、リターンキーを押した時にパスワード入力欄にフォーカスを当てます。
次に、UITextField のタグが 1、つまりパスワード入力欄だったら、ソフトウェアキーボードを閉じます。

実装するコードは以上です。

 

以上、UITextField にテキストを入力した後、キーボードのリターンキーで次の UITextField に移動する方法でした。

なお、これらの処理を簡単に実装できる FormChangeable というライブラリもありましたが、私の環境ではエラーが発生してしまったので、今回は採用しませんでした。
個人的な感覚では、ライブラリを使わなくてもそこまで複雑な処理ではないので、自前で実装でも問題ないと思います。

村上 著者:村上

【Swift】「A background URLSession with identifier [identifier名] already exists!」警告の対処法

Swift の URLSesssion を使った POST 送信の処理を実装した際に発生した警告についての対処法です。
これのせいでアプリがクラッシュするということはないのですが、私の環境では dismiss() が正常に作動せず、前のページに戻れないというバグが発生したので、修正することをお勧めします。

今回参考にさせていただいた記事はこちら。

【Swift】URLSessionまとめ – Qiita
https://qiita.com/shiz/items/09523baf7d1cd37f6dee#1アプリ1URLSession

 

こちらの記事内で、下記のような記述があります。

・同時平行処理される複数のURLSessionTaskは1つのURLSessionで共有可能。
・独自でURLSessionを作成している場合は、finishTasksAndInvalidateやinvalidateAndCancelでデリゲートやコールバックオブジェクトへの参照をクリアしないとメモリーリークを起こす。

で、自分のプロジェクトを確認したところ…セッションを無効にする処理を追加してない!
ということで、下記のコードを追加して修正しました。

// ページが破棄される直前に呼び出される
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    // 通信キャンセル&セッション無効
    self.session?.invalidateAndCancel()
}

私の場合は、ページが破棄される直前に Session も無効にしていますが、他のタイミングで実行しても問題ありません。
適宜、環境に合わせて変更してください。

また、URLSession は下記のようにグローバル変数で宣言することをお忘れなく。

var session: URLSession?

URLSession を使った POST 送信処理については、以前に記事を投稿しましたので、そちらを参考にして頂けると嬉しいです。

【Swift】URLSessionを使ってPOST送信を行う方法
https://cpoint-lab.co.jp/article/201911/12524/

 

以上、Swift の URLSession を使用した際に、「A background URLSession with identifier [identifier名] already exists!」という警告が発生する際の対処方法についてでした。
ご参考になれば幸いです。