20190711のiOSに関する記事は9件です。

【Swift】 UITextFieldの左側に余白を設ける

前置き

今回はほぼ前置きですが。

自作アプリもだいぶイメージができてきて、UITextFieldのplaceHolderにも文字を入れたりして、全体的な見た目を確認したりしてたんですが、どうも文字列が左端ぴったりに設定されて余白が全然ない感じがよろしくない。

↓の画面のようなイメージですね。
Screen Shot 2019-07-11 at 23.30.41.png

左端から少し余白を設けて(要はpadding)、見た目よくしたかったんですが、どうも調べてみるとUITextFieldのカスタムクラスを作って、textRect(forBounds bounds: CGRect)のような関数を設定する、といった手法ばっかりで、おおかた画面イメージも出来てきてるところに今からそんな修正を入れるのも正直面倒。
というわけで、もう少し調べてみると、さらっと解決できる方法がありました。

Screen Shot 2019-07-11 at 23.36.32.png

少し左側に余白ができているのがお分かりいただけると思います。

ソース

実装というより、おまじないのように書いていたある記述が悪さをしていただけ、というハナシなのですが。

ViewController.swift
        textField.borderStyle = .none
        textField.layer.cornerRadius = 5
        textField.layer.masksToBounds = true

UITextFieldの枠線の角に丸みを持たせるために、こういう書き方をしていたのですが、これの一番上の行、textField.borderStryle = .noneという記述があると、textField内の文字列が左にきっちり詰めた状態になるようです。
なので、対処としては、この1行を消してあげるだけです。
(上で画像を貼っているとおり、角の丸みの設定はきちんと残っています)

ViewController.swift
        // textField.borderStyle = .none
        textField.layer.cornerRadius = 5
        textField.layer.masksToBounds = true

もし同様の事象で対処法を探っている方がこれを読まれているのであれば、textField.borderStyle = .roundedRectように明示的に.borderStyleの設定を.none以外に変えてみるのは、対処の一つとして試してみてもよいかもしれません。

まとめ、ポイント

今回は何か新しいやり方を知った・気づいた、というより、「いつもこう書いてるから」という理由で盲目的にソースをコピペして必要な部分だけを書き換えるようなやり方をしていると、実はそのロジックが悪さをしていることに気づきにくい、という教訓を得た感じでした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

BitriseのWorkflowをSlackから起動してiOSのデバイス登録をしよう!

TL;DR

  • Slackのslash commandの使い方が(なんとなく)わかるよ
  • BitriseのWorkflowをSlackから起動する方法が(なんとなく)わかるよ
  • fastlaneでデバイス登録する方法が(なんとなく)わかるよ
  • fastlaneで証明書更新する方法が(なんとなく)わかるよ

前提

  • Bitriseの使い方がある程度わかる
  • fastlaneの使い方がある程度わかって, matchで証明書を管理している

手順

  1. fastlaneにデバイス登録, 証明書更新をするlaneを作る
  2. Bitriseにデバイス登録, 証明書更新するWorkflowを作る
  3. BitriseからWebhook URLを取得する
  4. Slackのslash commandを登録する

まずはlaneを作ろう

Fastfileに下記の様な内容を登録

Fastfile
# このlaneをBitriseから起動
lane :add_device_and_update_certificates do
  add_device(
    name: ENV["DEVICE_NAME"], # Slackから環境変数で指定
    udid: ENV["UDID"] # Slackから環境変数で指定
  )
  update_certificates
end

# デバイス登録をする
lane :add_device do |options|
  if options[:name] && options[:udid]
    # こちら参照->https://docs.fastlane.tools/actions/register_devices/
    register_devices(
      devices: {
        options[:name] => options[:udid]
      }
    )
  else
    UI.error "Usage: fastlane add_device name:'New device name' udid:'UDID'"
  end
end

# 証明証を更新する
lane :update_certificates do
  # こちら参照->https://docs.fastlane.tools/actions/match/
  match(
    type: "development",
    app_identifier: "tihimsm.app",
    force_for_new_devices: true, # デバイスが追加されていた場合証明書を更新する
    readonly: false
  )
