アンドロイド版が、なかなか難しいので、先にiPhone/iPad版9VAeきゅうべえを作ることにした。iOS 10 (iPad第4世代)以上で動くように開発。iPhoneXが画面の上に凹みができたので、メニューバーの場所を変えないといかん。画面を横にした時、今まではどちらの方向も同じだったが、凹みがどっちかで、変更しないといかん。カメラロールへの保存方法が変わってる・・・ Android版9VAeの開発記事はこちら
- iPhone実機デバッグ
- iPhoneXのシミュレータを使うには、HighSierra にして、最新の Xcodeを入れないといかん
- カメラロールへの保存は、PHPhotoLibraryを使う
- Localize(多言語対応)
- ライブラリ作成
- プロジェクト管理に BitBucket を導入
- iPhone 11 対応
- bitcode 対応
- iCloud drive
- ファイルで読み込めるようにする(iOS11以降)
- ApplePencil対応
- NSURLConnection から NSURLSession への移行
- Downloadファイルを開く方法
- invalid context エラーの場所をさがす方法
- info.plistの多国語対応
- Mac Catalyst
- AppStoreへの登録手順
- 実機テストができたりできなかったりする
- M1 Xcode でシミュレータ用ライブラリを作る方法
- iOS 15 でカーソルキーが効かない問題
- iCloud対応
- iOS版にカラーピッカーをつける
- しゃべる機能をつける
- シェイクモーション
- GameCenter のビルドエラー対応
- Appleペンシルをつかった手書き入力
- TestFlightで「テストを停止」してしまったときの処理
- iOS18で音声合成の動画出力がおかしくなった
- 「ios13以降」と以前を分けて書く書き方
- マウスホバー座標の取得(iOS13以降)
iPhone実機デバッグ
- iPhone機種とiOSの対応表(Qiita)、最終バージョン表(アンチョコ)
- Xcodeのバージョンと対応する iOSの表
- iOSのバージョンをあげると、Xcodeから実機デバッグできなくなることがあるので、自動アップデートにしてはいけない。一度バージョンをあげると元のバージョンにもどすことはできない。下位機種テストには中古でバージョンがあげられないものを入手しておくとよい
- 9VAeは、iOS10.0 から対応している(iPad 第4世代2012)
iPhoneXのシミュレータを使うには、HighSierra にして、最新の Xcodeを入れないといかん
- 凹みへの対応は、SafeArea を使う。
- StoryBoardの設定で、メニューバーはうまく行ったが、9VAeの独自ビューと、WebViewがうまくいかない。これは、プログラムで修正する。
ios - Objective C : How to create self.view inside Safe Area programmatically - Stack Overflow
- プログラムで修正するには、SafeAreaのサイズとデバイスの向きを知る必要があるが、普通の場所ではわからんらしい。
- メニューバーの項目の配置には、StoryBoard で、UIStackView に入れ、Distribution を「Fill Proportionally」に設定すると綺麗になった。複数のボタンをUIStackView に入れるには、複数のボタンを選んで、StoryBoardの右下のアイコン「Embed In」を押して何の中に入れるか選ぶとできる。なお、UIStackViewには描画機能がなく、背景に色をつけても無視される。
- StoryBoardで挿入した Viewをプログラムから変更するには、StoryBoardの名前とプログラム上の名前を対応づけしないといかん。それには、StoryBoard上のオブジェクトから右ボタンメニューを開き、「Referencing Outlets」の右側の+ボタンをドラッグし、Viewの上に持って行って対応するプログラムの中の名前と結ぶ。
- Viewを処理するプログラムには、インクルードファイル(.h)の中に、「@property (weak,nonatomic) IBOutlet Viewのタイプ 名前」という行と、プログラム(.m)の中に、「@synthesize 名前」が必要。IBOutlet の指定を元に、StoryBoard上で+ボタンをViewまでドラッグした時に、その名前が表示される。同じ名前にしておけば自動的にリンクされるというものではない。
- Viewをプログラムで操作する場合、画面の表示に反映されるのは、トップビューだけ。ボタンをつけたり消したりするのは、トップビューのプログラムでやらないといけない。
- WebView は、なぜか、画面配置をStoryBoardで制約をつけると描画されなくなった。プログラムでサイズ変更することはできた。
- iPhoneとiPadでもステータスバーの表示が違うみたいだ。ステータスバー有る無しでレイアウトを変えないといかん。
- StoryBoardでの制約のつけかた。(1)制約をつけたいViewを選ぶ。(2)
ボタン(3)左右はView、上下はSafeAreaを選ぶ(どのViewとの関係を制約にするか選べる)(4)最後に制約の追加ボタンをクリック。 - StoryBoardで制約をつけると、プログラムでViewのサイズ変更は不要になった。
カメラロールへの保存は、PHPhotoLibraryを使う
前からわかっていたが、ALAssetsLibrary が非推奨になって、PHPPhotoLibraryを使うことになった。iPhoneXシミュレータでアニメGIFをカメラロールに保存できなくなったので、対応する。
- 参考サイト
- GIFファイルは一旦自分のアプリ領域に作成する。それをカメラロールに転送する。
- UIImageを使って作成したGIFを転送すると静止画になってしまう。creationRequestForAssetFromImageAtFileURL にアニメGIFファイルのURLを指定すれば、アニメGIFを保存できた。
- エラーメッセージはGIF作成時とカメラロールへの転送時と異なる。メッセージ表示はボタンを押してから行うように作る。エラーコードを受け取って一か所でメッセージ表示するのではなく、各処理に分岐した最後にメッセージ関数を呼ぶ構造にする。
Localize(多言語対応)
- プロジェクトの設定で「Use Internatinalization」にチェックを入れる。すると、Base言語ができる。その後、+ボタンで Japaneseを追加。言語化用フォルダja.lproj, en.lproj ができる。
- Localizable.stringsは自動的にはできない。ja.lproj, en.lprojの中に infoPlist.stringsができているので、これを複製+リネームして、プロジェクトに追加する。
- @"xxx"をNSLocalizedString(@"xxx", comment: "") のように記述しておく。"xxx"="yyy";をLocalizable.stringsに入れておけば、"xxx"が"yyy"に置き換えられる。
ライブラリ作成
プロジェクト管理に BitBucket を導入
SouceTreeとの連携までややこしかったが、いろんな人に聞いて設定できた。
BitBucketはつぎのようにしてつかう。
- BitBucket(ネット上)にリポジトリを作る。リポジトリーはプロジェクトのいれもので履歴をもったフォルダみたいだ
- SouceTreeとの連携ができている場合、BitBucketから「SouceTreeにクローン」をつくれば、パソコン上に対応したフォルダが作られるみたいだ。フォルダの中を修正すれば、修正点がリストされるので(1)アップするものを index にあげる。(2)コミットボタン(3)プッシュボタン でネット上と同期される。
SouceTreeを使って新しい環境にクローンを作る方法
- SouceTreeをインストールして実行
- 「BitBucketクラウド」をクリック
- ATLASSIANに Googleアカウントでログイン>名前、メールアドレスが設定される>完了
- これで、BitBucketとSouceTreeが連携される。
- 取得したいリポジトリーを選んで「クローン」をクリック。
- フォルダ名を入力すればそこにクローンができる
- 今は使ってない。個人開発だと、ローカルでバックアップ複製とっておくほうが簡単。
iPhone 11 対応
iPhone11 に対応した Xcode にバージョンアップすると、ヘルプが表示されなくなった。UIWebView が使えなくなったようで、WKWebView に変更しないといかんらしい。
bitcode 対応
機械語をつくるまえの中間コードで、iPhoneアプリにはこれをつけて提出しないといかんらしい。これがあるといろんなCPU対応のコードをアプリストア側でつくれる。
iCloud drive
差替えリスト、命令リストを、9VAeフォルダに保存すると外部アプリから書き込めない。iCloud Drive をつかうと他のアプリから書き込めるかも
ファイルで読み込めるようにする(iOS11以降)
以下の対応をいれればファイルアプリでダウンロードからデータ転送できるみたい
ApplePencil対応
NSURLConnection から NSURLSession への移行
Deprecated対応
Downloadファイルを開く方法
- UIDocumentPickerViewController
info.plist内に以下を追加し値をYESにするとファイルアプリでコピーできるようになるようだ
UIFileSharingEnabled(Application supports iTunes file sharing)LSSupportsOpeningDocumentsInPlace(Supports opening documents in place)
invalid context エラーの場所をさがす方法
- 実行中にコンテクストエラーメッセージがときどき発生した。このエラーが発生した場所をさがす方法は以下の記事が役に立った。
-
xCode左のナビゲーションエディタを、ブレークポイントのタブに切り替えて、
左下の+ボタンを押して、add symbolic breakpointを選びます
そしてSymbolに、CGPostErrorと入力
info.plistの多国語対応
- infoPlist.strings を各言語で作成。左側のキーワードは、info.plist の xml タグ名をかく
- 例:写真アクセス>
NSPhotoLibraryUsageDescription="写真の読み込み/GIF,MP4保存";
Mac Catalyst
Mac Catalyst に対応すると、Macでも動くようになる
AppStoreへの登録手順
Apple Connect で審査用紙を作成
- アプリを選ぶ
- iOS APP + をクリックし、次のバージョン番号を入力
- このバージョンの最新情報(日本語、英語)を入力
- プロモーション用テキストを前のバージョンからコピー(日本語、英語)
XCode
- Project > Target にバージョン番号を設定
- Project > archive を実行し、バージョンなどをチェック
実機テストができたりできなかったりする
- entitlement の中に Apple developement があると、テストできないが、このキーを除くとテストできることがあった。以下が関係しているかも?
-
What is a provisioning profile & code signing in iOS? | by Abhimuralidharan | Medium
- iPhone や iPad の OS バージョンをあげると、Xcode が未対応で実機デバッグできなくなった。macOS を Big Sir にあげたくない場合は、実機 iOS のバージョンをあげてはいけない。
- 実機テストができていたプロジェクトをバックアップから取り出すと、実機デバッグができなくなることがあった(iOS18)Cleanしてもなおらない。この場合、Verの低い実機だと動くことがあって、それが動くともとの実機でも動くようにもどった。動作環境のパスのキャッシュ問題と思われるが、とにかく絶対にVerUpしない実機を一つはもっておくべき。
- Xcodeのバージョンをあげていくと、古い実機がデバッグできなくなる。最新のXcodeでないと、最新のOSの実機デバッグができなくなる、複数のOSを確保しておかないといけない
M1 Xcode でシミュレータ用ライブラリを作る方法
- M1 Xcode でシミュレータで動作テストするとき、「i386, x86_64 がライブラリにない」エラーがでた場合、プロジェクト> Build Setting > Excluded Architectures に「i386 x86_64」を入れると実行できる。もともと シミュレータが Intel で動作していたため、Intel用コードを作成しようとして失敗しているもよう。Intel用コードを作らないように設定する。
- M1のシミュレータ用ライブラリは、arm でよいが、シミュレータと iOS用ライブラリは異なる。そのため違う名前で作成する。
- テストプロジェクトでは、iOSとシミュレータと異なる名前のライブラリをリンクする。その設定は、プロジェクト> General >Frameworks, Library で、iOS用ライブラリをシミュレータと実機で切り替える(どちらも iOSで両方書くことができない)XCFramework をつかうとできるらしい
iOS 15 でカーソルキーが効かない問題
- iOS 15 でカーソルキーが効かなくなった。以下のように wantsPriorityOverSystemBehavior の設定が必要。keyUp, keyDown, keyLeft, keyRight はキーを押したときの処理
- if (@available(iOS 11.0, *))をいれておかないと、iOS 10 で落ちた。
- 修正には、Xcode 13 (Big Sur以上)が必要
<修正前>
- (NSArray *)keyCommands {
return @[
[UIKeyCommand keyCommandWithInput: UIKeyInputUpArrow modifierFlags: 0 action: @selector(keyUp)],
[UIKeyCommand keyCommandWithInput: UIKeyInputDownArrow modifierFlags: 0 action: @selector(keyDown)],
[UIKeyCommand keyCommandWithInput: UIKeyInputLeftArrow modifierFlags: 0 action: @selector(keyLeft)],
[UIKeyCommand keyCommandWithInput: UIKeyInputRightArrow modifierFlags: 0 action: @selector(keyRight)]
];
}
<修正後>
- (NSArray *)keyCommands {
UIKeyCommand *ukey = [UIKeyCommand keyCommandWithInput: UIKeyInputUpArrow modifierFlags: 0 action: @selector(keyUp)];
UIKeyCommand *dkey = [UIKeyCommand keyCommandWithInput: UIKeyInputDownArrow modifierFlags: 0 action: @selector(keyDown)];
UIKeyCommand *lkey = [UIKeyCommand keyCommandWithInput: UIKeyInputLeftArrow modifierFlags: 0 action: @selector(keyLeft)];
UIKeyCommand *rkey = [UIKeyCommand keyCommandWithInput: UIKeyInputRightArrow modifierFlags: 0 action: @selector(keyRight)];
if (@available(iOS 11.0, *)) if(@available(iOS 15.0, *)){
ukey.wantsPriorityOverSystemBehavior = YES;
dkey.wantsPriorityOverSystemBehavior = YES;
lkey.wantsPriorityOverSystemBehavior = YES;
rkey.wantsPriorityOverSystemBehavior = YES;
}
return @[ ukey, dkey, lkey, rkey ];
}
iCloud対応
- 参考記事
- iOSでのファイル保存
- iCloud Documentをつかうのがよさそう。
- 有料のDeveloperAcountがないと開発できないらしい。Cloud capabilityを追加するみたい
iOS版にカラーピッカーをつける
しゃべる機能をつける
- AVSpeechSynthesizer < Macは、NSSpeechSynthesizer 。MacのNSSpeechSynthesizerはsleep(1)の間に動作するが、AVSpeechSynthesizerは動かない
- AVSpeechSynthesizer の出力をファイルにおとす方法(obj-c)
- 終了検出 デリゲートをつかう
シェイクモーション
- iPhoneをふるとメニューがON/OFFする機能をつけた。実装方法は以下の命令を ViewControrer にいれる
// シェイクモーションの開始を検知
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) {
//NSLog(@"シェイクが開始されました。");ここに実行命令をいれる
}
}
// シェイクモーションの完了を検知
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) {
//NSLog(@"シェイクが終わりました。");
}
}
GameCenter のビルドエラー対応
- 2023年8月から、アプリストアに登録するときに、勝手に GameCenter 属性がつくようになり(ゲームでないのに)、ゲーム用情報入力が求められるようになりました。ひどい。解説記事
- GameCenter 設定をOFFにする設定を entitlement に追加する必要があります。
<key>com.apple.developer.game-center</key><false />
- 上をいれてもエラーがなくならない場合、Xcode > 左一番上>Signing & Capability タブで、左上の「+Capability」から「Game Center」をドラッグして右側に追加すると、ビルドエラーがでなくなりましたが、GameCenterがONに書き換えられました。

