2017年9月30日土曜日

シミュレータだとメールが送れない

iOS8、Xcode6当時からずっと直しやがらないバグ。

メールを送信させるために
MFMailComposeViewController *mailPicker = [[MFMailComposeViewController alloc] init];
を書くわけだけど、これがiPhoneシミュレータ上だとnilのままになってしまい、
[self presentViewController:mailPicker animated:YES completion:nil];
で送信用ビューを表示しようとしても落ちる。
iPhone4だと一瞬表示されるけど引っ込んじゃってエラーを吐く。

高い給料と年会費取ってんだからさっさと直せや、ゴルァ!
Appleゥァア゛!
と強く思うけど、実機だと問題ないのでしょうがないけど我慢する。

2017年9月25日月曜日

Xcode 9でArchive時に変なチェックが

Xcode9でアプリを審査提出すべくArchiveしていたら、これまでと手続きが少し変わり、ValidateおよびUploadの段階でこのような同じメッセージが出たので覚書。

App Store distribution options

Include bitcode for iOS content
Allows the App Store to build your app to take advantage of hardware, software or compiler changes.
iOSコンテンツのビットコードを含める
App Storeがあなたのアプリケーションを構築して、ハードウェア、ソフトウェア、またはコンパイラの変更を利用できるようにします。
Upload your app's symbols to receive symbolicated reports from Apple
Crash logs and other diagnostic information from your customers will be symbolicated and viewable within Xcode.
Appleのシンボル化されたレポートを受け取るために、あなたのアプリのシンボルをアップロードしてください。
お客様のクラッシュログやその他の診断情報は、Xcode内で象徴的に表示されます。
最初のチェックのビットコードとは、アプリをコンパイルする時に、最終的に近藤マシン語臣に変換する前段階で、中間コードのようなものに変換するんだそうな。それのことのようで、含めておくといいことあるなんて読んだ。
なんのこっちゃよくわからんけどね。
最初bitcoinって書いてあるのかと思って、驚いた(笑)

次のチェックはおそらくクラッシュ情報なんかをアップルに送信していいかどうかだろう。今までは勝手に送信されてたんじゃないかって思うけど、コンプライアンス上明示し、選択できるようにしたんじゃないかと。

両方ともチェックされたままでいいと思う。

Re-sign "App name"

"App name" needs to be re-signed for App Store distribution.
Select one of the following signing options to continue.
あなたのAppは、App Storeの配布のために再署名する必要があります。
続行するには、次の署名オプションのいずれかを選択します。
App Storeで配布するための署名を自動で管理するか手動でするかの選択。これも上の自動のままでいいだろう。

Distoribution completed with warnings

その後のいつもの手順でUploadは終わったみたいなんだけど、また変なメッセージが出た。
iTunes Storeのオペレーションに失敗したと。
1024x1024のPNGフォーマットのマーケティングアイコンが含まれてないからApp Reviewかベータ App Reviewに提出できないと。
プロジェクトのImages.xcassetsのアイコンのところに1024ptのアイコンを登録する欄が増えとる…orz
他にもiPhoneとiPadでそれぞれ1x〜3xの20ptのアイコンの欄が…。めんどくせえ。
ScreenShot同様に、アイコンもでっかいの一個アップロードして、小さいのはそれを縮小して対応するようにできないのかね? 21世紀なのに…グチグチ…。

アイコン追加してアップロードし直して無事審査提出できたわい。
そのかわりiOS11やiPhone Xへの対応なんかが超テキトーだから、リジェクトされるかもね?(笑)
そんなもんにいちいち対応してられるか! 毎度毎度仕様を変えやがって!

2017年9月23日土曜日

Web関係のViewはどんどん新しく

Webを表示するためのViewは、もともとUIWebViewだったけど、いつの間にかこれdeprecate(使用不可)になってるのな。

かわりに、いつ登場したんだか知らんけど、WKWebView(WebKit View)がオススメになってる。
さらに、SFSafariViewControllerってのもあるようで。