end

一つのlaneにregister_devicesとかmatchとか直接書いてもいいんだけど, 一応単品で使える様にlane分けてます.

BitriseにWorkflowを設定しよう

↓みたいな感じでfastlane使えるWorkflowを作ります
スクリーンショット 2019-07-11 15.18.57.png
そして↓のようにlane名(add_device_and_update_certificates)を指定します
スクリーンショット 2019-07-11 15.20.37.png

fastlaneもBitriseも適宜公式ドキュメント見て確認してくださいね.

BitriseからWebhook URLを取得しよう

codeタブのINCOMING WEBHOOKSのブロックからURLを取得します.
SETUP MANUALYを選択し, プルダウンからSlackを選択して, 表示されたURLをコピーしときます.
スクリーンショット 2019-07-11 15.23.59.png

Slack commandを作ろう

SlackのサイドバーのAppsにある「+ボタン」をクリックします.
Appsの中から「Slash Commands」を選択しましょう.
スクリーンショット 2019-07-11 15.28.28.png
「Add Configuration」から進み, 適当なコマンド名をつけちゃいます.
スクリーンショット 2019-07-11 15.30.49.png
Slash Commandを作成すると↓のような画面が表示されるので, URL覧に先ほどBitriseから取得したURLを入力しましょう.
スクリーンショット 2019-07-11 15.31.37.png
あとはアイコンの設定や, コマンドのヒントなどは任意で設定してください.

あとはSlackでコマンドを叩きましょう

こんな感じの長ったらしいコマンドをSlackで入力します.
/test_command workflow: add_device_and_update_certificates|b: develop|ENV[DEVICE_NAME]: デバイス名|ENV[UDID]: UDID

  • workflow: BitriseのWorkflow名
  • b: githubのブランチ名
  • ENV[DEVICE_NAME]: デバイス名が入ってfastlaneに渡されます
  • ENV[UDID]: UDIDが入ってfastlaneに渡されます

詳しくは公式ドキュメントをご覧ください.

まとめ

いかがでしたでしょうか?
デバイス登録&証明書更新はiOS開発の面倒な作業の一つですが, コマンド一つで簡単にできるようになります.

複数証明書を更新したい場合などはupdate_certificatesの中でループさせるなどすれば, 一気に複数証明書を更新も可能です.

もっと良い方法知ってる方たくさんいると思うので, CI周りの特殊な訓練を受けている方々のご意見お待ちしております.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

BitriseのworkflowをSlackから起動してiOSのデバイス登録をしよう!

TL;DR

  • slackのslash commandの使い方が(なんとなく)わかるよ
  • Bitriseのworkflowをslackから起動する方法が(なんとなく)わかるよ
  • fastlaneでデバイス登録する方法が(なんとなく)わかるよ
  • fastlaneで証明書更新する方法が(なんとなく)わかるよ

前提

  • Bitriseの使い方がある程度わかる
  • fastlaneの使い方がある程度わかって, matchで証明書を管理している

手順

  1. fastlaneにデバイス登録, 証明書更新をするlaneを作る
  2. Bitriseにデバイス登録, 証明書更新するworkflowを作る
  3. BitriseからWebhook URLを取得する
  4. slackのslash commandを登録する

まずはlaneを作ろう

Fastfileに下記の様な内容を登録

Fastfile
# このlaneをBitriseから起動
lane :add_device_and_update_certificates do
  add_device(
    name: ENV["DEVICE_NAME"], # slackから環境変数で指定
    udid: ENV["UDID"] # slackから環境変数で指定
  )
  update_certificates
end

# デバイス登録をする
lane :add_device do |options|
  if options[:name] && options[:udid]
    # こちら参照->https://docs.fastlane.tools/actions/register_devices/
    register_devices(
      devices: {
        options[:name] => options[:udid]
      }
    )
  else
    UI.error "Usage: fastlane add_device name:'New device name' udid:'UDID'"
  end
end

# 証明証を更新する
lane :update_certificates do
  # こちら参照->https://docs.fastlane.tools/actions/match/
  match(
    type: "development",
    app_identifier: "tihimsm.sugoiyapp",
    force_for_new_devices: true, # デバイスが追加されていた場合証明書を更新する
    readonly: false
  )