- GameCenterをOFFに戻すとエラー。アプリIDと矛盾という意味か?
Appleペンシルをつかった手書き入力
- iPadOS 15 から手書き文字認識がつかえる。設定>Appleペンシル>スクリブルをON
- 入力エリアで文字をかくと変換される。英語に変換される場合は、メモ帳を起動し、Aボタンで文字入力モードにし、パレットの設定で日本語に切り替える
- 文字を削除するには、文字の上をゴシゴシなぞる
- 文字の削除や改行をいれたい場合、ペンでカーソルを移動させ、入力パネルのタイトル領域を指でタッチして、ソフトキーボードを表示すればよいです。文字削除や改行が入力できます。
TestFlightで「テストを停止」してしまったときの処理
- TestFlight で、「テストを停止」を押してはいけない。押すとテストができなくなってしまう
- もし、押してしまったら、AppStoreConnectから、テスターとしてもう一度招待
iOS18で音声合成の動画出力がおかしくなった
- 音声ファイルを出力するときのフォーマットを「AVAudioPCMFormatInt16 」を「AVAudioPCMFormatFloat32」に変更する必要があった
<修正後>
[tts writeUtterance:utterance
toBufferCallback:^(AVAudioBuffer * _Nonnull buffer) {
NSError *err;
AVAudioPCMBuffer *pcmBuffer = (AVAudioPCMBuffer*)buffer;
if (!pcmBuffer) {
NSLog(@"Error");
return;
}
if (pcmBuffer.frameLength != 0) { //append buffer to file
if (output == nil) {
if(@available(iOS 18, *)){//しゃべる音声ファイル
output = [[AVAudioFile alloc] initForWriting:url
settings:pcmBuffer.format.settings
commonFormat:AVAudioPCMFormatFloat32
interleaved:NO
error:&err];
}else{
output = [[AVAudioFile alloc] initForWriting:url
settings:pcmBuffer.format.settings
commonFormat:AVAudioPCMFormatInt16
interleaved:NO
error:&err];
}
}
[output writeFromBuffer:pcmBuffer error:nil];
long msec = [output length];
double sampleRate=[output processingFormat].sampleRate;
aGG.talkingmsec = msec*1000. / sampleRate;
}
}];
「ios13以降」と以前を分けて書く書き方
- if(@available(iOS 13, *)){ //プログラム中の書き方、#if は次のように書く
- #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 #endif
マウスホバー座標の取得(iOS13以降)
- View(下の例では qvaeView)にUIHoverGestureRecognizerを追加する
if(@available(iOS 13, *)){//ホバー座標取得の追加
UIHoverGestureRecognizer *hoverGesture = [[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(handleHover:)];
[qvaeView addGestureRecognizer:hoverGesture];
}
}
- ホバー座標取得
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
- (void)handleHover:(UIHoverGestureRecognizer *)gesture {
CGPoint po = [gesture locationInView:qvaeView];
switch (gesture.state) {
case UIGestureRecognizerStateChanged: // マウスが動くたびに座標を取得
[qvaeView.qvae mouseMoved:(int)po.x qY:(int)po.y];
break;
case UIGestureRecognizerStateBegan:
//NSLog(@"ホバー開始: %@", NSStringFromCGPoint(location));
break;
case UIGestureRecognizerStateEnded:
//NSLog(@"ホバー終了");
break;
default:
break;
}
}
#endif