SFSafariViewControllerはXcodeでSafariServices.frameworkをLinkeすると使える。
Xcode9現在、まだStoryboardからは直接扱えないみたいだけど、Safariとほぼ同等の機能が使えるようだ。
デフォルトでタブブラウジングができないものの、cookieとかのデータが共有できるんだと。

カスタマイズして使いたい人はWKWebView、できるだけカスタマイズせずに楽に使いたい人はSFSafariViewみたいな住み分けかしら? 最終的に一本化されるかもしれないけど。

2017年9月21日木曜日

TwitterKit3でツイートできない -> できた

Twitterは公式SDK使用になった

iOS11にアップデートして、自分のアプリからツイッターに投稿しようとしたら、

if SLComposeViewController.isAvailable(forServiceType: SLServiceTypeTwitter) { }
のところで値が false になっちまいまして…orz
むろん今まではうまくいってた。

どうやら、他社のサービスは他社のやり方でやれ、Appleはしらんわ…ということらしい。まあセキュリティの問題とかいろいろあるせいとは思うけど、めんどくさくなるのは勘弁してほしい。
ツイッター公式のTwitter kitというSDKがあって、今後はそっちを使わねばいけなくなるようだ。

詳しくは上のサイトのやり方に従って欲しいが、
  1. ツイッターにサインインしてアプリ情報などを入力
    1. 【重要】PermissionがRead & Writeになってないので、Update Settingsボタンを押してやって直す。(詳しくは後述)
  2. Twitter Kit SDKをダウンロードしてXcodeのプロジェクトにインストール(たいていはLinked Frameworks and Librariesに勝手に入ってくれると思う)
  3. Info.plistに項目追加
  4. ソースコードを編集
という手順になるようだ。

SafariServices.framework、AVFoundation.framework、CoreMedia.framework の3つも必要という情報もあったのだが、少なくともTwitterKit3、Xcode9、iOS11においては不要だった。

TwitterKitをゲット

さっそくツイッターのアプリ登録サイトに情報を入力してみたが…

ツイッターアカウントに電話番号情報が必要

…無情にもはじかれる。
You must add your mobile phone to your Twitter profile before creating an application. 
アプリケーションを作成する前に、携帯電話をTwitterプロファイルに追加する必要がありマース。
自分のツイッターアカウントに、セキュリティ用の携帯電話番号を登録しないといけないようだ。
普通にツイッター使うだけならいらないから無視してたんだけど、とうとうこの日がやってきてしまったか。

携帯電話番号登録のメリットとは!?

  • アカウントのセキュリティ
  • アカウント回復にかかる時間が短縮
  • ショートメールを介してTwitterを使用
  • 友だちがあなたのアカウントを見つけやすくなる
    • やめてくれ! むしろ見つけにくくしてくれ!
ツイッターがハッキングされて番号情報が漏れたら嫌だなあと思いつつ、やむをえずTwitter for iOSから携帯電話番号の登録をした。
登録した携帯にショートメールで認証番号が届くので、それを入力してやったらすぐにさっきのページが通ったよ。
これのConsumer Key (API Key)ってのを、後で自分のプロジェクトのInfo.plistに書き込む。
また、このConsumer Keyと、Keys And Access TokensタブにあるConsumer Secret (API Secret)をプロジェクトのAppDelegate内に書き込むことになる。

TwitterKit ダウンロード

現時点のバージョンは
CocoaPodsというiOSライブラリ管理ツールを使うと、手作業でいちいちダウンロードしてプロジェクトに追加して…ってやらずに済むらしいんだけど、最初にコマンドライン使ったりするようで、俺にとってはかえって敷居が高いので、今回は手作業でやってみる。

このページ中ほどの Install Twitter Kit Manuallyにある
1. Download and unzip Twitter Kit.
からダウンロード。

プロジェクトにインストール

この中の左3つを自分のプロジェクトにインストールしてやる。
右2つはTwitterKit.frameworkに内包されてるエイリアスなんだけど、別物と考えてそのままインストールする。

Info.plistの設定