end

一つのlaneにregister_devicesとかmatchとか直接書いてもいいんだけど, 一応単品で使える様にlane分けてます.

Bitriseにworkflowを設定しよう

↓みたいな感じでfastlane使えるworkflowを作ります
スクリーンショット 2019-07-11 15.18.57.png
そして↓のようにlane名(add_device_and_update_certificates)を指定します
スクリーンショット 2019-07-11 15.20.37.png

fastlaneもBitriseも適宜公式ドキュメント見て確認してくださいね.

BitriseからWebhook URLを取得しよう

codeタブのINCOMING WEBHOOKSのブロックからURLを取得します.
SETUP MANUALYを選択し, プルダウンからSlackを選択して, 表示されたURLをコピーしときます.
スクリーンショット 2019-07-11 15.23.59.png

slack commandを作ろう

SlackのサイドバーのAppsにある「+ボタン」をクリックします.
Appsの中から「Slash Commands」を選択しましょう.
スクリーンショット 2019-07-11 15.28.28.png
「Add Configuration」から進み, 適当なコマンド名をつけちゃいます.
スクリーンショット 2019-07-11 15.30.49.png
Slash Commandを作成すると↓のような画面が表示されるので, URL覧に先ほどBitriseから取得したURLを入力しましょう.
スクリーンショット 2019-07-11 15.31.37.png
あとはアイコンの設定や, コマンドのヒントなどは任意で設定してください.

あとはSlackでコマンドを叩きましょう

こんな感じの長ったらしいコマンドをslackで入力します.
/test_command workflow: add_device_and_update_certificates|b: develop|ENV[DEVICE_NAME]:デバイス名|ENV[UDID]:UDID

  • workflow: Bitriseのworkflow名
  • b: githubのブランチ名
  • ENV[DEVICE_NAME]: デバイス名が入ってfastlaneに渡されます
  • ENV[UDID]: UDIDが入ってfastlaneに渡されます

詳しくは公式ドキュメントをご覧ください.

まとめ

いかがでしたでしょうか?
デバイス登録&証明書更新はiOS開発の面倒な作業の一つですが, コマンド一つで簡単にできるようになります.

複数証明書を更新したい場合などはupdate_certificatesの中でループさせるなどすれば, 一気に複数証明書を更新も可能です.

もっと良い方法知ってる方たくさんいると思うので, CI周りの特殊な訓練を受けている方々のご意見お待ちしております.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】iOSアプリでよく見る『既に開いているタブをタップして、一番上までスクロール』の実装方法

概要

iOSアプリでよく見るこの動きをサクッと実装します

  • tabbarの既に開いているタブアイコンをタップすると、一番上までスクロールする
  • ただしUINavigationControllerでの「戻る遷移」を邪魔しない

ezgif.com-video-to-gif.gif

アプリの構造

  • タブバー(UITabBarController)が複数の画面(UIViewController)を持っている
  • 各UIViewControllerはUINavigationControllerで遷移する

注意点

概要の通りだが、
UINavigationControllerで遷移する場合、タブをタップ時のデフォルトの動きは
『UINavigationControllerのrootに戻る』であり、
このときはスクロールさせないようにしたい

実装

class SampleTabBarController: UITabBarController, UITabBarControllerDelegate {
    // shouldSelectでは、現在開いている画面がnavigationControllerのルートかを判定するまで。
    var shouldScroll = false
    func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
        self.shouldScroll = false
        // 表示しているvcがnavigationControllerルートのときはスクロールさせる
        // ルート以外は、navigationControllerの戻る機能を優先しスクロールさせない
        if let navigationController: UINavigationController = viewController as? UINavigationController {
            let visibleVC = navigationController.visibleViewController!
            if let index = navigationController.viewControllers.index(of: visibleVC), index == 0 {
                shouldScroll = true
            }
        }
        // 遷移を許可するためのtrueを返す
        return true
    }

    // didSelectで、選択されたタブが、前回と同様なら、shouldSelectの結果(shouldScroll)も考慮し、スクロールさせる
    var lastSelectedIndex = 0
    func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController)  {
        guard self.shouldScroll else { return }

        if self.lastSelectedIndex == tabBarController.selectedIndex  {
            if let navigationController: UINavigationController = viewController as? UINavigationController {

                let visibleVC = navigationController.visibleViewController!
                if let scrollableVC = visibleVC as? ScrollableProtocol {
                    scrollableVC.scrollToTop()
                }

            }
        }
        self.lastSelectedIndex = tabBarController.selectedIndex
    }
}

