頭と尻尾はくれてやる!

パソコンおやじのiPhoneアプリ・サイト作成・運営日記


NSSecureCodingでオプショナル型の保存、読み込み方法

iOS/macOSアプリでデータを保存する時に、データが色々あるとそのデータを持つクラスを作って、そのクラスがNSSecureCodingプロトコルに従うようにすることが多い。
保存の方法がUserDefaultsだろうがNSKeyedArchiverやFileWrapperなどを使おうがまあいい。ともかくクラスがNSSecureCodingに従ってる場合の話。

このプロトコルに従うとクラスはこんな感じになると思う。
class Person: NSObject, NSSecureCoding {

var name: String?
var weight: Float?

static var supportsSecureCoding = true

// save
func encode(with coder: NSCoder) {

coder.encode(name, forKey: "name")
coder.encode(weight, forKey: "weight")
}

// load
required init(coder: NSCoder) {

super.init()
name = coder.decodeObject(forKey: "name") as? String
weight = coder.decodeFloat(forKey: "weight”)
}
}
これを実行するとname: String?についてはいけるが、weight: Float?について読み込み時に
*** -[NSKeyedUnarchiver decodeDoubleForKey:]: value for key (weight) is not a 64-bit float
なんてエラーが出たりする。
まだこのエラーが出るような場合はまだマシで、ビット数を間違えてデータをメモリに展開するのかアプリが起動時に全然関係ないところで「このデータがおかしいで!」と落ちてしまう、という症状が出てた。

結局のところオプショナル型を扱う場合には
//weight = coder.decodeFloat(forKey: "weight”)
weight = coder.decodeObject(forKey: "weight”) as? Float
のようにしてやるといいみたい。
どうしてもcoder.decodeFloatを使いたいなら保存時に
coder.encode(weight!, forKey: "weight")
のようにunwrapすれば可能。もちろん変数がnilではないことが前提だけど。

func decodeFloat(forKey key: String) -> Float
確かにリファレンスだと↑このように返り値はFloatなのでnilだと返せないよな。



macOS 10.15.6 Catalina
Xcode 11.7
Swift 5.2.4
スポンサーサイト






NSTableViewでセルの編集をしたい時にハマったこと

macOSのNSTableViewを使う時、最近はdelegateとかdataSourceとか使わずにバインディングを使ってる。その方が楽だからだ。

macOSでバインドを使いテーブルを表示する
↑基本はこんな感じ。

今回、このようなNSArrayControllerを使って表示しているテーブル内のセルの値を直接編集しようとしてハマったところをメモ。

NSTextFieldの設定

1. セル(NSTextField)のBehavior(上の図)をEditableにする。

2. セルの入力(編集)完了を拾いたい場合にはAction(上の図)をSent On End Editingとしておき、
Sent Actionsで自分で設定したメソッドに接続しておく(下の図)。
Sent Actionsの設定
かつ、tableViewのdelegateを接続しておく(なぜかセルのNSTextFieldのdelegateではない!)。

3. xibファイルにNSTableViewを設置したらtableのセル(のNSTextField)にはデフォではNumberFormatterは設定されてない。
NumberFormatterを後から設定する場合、
Number Formatterの追加方法
↑この部分をドラッグしてセルにドロップすればいけた。

4. セルのバインディングの設定で参照する変数の型がStringならいいけどIntやFloatだと
Property cannot be marked @objc because its type cannot be represented in Objective-C
となってしまう。なのでNSNumber型にした。
厳密には当方の都合によりNSNumber?型にしてる

5. 上記のNSNumber?で値がまだnilの場合にはplaceholderを表示させたい。
placeholderの指定場所
↑この場合、NSTextFieldのではなく(なんでや?!)、セルのバインディングの設定ペインの”Null Placeholder”に記述するといけた。

NSTableView表示結果
↑placeholder表示した様子。


NSTableViewはいつもながら苦労するけど、SwiftUIになったらこんなの一撃なんかなあと妄想。



macOS 10.15.6 Catalina
Xcode 11.7
Swift 5.2.4



NSSegmentedControlで選択時の画像を白くする

NSSegmentedControlで文字を設定した場合
↑macOSのNSSegmentedControlで各項目に文字列を設定すると選択したところの文字の色は白になってくれる。

NSSegmentedControlで画像を設定した場合(1)
↑ところが文字ではなく画像を設定すると選択、非選択とも元の画像そのままだ。
青い背景だと選択時のが見にくい。できれば文字のように白くなった方が視認性が高い。
Xcodeで選択、非選択それぞれの画像を指定できるのか?と探すもなさそう。

なので自作した。
画像を白黒両方準備するのが面倒なので黒画像のみ作成しておき、白画像は黒画像を反転(※1)させたのを使用する。
クリック時にそれぞれの画像を選択、非選択で適切な画像を設定する、というのを完成させた。

ところがその後で気付いた。
元々それ用のコードが用意されてたことに。
NSSegmentedControlクラスではなくNSImageクラスのプロパティにisTemplateというのがある。
これを真にしておくだけ。
for index in 0..<segmentedControl.segmentCount {
segmentedControl.image(forSegment: index)?.isTemplate = true
}
↑あらかじめ全ての項目に設定しておくと

NSSegmentedControlで画像を設定した場合(2)
↑それだけで選択時には白くしてくれる。
あー、時間を無駄にした、、、



参考ページ
※1 Swift:左右反転した画像,白黒画像,ネガポジ反転画像を生成する - Qiita



iOSアプリ、ポケモンGO用「わざタイプ相性表」をリリース

pokegochart_iconR.png
わざタイプ相性表

ポケモンGOに登場するポケモンたちの、わざとポケモンのタイプの相性を調べるアプリです。
レイドバトルでレイドボスに有効なわざを確認したり、GOバトルリーグで「XXXが出てきた時、どのポケモンに交代すれば有利だったのか?」など復習できます。
ポケモンGOで実装されていないポケモンのデータはありません。随時追加していきますので反映されるまで時間がかかります。
このアプリはポケモンGOの一ファンが作成したもので、公式なものではありません。


スクリーンショット

↑アップしたアプリの説明文、スクショがこんな感じ。

アプリ名に「ポケモンGO」という文字列を入れられないのはAppleの規約に従ってる。実際入ってるのがApp Storeには並んでいるのだが、これは審査する人によるんだろう。前のアプリ(個体値カメラ)で「for ポケモンGO」の文字列を入れて申請しリジェクトされてるので、再度やったら何されるかわからない(アカウント剥奪が怖い)ので、今回は大人しく最初からタイトルには「for ポケモンGO」は入れてない。けどアプリ内での表示やlaunch screenには入れてる。


一昨日の夕方に申請し、今朝4時頃に審査開始、通過してた。このアプリは日本語版のみなので審査の時間ってどうなるのかと思ってたがいつも通り朝起きたら審査が終わってた、というパターンだった。


申請直前の段階で最新のデータを入れたにもかかわらず、今朝いきなりポケモンGOにメガシンカが実装され、近所のジムでメガカメックスがレイドに登場してた、、、なるべく早くデータ更新版を申請せねば。


今回のアプリ、当初はGOバトルリーグでリアルタイムで対戦してる時に使えるように、相手のポケモンや自分のチームのポケモンの交代を音声でできるように開発してた。
ところがiOSで使える本家の音声認識(SFSpeechRecognizer等による)はポケモンの名前を聞かせてもそれに近い日本語を探すようになっていた。
なので「カイリキー!」と言っても「怪力」という文字列が返ってくるのだ。
それでは「怪力」だったら「カイリキー」と変換するようにやってみようと、自分の発声で返ってきた単語を片っ端からリストアップし、

変換例

↑こんな感じで変換を実装していたが、、、あまりにもキリがない。音声だけでポケモンを特定するのは無理筋だな、と思い撤退。
ポケモンの選択は普通にキーボードで手入力とし、対戦後に復習したい時に使えるような感じにまとめた。


可能なら英語版も、と思ったもののポケモン名やわざの名称を得られる信頼できそうなサイトが見つからず結局日本語版のみになった。


アプリはportraitでもlandscapeでも可能で、レイアウト切り替えやアニメーション時の移動などはほぼAutoLayoutの制約を書き直してる。だいぶ自分の中では使い慣れてきたなあ、と感じてきたが時代はSwiftUIですか、そうですか、、、


Appプレビュー用動画がアップロードできなかった

iOSアプリの申請時にアップするAppプレビュー用の動画(movファイル)がなぜか受け付けてくれないという状況に陥った。

エラーメッセージ

「FILE_RESAVE_CORRUPTED」
「1つ以上のAppプレビューのオーディオがサポートされていないか、破損しています。」
などとエラー表示されるのである。

ただ全てがダメなわけではなかった。
App Store Connectの申請ではiPhone用(6.5インチ、5.5インチ)、iPad用(2つあるが同じでも可)の3パターンが必要でそれぞれ2種類、合計6つの動画を用意してたのだが、半分は期待通りにアップロードできていたのだ。
なので、何が違うんだろうな?と色々と調べてた。

Appプレビューの仕様

↑これはAppプレビューの仕様の一部だが、オーディオに関し1トラック(ステレオ2チャンネル)もしくは2トラックでそれぞれLとRでステレオとある。

アップロードに失敗した動画を確認するとどうやらトラックが2つある。おそらく1トラックにステレオ2チャンネル。「おそらく」としたのはトラックの中身が何チャンネルまでか確認できなかったので。
だとしたら要求仕様から外れてたのでそりゃアップロードはできないだろう。

そもそも今回のAppプレビュー用動画は現在作成中のmacOSアプリで出力したものなのだが、動画ごとにトラックを用意していたために複数の動画から作成する場合はトラックが複数になっていた。
なので強引に1つのトラックになるようにして動画を出力したところApp Store Connectでアップロードできるようになった。




Copyright ©頭と尻尾はくれてやる!. Powered by FC2 Blog. Template by eriraha.