Info.plistに以下の項目を追加する。
プロパティリストのままでもいいし、右クリックでOpen AsからSource Code表示にしてもいい。
// Info.plist
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>twitterkit-ここに取得したconsumerKeyを書く</string>
    </array>
  </dict>
</array>
<key>LSApplicationQueriesSchemes</key>
<array>
    <string>twitter</string>
    <string>twitterauth</string>
</array>

コードを書きませう

AppDelegate

TwitterKitのimportは言わずもがな。
import TwitterKit

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        //TwitterKit初期設定
        Twitter.sharedInstance().start(withConsumerKey: "ここにConsumerKeyを書く", consumerSecret: "ここにConsumer Secretを書く")
        
        return true
    }

//Twitterにログイン時に、認証トークンをディスクにリダイレクト(転送)し保存
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
        
    return Twitter.sharedInstance().application(app, open: url, options: options)
    }

ツイートする処理

    //ツイッターへツイート
    @IBAction func actionBtn(_ sender: AnyObject) {
        //Twitterログイン認証トークンの有無を確認
        if Twitter.sharedInstance().sessionStore.hasLoggedInUsers() {
            //ツイート
            let composer = TWTRComposer()
            composer.setText("イニシャルテキスト") //初期テキスト
            composer.setURL(URL(string: "リンクのURL")) //リンク
            composer.setImage(UIImage(named: "リンクのURL")) //画像
            
            composer.show(from: self) { (result) in
                if result == TWTRComposerResult.done {
                    print("ツイートされた")
                } else {
                    print("ツイートできんかった")
                }
            }
        } else {
            //Twitterログイン処理
            Twitter.sharedInstance().logIn(completion: { (session, error) in
                if session != nil {
                    print("signed in as \(String(describing: session?.userName))")
                } else {
                    print("error: \(String(describing: error?.localizedDescription))")
                }
            })
        }
    }

うまくいかん!

以上でツイートできるはずなのだが、結果は失敗。

今までのSLComposeViewControllerのちっちゃいウィンドウと違い、もっとちゃんと大きなツイートのウィンドウが開き、画像もURLも貼られてるのに、ツイートボタンを押してもツイートされない。

エラーメッセージとしては以下のものが出るが、さっぱりわからん。
CredStore - performQuery - Error copying matching creds.  Error=-25300, query={
    class = inet;
    "m_Limit" = "m_LimitAll";
    ptcl = htps;
    "r_Attributes" = 1;
    sdmn = "https://api.twitter.com";
    srvr = "api.twitter.com";
    sync = syna;
}