各タブのViewControllerたちの実装

// スクロールを持つプロトコル
protocol ScrollableProtocol {
    func scrollToTop()
}

// ↑のように protocolで縛ることで、各画面のスクロールが
// scrollViewだろうが、tableViewやcollectionViewだろうが、
// tabbarControllerは意識せずに実行できる

class AAAViewController: ScrollableProtocol {
    // scrollViewを持つViewController
    @IBOutlet weak var scrollView: UIScrollView!
    func scrollToTop() {
        scrollView.setContentOffset(CGPoint(x:0, y:0 - scrollView.contentInset.top), animated: true)
    }
}


class BBBViewController: ScrollableProtocol {
    // tableViewを持つViewController
    @IBOutlet weak var tableView: UITableView!
    func scrollToTop() {
        let indexPath = IndexPath(row: 0, section: 0)
        tableView.scrollToRow(at: indexPath, at: .top, animated: true)
    }
}

class CCCViewController: ScrollableProtocol {
    // collectionViewを持つViewController
    @IBOutlet weak var collectionView: UICollectionView!
    func scrollToTop() {
        let indexPath = IndexPath(row: 0, section: 0)
        collectionView.scrollToItem(at: indexPath, at: .top, animated: true)
    }
}

実装の解説と、改善したいと思っているところ

UINavigationControllerで遷移する場合、タブをタップ時のデフォルトの動きは
『UINavigationControllerのrootに戻る』であり、このときはスクロールさせないようにしたい

この動きがやや面倒で
今回の実装ではTabBarControllerのshouldSelectdidSelectで判定を行っている。

didSelectの発火時では、すでにUINavigationControllerのルートに移動してしまっているため、
UINavigationControllerで遷移先のVCに居たとしても常にスクロールが走ってしまう。

一方でshouldSelectではtabBarController.selectedIndexがまだ
「今回選択されたタブ」ではなく「前回選択されたタブ」であるため
『既に開いているタブが選択された』が判定できない。

この辺をきれいに改善する方法があれば教えてほしいです。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初めて開発した個人Toy Project!!

非常に単純な支出管理アプリケーション
Spending Management - SPEM!
旅行,小遣い,その他いろいろのお金を
複雑に管理する必要なく簡単に
目標のタイトルと金額を設定して管理してみてください
皆様の支出をSPEMがお手伝いいたします。

https://apps.apple.com/jp/app/spem/id1470758766

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Bitrise で並列ビルド (iOS/Android)

概要

  • React Native のビルドとデプロイに Bitrise を利用している
  • iOS のビルド時間が長いせいで、 Android が完了する前に45分超えてしまう
  • そのため、 iOS/Android でビルドするワークフローを並列実行したい
    • 使っている技術が別物だから、並列実行しても問題ないのではないか

結論

並列実行は GUI ではサポートされていないが、 Bitrise CLI を使うと、超単純に並列ビルドが可能。

  • ローカルに下記のような bitrise.yml を入れてGitリポジトリに入れる
bitrise.yml
---
format_version: '6'
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
project_type: react-native
workflows:
  common:
    steps:
    - yarn@0.0.8:
        inputs:
          - command: install

  ios:
    steps:
      - certificate-and-profile-installer@1.10.1: {}
      - # 色々

  android:
    steps:
      - install-missing-android-tools@2.3.5: {}
      - # 色々

  • Bitrise にて、ワークフローエディタを開き、下記を入力
#!/bin/bash
set -ex
bitrise run common --config bitrise.yml
bitrise run ios --config bitrise.yml &
bitrise run android --config bitrise.yml &
wait

全体としてはこんな感じ。

image.png

これで、ビルド時間が3分の2くらいになった。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

スマホのシェア率を見れるサイトのまとめ

スマホのシェア率なんてググればすぐ出てくるだろーって思ったら意外と時間かかったのでまとめました。
特にandroidの機種ごとのシェア率は全然見つからないですね…

サイト名 OS カテゴリ 集計期間 対象 集計方法
apple iOS バージョン 1日? 世界 App Store(アクセス解析?)
スマタブinfo iOS/android バージョン 直近3ヶ月? 日本 独自調査
DeviceAtlas iPhone+android 機種 四半期 国別 提携サイトのアクセス解析
statista 記事により異なる
android android バージョン 1週間 世界 Google Play ストアのアクセス解析
statcounter iOS/android バージョン/メーカー 最小で日単位 世界/国別 提携サイトのアクセス解析
webrage iPhone+android 機種 1ヶ月? 日本 アクセス解析
BCN+R android メーカー 半期 日本 販売台数
BCN+R iPhone+android 機種 1ヶ月 日本 販売台数

apple

スクリーンショット 2019-07-10 15.49.33.png
https://developer.apple.com/support/app-store/
シンプルな円グラフでわかりやすいです。

スマタブinfo

スクリーンショット 2019-07-11 11.12.26.png
http://smatabinfo.jp/os/android/index.html
iOSはバージョンごと、androidはAPKレベルごとに見れます。
また実測値は見れませんが、androidの機種ごとのシェアが5段階のランクで見れるのがいいですね。

DeviceAtlas

スクリーンショット 2019-07-10 15.22.40.png
https://deviceatlas.com/blog/most-popular-smartphones#japan
OS関係なく全機種のシェア率の上位12機種が見れます。
日本はiphoneのシェア率が高いので実質ihponeの機種ごとのランキングになってます。

statista

https://www.statista.com/search/?q=share&category=418&subCategory=481
ほとんどの記事が有料会員専用なのであまり使えないかもしれません…
集計方法は記事によって異なります。

android

スクリーンショット 2019-07-11 11.25.21.png
https://developer.android.com/about/dashboards/index.html
集計期間が2018/10/20~2018/10/26とちょっと古いです。
他に画面サイズと密度ごとのシェア率も見れます。

statcounter

スクリーンショット 2019-07-11 11.31.23.png
http://gs.statcounter.com/
一番情報量が多く使い勝手がいいサイトでした。
OSごと、バージョンごと、などなどたくさんの統計を2009年1月から最小は日単位で見ることができます。
元となるデータも200万サイト月間100億PVと申し分ないです。

webrage

スクリーンショット 2019-07-10 15.25.30.png
https://webrage.jp/techblog/sp_share/
日本でのシェア率ランキングです。
iPhoneはiphoneで一括りにされてます。

BCN+R

スクリーンショット 2019-07-11 11.35.02.png
https://www.bcnretail.com/market/contents_type=49
androidのシェア率を全国の主要家電量販店・ネットショップのPOSデータから集計しています。
アクセス解析ではなく販売台数なので注意。
目当ての記事探すのがちょっと大変かも。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CocoaPods 1.7でCDNを使ったらSpecs更新が爆速だった

tl;dr

CocoaPodsをv1.7系にしてCDNをsourceにして爆速!

CocoaPods v1.7

CocoaPodsのv1.7系が5/23にリリースされています。
あまりCocoaPodsは追っていなかったのですが、Specsの更新が爆速になってて感動したのでまだの人は使ったほうがいいです!

CDNサポート

v1.7からsourceとしてCDNがサポートされ
1.7.2からFinalizeされNetlifyでホストされているようです。

Specs.gitのかわりにcdnをsourceに指定しましょう。

- source 'https://github.com/CocoaPods/Specs.git'
+ source 'https://cdn.cocoapods.org/'

でも使ってみた感じだとbeta1で案内されていたjsdelivr.netの方が早かったです。

CDNソースだと必要なSpecだけ取得できる

以下のPodを利用している環境で試しました。