Did encounter error sending Tweet: Error Domain=TWTRNetworkingErrorDomain Code=-1011 "Request failed: unauthorized (401)" UserInfo={NSLocalizedFailureReason=, TWTRNetworkingStatusCode=401, NSErrorFailingURLKey=https://api.twitter.com/1.1/statuses/update.json, NSLocalizedDescription=Request failed: unauthorized (401)}

[TwitterKit] Composer did fail: Error Domain=TWTRNetworkingErrorDomain Code=-1011 "Request failed: unauthorized (401)" UserInfo={NSLocalizedFailureReason=, TWTRNetworkingStatusCode=401, NSErrorFailingURLKey=https://api.twitter.com/1.1/statuses/update.json, NSLocalizedDescription=Request failed: unauthorized (401)}

調べると他の人も同じことで困ってる人がいるみたい。ここにも。ここにも。
ツイート内に「^ < > \ | ` または \r」の文字が使われていると401エラーになるそうだが、使ってないしなあ。

Fabricってのが必要?

2014年10月にTwitter社が発表したモバイルアプリ開発プラットフォーム。
安定性、利用者増、収益化、ユーザー認証の側面を網羅した開発キットだそうだ。
どうやらTwitterアプリを開発するにあたってこいつを使ってる人が多いらしいのだが、ツイートするだけとかならいらないのかもしれず、Twitter開発者のサイトでも「Upgrade from Fabric」として少ししか書かれてない様子。
もしどうしても必要なら調べて使うけど…??

うまくいった!

原因はTwitterKitのWebにある管理画面のパーミッションの設定で、表示と実際が違っていたためだ。
Read and Writeが嘘っぱちだった!
デフォルトの設定は表面上Read and Writeになってるんだけど、これが実際はRead onlyになっているものだから、ツイートしようとしても失敗してしまうのだ。
そりゃよっぽど注意しないと気づかねえってば。
責任は完全にTwitter社にある。

アカウント連携時の注意書きを見ると、ツイートできませんって!
それじゃツイートできるわきゃないよ。

直し方

管理画面でPermissionsタブの下の方にあるUpdate Settingsを押してやると、選ばれたとおりのPermissionに直ってくれる。
Fabricとかは全然いらない。
ツイッターゥァア゛ーーー
こんなもので何日間も悩ませやがって!

2017年9月20日水曜日

Swift 4リリース

Xcode 9でさっそくSwift 4がリリースされた。
2.x → 3 ほどの大きな変更はないと思うが、気付いた点があったら追記していく。

@objc推論の廃止

Dependency Analysis Warning
The use of Swift 3 @objc inference in Swift 4 mode is deprecated. Please address deprecated @objc inference warnings, test your code with “Use of deprecated Swift 3 @objc inference” logging enabled, and then disable inference by changing the "Swift 3 @objc Inference" build setting to "Default" for the "アプリ名" target. 
Swift 4モードでのSwift 3 @objc推論の使用は廃止されました。
推奨されない@objcの推論のWarningに対処し、「廃止されたSwift 3 @objc推論の使用」ロギングを有効にしてコードをテストし、 「アプリ名」 targetの 「Swift 3 @objc Inference」ビルド設定を 「Default」にしてください。

——Swift 3で作ったコードを開いたら出た。よくわからんのだけど、Objective-CのクラスとかをSwiftで扱う時に型を推定してくれてたのかな? んで、それが廃止になって、型変換とかを自分で明示的にやれ…とかそんな意味か?
別にObjective-Cのコードとか使ってなかったけど、Xcode 8でデフォルトでそういう設定だったんだろう。それなら自動で直してくれりゃいいのに。グチグチ。
ここをDefaultに変えるとWarningが消える
Build Setting、TARGETSにはアプリ名とアプリ名+Testsの二つがあるが、その両方にSwift 3  @objc inferenceの設定があるので、両方変えてやる必要がある。

#selectorの書き方?

Timer.scheduledTimer(timeInterval: 0.5,
                  target: self,
                selector: #selector(IdleEvents.filterUpdate(timer:)),
                userInfo: nil,
                 repeats: true)

こういうコードをSwift 3で書いてたんだけど、#selectorのところに以下のエラーが。
2番目のエラーにはFixってボタンが出るんでそれを押すと消えるんだけど、一時的なものらしく、また出たり、@objcってのをコード中の変なところに挿入されたり、なんだかよくわからん!
Argument of '#selector' refers to instance method 'filterUpdate(timer:)' that is not exposed to Objective-C
Add '@objc' to expose this instance method to Objective-C

どうやら、#selectorで呼び出す関数、メソッドの頭に「@objc」ということらしい。
@objc func hogehoge() { … }

んで、そんなこんなやってたら自作のクラスにExpected declarationというエラーが出るようになった。
調べたらコードの最後に「@objc」が付けられてた。さっきのFixボタンがいい加減なところに挿入しやがったんだな。

'NSFontAttributeName' has been renamed to 'NSAttributedStringKey.font'

Replace 'NSFontAttributeName' with 'NSAttributedStringKey.font'

'NSForegroundColorAttributeName' has been renamed to 'NSAttributedStringKey.foregroundColor'

Replace 'NSForegroundColorAttributeName' with 'NSAttributedStringKey.foregroundColor'

'NSShadowAttributeName' has been renamed to 'NSAttributedStringKey.shadow'

Replace 'NSShadowAttributeName' with 'NSAttributedStringKey.shadow'

Xcode 9の変更点

2017/9/20にアップデートされたXcode 9について、Mac AppStoreの説明文の訳。

Xcode 9には、iOS11、watchOS 4、macOS High Sierra 10.13用のSwift 4とSDKが含まれています。
  • リファクタリングにより、Swift、Objective-C、C、およびC ++コードの構造を簡単に変更できます。
  • コードエディタは驚くほど速く応答性があり、Markdown構文のネイティブサポートを追加します。
  • Fix-itsはワンクリックでコードを複数改善し、必要なプロトコルメソッドを追加することもできます。
  • 新しいソースコントロールナビゲータと統合されたGitHubアカウントにより、チーム全体のコードを簡単に管理できます。
  • ネットワーク上のデバイス上のiOSおよびtvOSアプリケーションのワイヤレスインストールとデバッグ。
  • シミュレータは実際のデバイスのように見え、動作し、一度に複数のデバイスをシミュレートできます。
  • iOSのPlaygroundテンプレートは、XcodeとiPadのSwift Playgroundの両方で動作するドキュメントを作成します。
  • Find navigatorは非常に高速で、結果はすぐに表示されます。
  • プロジェクトナビゲータは自動的にファイルとグループをFinderとソースコントロールと同期させます。
  • XcodeサーバーはもはやmacOSサーバーを必要とせず、Xcode環境設定で完全に設定することができます。
  • 次世代のビルドシステムは、多くのプロジェクトを構築するとき(オプションで有効)、信頼性とパフォーマンスを向上させます。
  • Swift 4コンパイラは、Swift 3コードを構築して、一度に1つのモジュールで段階的な移行を可能にします。
  • 最新のSDKには、機械学習のためのCore ML frameworkと、拡張現実のためのARKitが含まれています。
えーと、Swift 4がもうリリースされたのだね Σ(((°Д°;))))ガクガク

Photo Libraryへのアクセスについて

フォトライブラリの画像を読み書きするとき、これまではInfo.plistの「Privacy - Photo Library Usage Description」にだけ「フォトライブラリにアクセスします」とかのメッセージを設定すれば良かったけど、「Privacy - Photo Library Additions Usage Description」にも「フォトライブラリに画像を追加します」とかのメッセージの設定が必要になったようだ。
設定しておかないとアプリが落ちるので困る。

2017年9月19日火曜日

URLスキーム/URLで地図アプリを開く

やりたいこと

緯度経度の位置情報を持ったリンク(たとえばTwitterのツイートとか)を押すと、自動的に地図アプリを起動して指定した場所を表示してくれるようにしたい。

こういうのをURLスキームという。

2種類のスキーム

Appleのマップ用

iOSの場合のURLスキームは以下のものとなる。
http://maps.apple.com/maps?パラメータ

これはApple純正マップ.appがインストールされているデバイスではマップ.appが優先的に起動し、非インストール機(WindowsやAndroid含む)ではGoogleMapが起動される(GoogleMapアプリがなければブラウザ上で)。

GoogleMaps専用

詳しくは公式ガイドページ
マップ.appがインストールされたデバイスでもGoogleMapを起動することもできる。
https://maps.google.com/?パラメータ
https://www.google.com/maps/preview/パラメータ

こちらはMacなら直接ブラウザで、iOSのTwitterアプリならそれの子プロセスとしてWebビューが開く。
iOSの場合、右上のアイコンからSafariで開き、さらにSafariで開いた画面上部に"Google Maps" Appで開くという表示から、GoogleMapsアプリで開くこともできる(アプリがインストールされていればだが)。
試してないがWindowsでもAndroidでも同じような感じだろう。
GoogleMapsアプリで開くまでが少し手間だが、難しいことではないし、たいていは子プロセスとしてのWebビューで事足りるだろう。

comgooglemaps://?パラメータ
などという方法もあるが、こちらは直接ブラウザを経由せずにGoogleMapアプリを開くためのもので、ツイートに直接書き込んでもURLとして認識されない。
ブラウザのURL欄に書いた場合、iOSでは直接GoogleMapsアプリが開けたが、アプリがないMacだと当然ダメ。素直にhttps〜形式の方がいい。

どっちの方がいいか

機能としてはGoogleMapsの方がStreetViewがあったりと高機能なので、当面は後者のURLスキームの方が良さそうだ。
同じ緯度経度、表示範囲(拡大率)を維持したまま相互を切り替えられるといいんだけどな。

2017年9月17日日曜日

Objective-CのコードをSwiftに移植

Swift3にもなって、そろそろObjective-Cで作ったアプリをSwift化した方がいいと思うようになった。

メンテナンスのたびにObjective-Cを思い出しながら作業するのも面倒。
いつアップルが「もうObjective-Cのサポートやめるから、これからは審査通さんからね」って言い出しかねない。
なんだかんだ言ってObjective-Cのコードはごちゃごちゃしてることが多い。ヘッダファイルやimportも必要だし。

そんなわけで今さらだが、Swiftに移植してみることにした。

以下、調べたことのメモ

  • 同じプロジェクト内でクラスのファイル(ViewController.h、同.mとか)ごとに移行可能。
  • クラスのファイル名は拡張子が別になるので、Objective-CとSwiftで同居可能。(ViewController.h、同.m、同.swift)
  • 最初にSwiftファイルを追加するときに「アプリ名-Bridging-Header.h」というファイルを作るかどうか聞かれるので、作っておく。

手順

移行するクラスファイルと同名のSwiftファイルを作ってコードを移植。
Objective-Cの.hと.mのファイルを削除。
Storyboardに結びついてるならCustom ClassのModuleがNoneになってるので、そこをプロジェクト名に直す。(最初にCurrentに直すとも?)
IBOutletやIBActionの結びつきを直す。

Objective-C、Swift相互の呼び出し

Objective-CのクラスからSwiftのクラスを呼ぶ

Objective-CのクラスからSwiftのクラスを呼ぶには、呼び出し元のObjective-Cの実装ファイル(.m)
#import "アプリ名-Swift.h"
を追加。
たとえばアプリ名がMyAppだったら
#import "MyApp-Swift.h"
とする。

ただ、なんかうまく認識してくれないことがあって、よくわかんねえんだ…。(´・ω・`*)
importしたら、すぐにそのクラスを認識してコードのサジェスト(推薦候補)を表示してくれるようになるわけじゃないようで、一度ビルドかけるとようやく認識してくれたり。それでも認識されなかったり。
このあたりはもっとしっかり作ってくれるといいんだけどね、Xcode。どうせSwiftへ移行する過渡期だけの仕様だから、いい加減なのかねえ?

SwiftからObjective-Cを呼ぶ

逆にSwiftからObjective-Cを呼ぶには作っておいた「プロジェクト名-Bridging-Header.h」に呼び出し先のObjective-Cファイルをimportする。

でいいらしい。

AppDelegateの移植

AppDelegateも同様に移植する。

main.mの削除

Objective-CではSupportingFilesフォルダ中にmain.mが作られてるけど、これが残ってるとかえってエラーになるので、削除する。

@UIApplicationMain

Swiftの普通のAppDelegateを参考にすりゃわかるけど、class AppDelegateの前に@UIApplicationMainってのを書いてやる。
多分これで最初に呼ばれるファイルに指定されるんだろう。

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

2017年9月14日木曜日

TableViewを使う


Xcode右下の部品一覧からUITableViewControllerをStoryboardにドロップすると簡単なんだけど、なぜかTableViewにConstraintsが設定できない。
一番上のCellがステータスバーに重なってしまうなどよろしくない。

普通のUIViewControllerにUITableViewをドロップし、UIViewの子Viewにすればこれは回避できる。
その際はTableViewとViewControllerをdelegateとdataSourceで結んでやること。(Storyboardの黄色いアイコンまで右ボタンドラッグ)

TableViewのStoryboard IDとか、TableViewCellのRestoration IDとかも設定した方がいい。

また、ViewControllerに結びつけるSwiftのクラスはUIViewControllerを親クラスとし、UITableViewDelegateと同DataSourceを設定してやる。

class TableViewController: UIViewController, UITableViewDelegate,UITableViewDataSource {
}

さらに、delegateメソッドはoverrideにしない。

2017年9月12日火曜日

乱数の関数メモ

いろいろある乱数発生の関数をメモ

arc4random()
返り値の型はUInt32なので、Swiftみたいに型をきっちり指定してやらないといけない言語の場合、ちょい面倒。

arc4random_uniform(__upper_bound: UInt32)
0〜引数-1までのランダムな整数を返す。
arc4random_uniform(10)なら0〜9まで。使い勝手良さげ。


drand48()
返り値の型はDoubleらしい。
0.0〜1.0の間の負でない倍精度の浮動小数点値を返す。
UIColorのrgb値が0.0〜1.0とこれにドンピシャガンガンなので、いちいちarc4randomとか使わんでいい。
そのかわりUIColor(red: , green: , blue: , alpha: )の引数がCGFloatなので、
let fontColor = UIColor(red: CGFloat(drand48()), green: CGFloat(drand48()), blue: CGFloat(drand48()), alpha: 1.0)
とかやらんといかんけどね。
今知ったばかりなんでよくわかってないw

UICollectionViewを使う際の注意

つまった点のメモ。
右クリックのドラッグで黄色のアイコンと結んでやる
StoryboardでUICollectionViewの部品をUIViewControllerの上にドロップするのはいい。
その後、ドロップしたそれをマウス右ボタンクリックでそのStoryboardの上にある黄色いアイコンにドラッグし、ポップアップしたメニューからdataSourceを選んで接続してやる必要がある。
これに気づかずに「コード合ってんのに表示されねー」ってだいぶ悩んだ。

あとはUICollectionViewCellのReusableViewのIDの設定とか、protocolになってる
UICollectionViewDelegate, UICollectionViewDataSource
をclassのところで設定してやるとか、その辺がつまづきやすい。

ところで、UIViewControllerにUICollectionViewを貼るよりも、XcodeのUI部品一覧の中にUICollectionViewControllerってのがあるから、それをStoryboardにドラッグした方がdataSourceとdelegateの設定もされるから早いみたいね ^^;

2017年9月6日水曜日

StoryboardでScrollViewのコンテンツを配置

UIScrollView上に配置するUIをStoryboardで配置しようとすると、iPhoneやiPadのサイズからはみ出したUIが隠れてしまう。
ScrollViewの上にMapViewと紫色のViewが乗ってる

そこで、そのStoryboardのViewControllerを選択し、SizeInspectorのSimulated SizeをFreeformにし、Height(もしくはWidth)を必要な値に変更してやれば、Storyboardがその長さまで広がってくれるのでUIの配置が簡単にできるようになる。


MapViewに表示できるpinの数

1000個くらい一度に表示できないものか、できるなら表示速度はどんなもんじゃろうと試してみた。

横並びの制限?

経度1度ごとに横に並べるようにしたら、なぜか411個という中途半端な個数が最大だった。
なぜか411個が限界

ランダム配置なら制限なし?

しかし世界中のランダムな位置に刺すようにしたところ、1000個でも表示できた
1000個でもいけた
昔のパソコンのスプライトじゃあるまいし、横位置に並べられる制限があるのかしら?

なお表示速度はどちらも一瞬で、目立った遅延もないようなので気にしなくて良さそう。

10,000個でも大丈夫

10,000個にもチャレンジしたら、表示できたが、さすがにspanを広くしてpinがいっぱい表示されるようにしたら重くなった。(iPad Air2)
でもちょっと引っかかる程度で、実用上さほど問題あるとも思えなかった。spanを狭く取ればスイスイだし。
10,000個もpinを表示する機会は滅多にないよね。
10,000個でもいけたがちょっと重い


override func viewDidLoad() {
        super.viewDidLoad()
        
        //pin1000個追加
        var annotationArr = Array<MKAnnotation>()
        for i in 1...1000 {
            let anno = MKPointAnnotation()
            let longi = Double(arc4random() % UInt32(360)) - Double(180)
            let lat   = Double(arc4random() % UInt32(180)) - Double(90)
            print(i)
            anno.coordinate = CLLocationCoordinate2DMake(Double(lat), Double(longi))
            anno.title = "経度:\(longi) 緯度:\(lat)"
            anno.subtitle = "番号 \(i)"
            annotationArr.append(anno)
        }
        self.myMapView.addAnnotations(annotationArr)

        //1000番目のpinの位置をマップのデフォルト位置に
        let lat = annotationArr.last?.coordinate.latitude
        let lng = annotationArr.last?.coordinate.longitude
        let coordinate = CLLocationCoordinate2DMake(lat!, lng!)
        let span = MKCoordinateSpanMake(0.05, 0.05) //表示範囲(拡大)
        let region = MKCoordinateRegionMake(coordinate, span)
        self.myMapView.setRegion(region, animated: true)

    }


ちなみに以前勉強した時はMKPointAnnotationを使わずにカスタムクラスを作ってannotationを管理するようにって読んだんだけど、単純に表示するだけならMKPointAnnotationで事足りるよね? よくわかんないけどいいか。

色を変えてみる

1000個表示でも少し重い
ピンの色を変えるにはMapViewのdelegateメソッドを使う必要がある。

ピンの緯度経度、タイトル/サブタイトル、色やアイコンのデザインまで一括してプロパティに持つクラスがありゃいいのにと思うけど、Appleの技術者様の考えることはわからない。
しょうがないので、みんなカスタムクラスを作ってるみたい。

表示は少し重くなる。ピンを表示するごとにdelegateメソッドが呼ばれるせいだろう。
数が少なければ気にならないと思う。


//pin1000個追加の後あたりに以下を追加
self.myMapView.delegate = self

さらにメソッドを追加。
    //pinのデザインを変えるにはdelegateメソッドを使う
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        let annoView = MKPinAnnotationView()
        annoView.annotation = annotation
        let r = Float(Double(arc4random() % 10) / 10)
        let g = Float(Double(arc4random() % 10) / 10)
        let b = Float(Double(arc4random() % 10) / 10)
        annoView.pinTintColor = UIColor.init(colorLiteralRed: r, green: g, blue: b, alpha: 1.0)
        annoView.canShowCallout = true
        
        return annoView
    }