Crashlytics
DeallocationChecker
Fabric
Mockingjay
Periphery
SwiftFormat
SwiftGen
SwiftLint
URITemplate

ゼロからSpecs.gitを更新すると手元で2分かかるところが・・・
CDNだと利用しているPodのSpecだけDLされます

Resolving dependencies of `Podfile`
  CDN: jsdelivr-cocoa Relative path downloaded: Specs/5/e/d/SwiftFormat/index.txt, save ETag: W/"2eb-6gklZWWeegEzBbq63V1uA8xQgu8"
  CDN: jsdelivr-cocoa Relative path: Specs/5/e/d/SwiftFormat/index.txt modified during this run! Returning local
  CDN: jsdelivr-cocoa Relative path downloaded: Specs/5/e/d/SwiftFormat/0.13/SwiftFormat.podspec.json, save ETag: W/"1ea-z72pQ1Xtukf7S9Ip+lU60dloSNg"
  CDN: jsdelivr-cocoa Relative path downloaded: Specs/5/e/d/SwiftFormat/0.16/SwiftFormat.podspec.json, save ETag: W/"1ea-4yhY9JtA+tyAFcmG52nHey4IYGc"
  CDN: jsdelivr-cocoa Relative path downloaded: Specs/5/e/d/SwiftFormat/0.16.3/SwiftFormat.podspec.json, save ETag: W/"1ee-EDv9/GnjwZJZpIb/nCzuPNfAy+o"
  CDN: jsdelivr-cocoa Relative path downloaded: Specs/5/e/d/SwiftFormat/0.16.1/SwiftFormat.podspec.json, save ETag: W/"1ee-/+ETQMcayrjdya79gkc0dwEHyko"

略

bundle exec pod install --repo-update --verbose  1.75s user 1.37s system 78% cpu 3.955 total

約4秒で終わってしまいました!
なんてこった30倍速

みんな使おう!

CircleCI使ってる人向け

CircleCIではSpecs更新を高速化するために cocoapods-specs.circleci.com/fetch-cocoapods-repo-from-s3.sh が案内されています。
執筆時点のこのスクリプトの中身は従来のSpecs.gitを更新する内容になっています。

速いといっても4分ぐらいかかるので、上述のCDNからの必要Specだけの取得に比べると雲泥の差ですし
今後スクリプトの中身がCDN sourceに対応したのだとしても所要時間に意味のある差は生まれないと思いますので利用をやめて素朴な

[bundle exec] pod install [--repo-update]

するだけで良いと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UnityでARKit2 - 第四回 -

UnityでARアプリ開発をやってみたい 第四回

1. この記事でやること

BitBucketから拝借してきたunity-arkit-pluginのExampleにあるUnityARWorldMapをちゃんと読もうの回です。

2. UnityARWorldMapを理解したい

UnityARWorldMapはUnityARKitPluginに用意されているExampleのUnityプロジェクトです。
ここではUnityARWorldMapがどういうことをやっているのかを理解することを目的としています。
まずは構成要素からチェックしていきます。

Hierarchy

UniryARWorldMapのHierarchyは以下のようになっています。

Hierarchy

  • Directional Light
    • 通常の光源です
  • Random Cube
    • AR空間においておくCubeです
    • このCubeにはX,Y,Z方向がわかるようにコンポーネントがぶら下がっています
  • Camera Parent
    • Main Camera
      • GameView用のメインカメラです
      • なんで階層化しているのかわからない...
  • Hit Cube Parent
    • Hit Cube
      • AR空間に配置するCubeです
      • UnityARTestHitExample.csがあてられています
      • なんで階層化しているのかわからない...
  • Generate Planes
    • AR空間上で平面を認識するものです
    • UnityARGeneratePlane.csがあてられています
  • PointCloudParticleExample
    • AR空間で特徴点を表すために追加されているParticleです
    • ParticleのPrefabをつかっています
    • PointCloudParticleExample.csがあてられています
  • Canvas
    • Layout
      • Save button
      • Load button
        • 画面上に表示される補助ボタンです
        • OnClickイベントを割り当てたりすることができます
  • EventSystem
    • イベントドリブンでゲームを構成するためのコンポーネント、なにか画面に見えるわけではないです
  • WorldMapManager
    • WorldMapManager.csがあてられています
    • ARCameraManagerと連携してWorldMapを管理します
  • ARKitControl
    • ARセッションをコントロールしています
  • ARCameraManager
    • カメラマネージャーです、ARアプリの基盤的役割を果たします

Unity Scriptの読み方

Inspectorの設定値

ARCameraManagerを例に取ります

Componentとして追加されているスクリプトはUnityARCameraManager.csです。
UnityARCameraManager.csのようにファイル名を指定し、このファイル名と同じ名前のクラスを内部に定義するのがルールです。

Unityのinspectorビューで設定できる項目がいくつかありますが、これはこのクラスのPubilcなメンバが自動的にUIに反映されるようになっており、これらの設定値はUnity UIで与えるようになっています。

public class UnityARCameraManager : MonoBehaviour {

    public Camera m_camera;
    private UnityARSessionNativeInterface m_session;
    private Material savedClearMaterial;

    [Header("AR Config Options")]
    public UnityARAlignment startAlignment = UnityARAlignment.UnityARAlignmentGravity;
    public UnityARPlaneDetection planeDetection = UnityARPlaneDetection.Horizontal;
    public bool getPointCloud = true;
    public bool enableLightEstimation = true;
    public bool enableAutoFocus = true;
    public UnityAREnvironmentTexturing environmentTexturing = UnityAREnvironmentTexturing.UnityAREnvironmentTexturingNone;

    [Header("Image Tracking")]
    public ARReferenceImagesSet detectionImages = null;
    public int maximumNumberOfTrackedImages = 0;

    [Header("Object Tracking")]
    public ARReferenceObjectsSetAsset detectionObjects = null;
    private bool sessionStarted = false;

    // 以下略
}

publicのCamera m_cameraではCameraクラスのインスタンスを指定する必要があり、ここにMain Cameraを割り当てています。
Headerを指定すると、Inspectorビューでパラメーターのグループ名として表示されます。
publicのパラメータがenumの場合はInspectorではドロップダウンで表示されます。
例えばUnityARAlignmentはこのenumがUnityARSessionNativeInterface.csに定義されています。
デフォルトで値を入れておけばInspectorビューでの初期値となります。

StartとUpdate

各スクリプトにはStart()メソッドとUpdate()メソッドが用意されていることが多いです。
Startは最初に1度だけ実行され、Updateは毎フレームで実行されます。
フレームはゲーム画面を常に最新に保つためのクロックのようなものです。

UnityARCameraManagerの場合、Start()メソッド内でUnityARSessionNativeInterfaceのセッションを取得し、セッションを開始します。
UnityARSessionNativeInterfaceはUnityでARを取り扱う上でのベースとなるクラスで、このセッションとしてすべての処理を管理します。
Update()メソッドでは各フレームにおけるカメラの物理的なポジション情報をデバイスから取得し、メンバのm_cameraにセットする。これにより、カメラのいち情報を内部で保持することができます。(別の処理で参照するなど)

SetCameraは初期だと誰も使っていなさそうですね、外部からカメラを再割り当てする感じかな。

スクリプトを読んで全体像を理解する カメラ編

ARCameraManagerのCameraメンバとして設定されているMainCameraに割り当てられているスクリプトをチェックします。
- UnityARVideo

メンバとしてMaterialが割り当てられています。YUVMaterialが割り当てられます。
いろいろ書いてあってややこしい。これはiOSのカメラからの情報をUnityCameraに展開するスクリプトなので、理解は難しいです。
このカメラ映像がUnityCameraに割り当てられる際にじゃまにならないよう、Cameraの設定でClear Clear FlagsはDepth onlyにする必要があります。
- UnityARCameraNearFar

SetCameraNearFar(iOS側:外部IF)経由でUnityCameraのNearとFarを設定します。
これらの値は、3D空間にものを描画するときの手前側と奥側を設定するものです。イメージとしては、AR空間に置かれた物体にカメラを向けながらじりじりと近づいたとき、ある瞬間から画面に映らなくなるような処理に使います。