画像を変えてみる

画像2種類+色を変えたピン
意外と軽い
今度は画像を変更して見たところ、動きが軽い。
色を変えないピンよりは引っかかるが、色を様々に変えたものよりは軽いのだ。
delegateメソッドを繰り返し呼ぶのがボトルネックになっているのではなく、新たに色が違う画像を生成するか、キャッシュされた画像を再利用するかの点で違いが出てるのかも?
もっと画像の種類を増やすと違いが出てくるだろう。

なお、画像の中心(9*9の画像なら5,5の位置)が設定した緯度経度の位置になるようだ。
同じ緯度経度のデフォルトのピンと重ねてみるとよくわかる。

2017年9月3日日曜日

ARとモーションセンサー覚書

勉強中のARプログラミングと、CMMotionManager( )を使ったモーションセンサー等についてメモっとく。

ジンバルロック

詳しくはググってほしいけど、3軸の姿勢センサーの2軸の動きが重なってしまい、姿勢が正しく計測できなくなること。
姿勢センサーを使ったアプリで、時々「∞の字に動かしてください」とか出るのはこれをリセットするためか?

Pitch, Roll, Yaw値の範囲

CMMotionManager( )のAttitudeとして得られるそれぞれの値の範囲(度数法の角度で)
  • Pitch
    • -90 〜 0 〜 90
    • デバイスを机に画面を上で寝かせた状態が0
    • 垂直に起こした状態が90
    • デバイス上部を垂直に下にした状態が-90
  • Roll
    • -180 〜 0 〜 180
    • デバイスを机に画面を上で寝かせた状態が0
    • 左に傾けて起こしていくとマイナスになって、完全にひっくり返すと-180
    • 右に傾けて起こしていくとプラスになって、完全にひっくり返すと180
  • Yaw
    • -180 〜 0 〜 180
    • デバイスを机に画面を上で寝かせて西を向けた状態が0
    • 反時計回りでプラスになって、東で180
    • 時計回りでマイナスになって、東で-180