ここまでに出てきた
UnityARVideo, UnityARCameraNearFar, UnityARCameraManagerの3つのスクリプトはARアプリ制作における必須アイテムです。

スクリプトを読んで全体像を理解する

UnityARKitControl

次はUnityARKitControlについてスクリプトをチェックします。
UnityARKitControlにはpublicメンバがないので独立スクリプトです。
Start処理はシンプルで以下のパラメーターのセットを行います。

  • runOptions
  • alignmentOptions
  • planeOptions

このスクリプトでは主にOnGUIで処理が行われます。OnGUIは先のUpdateに似た事前に定義されたタイミングで呼び出されるメソッドです。
Updateは毎フレームごと、つまりユーザーのオペレーションがあろうがなかろうが、一定間隔で実行されます。一方で、OnGUIはイベントが発生するたびに呼び出されるメソッドです。ユーザーが操作したときなどのイベント発生毎に呼び出されます。

OnGUIでは主にARSessionのStopとStartがコントロールされています。

WorldMapManager

次はWorldMapManagerについてスクリプトをチェックします。
WorldMapManagerは先程のUnityARCameraManagerをメンバに持っています。

まずStartメソッドではARFrameUpdatedEventにイベントが登録されています。
これはその名の通りフレームごとに発生するイベントです。
登録されたイベントはOnFrameUpdateです。
OnFrameUpdateではちょっとしたログの登録しかされないので、ここは本体ではないです。
WorldMapManagerにはSave()とLoad()が用意されており、これらはCanvasのSave buttonとLoad buttonに割り当てられています。
そのためこれらのボタンがクリックされるとこのメソッドに入ります。
まずSaveメソッドでは、ARWorldMapをpathに保存します。ARWorldMapについてはARKit2.0のReadMeを見ると色々書いてあります。
WhatsNewInARKit2_0.mdを見てください。
このARWorldMapはARKit2.0で追加された、AR空間の共有を実現するために非常に重要な仕組みです。
SaveSerializedとLoadSerializedはSaveとLoadの別パターンで、ARWorldMapをシリアライズしてByte列で保存するという違いがある。

Canvas (Buttons)

Canvas中のSave ButtonとLoad ButtonはWorldMapManagerのSaveとLoadが割り当てられています。

Generate Planes

Generate PlanesにはUnityARGeneratePlane.csのスクリプトが割り当てられています。
- UnityARGeneratePlane

publicでGameObjectが割り当てられています。デバッグ用のplanePrefabです。
デバッグ用というのは、このPrefabを開くとわかるように、境界線付き平面だからです。
通常のアプリには不要な線が表示されているので、Debugです。

Start処理ではまずUnityARAnchorManagerのインスタンス生成をしています。UnityARAnchorManagerは平面のアンカー情報を保持するクラスで、イベントドリブンでアンカー情報を更新します。ARGeneratePlaneではこのクラスからアンカーを教えてもらって、以後の処理をします。

次にUnityARUtilityでplanePrefabを初期化します。これで、先のDebug用Prefabがセットされました。あとはARUtility側でイベント更新に合わせてPrefabが画面上に表示されます。

HitCubeParent/HitCube

HitCubeにはBoxColliderがセットされています。ColliderはUnity空間上での物理量同士の接触を発生させるためにセットするパラメーターです。
これがないと物体を物体が通り抜けるようになってしまいます。Meshも定義されているので視認可能な物体ということになります。

HitCubeには次のスクリプトが割り当てられています。
- UnityARHitTestExample.cs
- publicなメンバとして以下の3つが用意されています。
- Transform
- HitCubeParentがセットされています
- LayerMask
- float maxRayDistance
- Unity上でのデバッグ用なので無視してOK

このスクリプトではUpdate処理が主役です。Update処理では画面がタッチされたポジションを取得します。
このポジションを使って、HitTestWithResultTypeを呼び出し、UnityARSessionNativeInterfaceがうまいことやって、ヒット位置を検出し、m_HitTransformに位置と回転をセットします。これにより、Cubeが配置されます。

おしまい

これでUnityARWorldMapの全体像チェックはおしまいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む