ペンギン村 Tech Blog

技術をこよなく愛するエンジニア集団が在住するペンギン村から、世界へ役立つ(かもしれない)技術情報を発信する技術系ブログです。某アラレちゃんが済む村とは一切関係ありません。んちゃ!

【iOS】ちょっと待って!その画面UITableViewで作る必要ないかも

f:id:kamui_project_tony:20180125233317j:plain:w250

はじめに

ダーリンことカムイです(嘘松)

今日は個人的によく使うクラスの1つであるUITableViewについて書きます。

一覧 != UITableView

皆さんはUITableViewを普段どんな時に使っているでしょうか。

「一覧を表示する時に使うアレでしょ?」というお方、こちらをご覧ください。

f:id:kamui_project_tony:20180126205526p:plain:w200

(ほっちゃん尊い…)

こちらはInstagramのTOP画面です。大きな画像とコメント欄がセットになった項目が連続でいくつも表示されます。 実際に開発に携わったわけではありませんが、これはUITableViewで実装していると思われます。 では次にこちらをどうぞ。

f:id:kamui_project_tony:20180126205703p:plain:w200 f:id:kamui_project_tony:20180126205731p:plain:w200 f:id:kamui_project_tony:20180126205800p:plain:w200 f:id:kamui_project_tony:20180126205823p:plain:w200

途切れ途切れになっておりますが、Airbnbの詳細画面です。画面上部から動画→ホストからのメッセージ→アクセス方法→MAP→カレンダー→レビューと多くの項目が登場します。

「これも一覧!UITableViewだね!」 と思いますか?

Yesだねという方、ちょっと待って下さい。

これも実際に開発に携わったわけではないので憶測ですが、UITableViewでなくても良い画面レイアウトだと思います。

UITableViewについて、公式では以下のように述べています。

Table Viewの一般的な用途(Table Viewに最も適した用途)は、階層的なデータのナビゲーションで す。階層の最上位レベルにあるTable Viewには、最も概要的なレベルのデータカテゴリのリストが表示されます。ユーザは、ある行を選択して、階層の次のレベルに「掘り下げ」ます(ドリルダウンします)。階層の一番下には、特定の項目の詳細を表示するビュー(通常はTable View)があります(たとえば、住所録の1つのレコード)。ユーザが、この項目を編集できるようにすることもできます。

(iOS TableView プログラミング ガイドラインより抜粋)

あくまで「一般的な」という前置きの元、使い方として求めている一例として住所録を上げています。つまりタ◯ンページですね。 要はこんなレイアウトです。

f:id:kamui_project_tony:20180125233800j:plain:w250

つまり、UITableViewを利用する最大のポイントは

同じレイアウトで構成されている項目を何個も続けて表示する

ことが重要だと思います。 そのため項目が縦並びになっているからといって、UITableViewを使うと不便になるケースが度々あります。 例えば以下の3つのケースです。

1. トルツメ問題

1つだけなら良いですが、「あれとそれも消したい」と言われてしまうとUITableViewのDelegate/DataSourceメソッド内が煩雑になる一方です。 またはDataSourceを分けて考えないといけない可能性もあります。1

2. キーボード表示中に起きる問題

キーボード以外を押下した場合にキーボードを閉じたい場合、タップジェスチャーはタップイベントが存在するUITableViewでは処理が出来ず、対応策も以下の記事のように処理が面倒です。

mzgkworks.hateblo.jp

また押下したUITextField自体がキーボードに隠れてしまう問題の対応策は、UIScrollViewを継承しているUITableViewは対処法は同じであるものの、UITableViewCell内でTextFieldを使うケース自体が少ないため、ググってもその対処方が見つからないことが多いです(最近の記事であれば特に)。

「UIScrollView UITextField keyboard scroll」などと検索すると、色々情報は出てきます。 qiita.com xyk.hatenablog.com

3. 罫線問題

UITableViewCellは標準で左側に15pxの余白を用意した罫線がついておりますが、非表示処理は地味に面倒です。

特定の UITableViewCell だけ separator の線消したい

(僕はコードで処理せずにこの設定値を大きい値にして誤魔化す処理をしたりしますが、、)

f:id:kamui_project_tony:20180125235506j:plain:w200

他にもUITableViewCellで言えば選択不可の設定をしないとセル押下時にハイライト処理されてしまうなど、実際に実現したいことのためにUITableViewにある本来の機能をOFFにする対応のようなことだらけの場合は、UITabeViewで作る必要がない画面であると言えます。

今回のような項目数の多い詳細画面では

・UIScrollViewを用意2

 -> その上にUIView(ContainerView)を用意

  -> その上にUIStackView(縦並び)で用意 3

   -> その上に表示項目ごとにUIViewを用意

    -> その上に各UI部品を用意

といったやり方がベターかなと思います。

Sampleをgithubにあげましたので、よろしければご覧ください。

github.com

storyboardの設定方法として以下の記事が参考になると思います。 xyk.hatenablog.com

利点としては上記であげた問題となるケースが解消できることです。

  • 対象のViewをisHiddenにするだけでトルツメが実現できる(UIStackView)
  • タップジェスチャーイベントを拾えるため、キーボード表示中に他領域をタップすればキーボードを楽に閉じれる。
  • キーボード問題の記事もScrollViewで調べるとヒットしやすい。
  • 罫線はUIViewで実現。レイアウト調整も楽。

などが挙げられます。

逆に、UX的にUITabeViewの仕様に合わせるのが難しい処理として

  • セクションヘッダをセクションのスクロールが完了するまで画面上部に固定する
  • ハイライトさせるViewは最下部のContainerViewのみにする(UITableViewCellと同様のハイライト方法)

などは実装が大変です。 またiOSエンジニアでないと気付かない見え方であったりもするので、事前にこれらのUXを加味した画面であるかは相談した上で実装に臨んだ方が良いと思います。

最後に

どのツール系アプリにも検索画面・詳細画面は多いと思うので、「UITableViewは住所録のようなものである」と理解すると「この画面には便利・この画面には不便」の意識改革にもつながると思います。

もし実装している画面が一覧っぽくない画面なのにUITabelViewで作られている場合、息抜きがてら一度UITableViewプログラミングガイドを読み直してみてみるのも良いかもしれません。


  1. enumで整頓する方法がありますが、こちらは別途記事にする予定です。

  2. iPhone4Sの画面の高さのサイズである480pxに満たない項目量であれば、UIScrollViewは不要です↩

  3. トルツメする項目が無いのであれば不要です↩

Kotlinで可変長引数を利用する時は、同一の型の引数を一緒に使用するのを避ける

こんにちは、tobi462(過去記事一覧)です。

今日は、Kotlinで可変長引数を使用した時にハマったことがあったので、アンチパターンの共有です。

可変長引数(vararg)

可変長引数の概念はJava5からあるもので、Kotlinでも似たように配列として引数を受け取る仕様になっていますが、構文が少し違います。

Kotlinではvarargという修飾子を用いて、可変長引数を宣言します。

// 宣言
fun printAll(vararg values: String) {
    println(values::class.java) // => class [Ljava.lang.String;
    values.forEach {
        println(it)
    }
}

// 使用
printAll("Apple", "Orange", "Banana")

配列自体を引数として渡す場合には、先頭に*をつけます。

val fruits = arrayOf("Apple", "Orange", "Banana")
printAll(*fruits) // *を頭につけないとコンパイルエラー

同一の型の引数を一緒に使う

さて、ここでクイズです。

以下のコードは先程の関数にデフォルト引数を追加したものですが、何が出力されるでしょうか?

fun printAll(vararg values: String, postfix: String = ".") {
    values.forEach {
        println(it + postfix)
    }
}

val fruits = arrayOf("Apple", "Orange", "Banana")
printAll(*fruits, "!") // クイズ:何が出力される?

おそらくここまでの流れから答えの察しはつくのではないかと思います。

以下のように出力されます。

Apple.
Orange.
Banana.
!.

つまり以下のコードは一見すると、可変長引数としてfruitsを渡し、第二引数として”!”postfixとして渡しているように見えますが、実際にはどちらも可変長引数として扱われているのが分かります。

printAll(*fruits, "!") // どちらも可変長引数として渡される

ちなみに第二引数にデフォルト値が使用されていない場合は、名前付き引数で明示しないとコンパイルエラーになるため、この問題は起こりません。

printAll(*fruits, postfix = "!") // `postfix = `がなければコンパイルエラー

教訓としてのアンチパターン

今回得られた教訓としては、可変長引数を利用する時は、同一の型の引数を一緒に使用するのを避けるべきであるということです。

デフォルト引数を利用しなければ問題ないと感じるかもしれませんが、それでも引数の境界が分かりづらくなり、APIとしては利用者に誤解を与えやすいため避けるべきかと思います。

最後に

といった形で、今日はKotlinのアンチパターンについての記事でした。

Kotlinはよくデザインされている言語だと思いますが、こういったハマリポイントもあるのだと分かったのは良い収穫だったと思います。

ではまた次回。

ターミナルからXcodeを1コマンドで開くCLIツールを作ってみた

どうも、攻殻機動隊シリーズでは S.A.C が一番好きな tobi462 です。

今回は Swift Package Manager で、ターミナルからXcodeを開くCLIツール xcode-open を作ってみたので、その紹介です。

f:id:yu_dotnet2004:20180121012156g:plain

モチベーション

  • ターミナルからXcodeを1コマンドで開ける
  • バージョン(e.g. 9.1)も指定できる
  • 開くバージョンを記憶することもできる

使い方

インストール

Homebrewでインストールできます。(ビルドのためにXcode 9.0以上が必要です)

$ brew install YusukeHosonuma/xcode-open/xcode-open

あるいはGitHubのリリースページからバイナリを直接DLしてもOKです。

使い方

Xcodeプロジェクトまたはワークスペースのあるディレクトリに移動し、以下のようなコマンドが使えます。

デフォルト(xcode-selectで指定されているXcode)で開く:

$ xcode-open

Xcodeのバージョンを指定して開く:

$ xcode-open 9.1

バージョン指定を記憶する:

$ xcode-open 9.1 --save
$ xcode-open # 9.1 で開かれるようになる

オススメのエイリアス設定

xcode-openだと微妙に長いので、.bash_profile.zshrcに以下を追記することを推奨します。

# xcode-open
alias xopen="xcode-open"

xopenで開けるようになります

$ xopen 9.1

なぜ作ったか?

ターミナルから起動したい

わたしは普段、ターミナルが作業の主軸になっています。

ターミナルからキーワード絞込で Git の作業ディレクトリに移動し、そこから VS Code を立ち上げたり、IntelliJ IDEA などを起動したりしています。

$ code . # VS Code を起動
$ idea . # IntelliJ IDEA を起動

Xcodeでも同様に1コマンドで起動したいと思ったのが最初のモチベーションです。

バージョンを指定したい、記憶させたい

複数のプロジェクトを同時に担当していると、そのプロジェクトによって利用している Xcode のバージョンが異なるということはままあります。

あるプロジェクトでは最新の 9.2 だけど、もう1つのプロジェクトでは 8.3 といったケースです。

であれば、プロジェクト毎に利用するXcodeバージョンを固定してしまいたいというのが2つ目のモチベーションです。

Swift Package Manager で CLI を作ってみたい

Swift Package Manager は現在バージョンは4まで出ています。

そろそろ実践で使えるレベルかなと思い、せっかくなのでCLIを作ってみようと思い立ちました。

作ってみた感想

わたしが欲しいものを作ったので当たり前ではありますが、必須ツールになりました(自画自賛?)。

もう手がxopenを覚えてしまったので、他のMacで入って無いときあれ?となってりします。

Swift Package Manager はそれなりに良く出来ていると感じたのですが、面倒な部分やハマリポイントがあったり、Xcodeが正しくファイルやシンボルを認識しなくなることがあり、ちょっと面倒なところも感じました。これくらいの小規模なツールであれば Ruby とかで書いたほうが早いかな、という気もしました。

Swift Package Manager は標準でXcodeプロジェクト生成機能を備えているので、Xcodeとの相性については今後改善を期待したいと強く思いました。

最後に

といった感じで、Swift Package Manager で CLI ツールを作ってみたお話でした。

内部的な作りなど技術的な面については、またどこか別のタイミングで記事にできればと思っています。

もちろんOSSとして公開 していますので、IssueやPRも歓迎です。

NavigationBarを含んだ画面でViewで全体を覆う方法

qiita.com

通信中に画面を操作させたくない理由から、マスキングを画面にすることがよくあると思います。 ただNavigationBarを含む場合、単純にself.viewにaddSubviewするだけではNavigationBarまで覆ってくれません。

f:id:kamui_project_tony:20180119010423p:plain:w200

その対処法としてappDelegate.windowに対しaddSubviewをするという手段もあるのですが、windowに直接Viewを貼り付けするのはバグの温床になるので避けた方が良いです。

記事ではself.navigationController?.viewに対しaddSubviewをすることで、対象画面でのみ処理を完結することに加え、storyboard上に部品を配置する方法で実現しています。普段の開発でstoryboardにView部品を設置している人にとっては馴染みやすい方法なのではと思い、やってみました。

f:id:kamui_project_tony:20180119010452p:plain:w200

追伸: 今日参加したAKIBA.swiftのAKIBA枠のアニメの話面白かったです(´・ω・`)

ストレスを溜めやすいエンジニアの体調管理

今週のお題「体調管理」

どうも、NEWGAME!では八神コウちゃんが一番好きなペンギン村の住人@tobi462です。

今回は今週のお題である体調管理について書いてみたいと思います。

事前に書いておきますが、技術的な要素は一切ありません。(Tech Blogなのにね・・・)

体調を大いに崩した去年末

毎日のような頭痛、朝起きるのはものすごく辛い、仕事もプライベートも楽しくない、なんだかずっと疲れている。

というのが私の去年のピークでした。はい、有り体に言って軽いうつ状態というやつですね。

仕事は続けられていたものの大した成果も出せず、わりと辛い日々を過ごしていました。

しかし、社内カウンセリングに行ったきっかけから、少しずつ回復しつつある現状の体調管理について書いてみたいと思います。

IT業界はストレスを溜めやすいですし、同じように苦しんでいる方に私の経験を共有して、何かしらのプラスにつながれば良いと思い記事にしてみました。

寝る前に思考内容を書き出す

前述したようにわりと限界が近い状態で、社内のカウセリングに行きました。(カウンセリングを勧めてくれた同僚にはとても感謝してます)

その時の私は、いつも仕事のことが頭から離れないというのがストレスというか悩みでした。

その時カウンセラーにオススメされたのが、金曜日の夜などに思考内容を書き出して整理するというものでした。

そのメソッド自体は知っていたものの、その時の私にそれを実践しようという発想はもちろん出てくるものではありませんでした。

それから不定期ではあるものの寝る前に気持ちのダンプっぽいものをやるようになりました。

手書きの方が良いのかもしれませんが、とりあえずテキストエディタ(これは余談ですがVSCodeを好んで使っています)を立ち上げ、思うがままに考えをタイプして、最後は保存せずに破棄するというやり方をしてみました。

そうしたところ、やはり休みの日でも仕事のことを考えてしまうことはあるものの、少しだけ回数が減ったように感じ、前よりはストレスレベルも落ちたような気がしました。

運動駆動開発

次に運動の回数を増やしました。

私は以前からスポーツジムに通って、軽いランニングはしていたのですが、その頃は運動の回数もめっきり減ってしまっていました。

そこで、最初は3日に1回を目標にし、次に2日に1回のペースで、30分ほどのランニングをするようになりました。

これの効果は抜群で、久しぶりに自分の思考回路が戻ったというか、頭の中に渦巻いている悪いものが抜けた気がしました。

運動駆動と表現したのは、忙しくなるとあっという間に運動しなくなるという事実からです。

しかし、運動しないことが最大のリスクという認識があったので、運動から駆動させるという考えを採用しました。

調子にのらない

さて人間不思議なもので、体調が良くなってくると妙に強気になってきます。

全快にはほど遠い状況でありながら、これはもう大丈夫だろうと夜遅くまでコードを書いたり、あるいはアニメを見たりすることもありました。

結果はもちろん悲惨なもので、翌日の朝は体調が悪く、仕事もあまり調子が乗らないこともしばしばです。

体調が少し良くなったからといって油断しないこと、これは今でもハマりやすいポイントなので気をつけるようにしています。

オープンソースであれ

前述したように、去年、社内のカウセリングに行きました。

あまりそういったことは誰かに話そうとも思わなかったのですが、あえて部署内の日報に書いてみました。

その後も体調悪いとか、今日は運動するとか、回復傾向かもとか書くようにしました。

それの効果かは分かりませんが、体調が悪くて生産性が悪い自分を少し許せるようになったような気がします。

体調が悪ければ、それを隠さずにオープンにしてしまう。そうすると少し客観的に慣れて、自分自身でも無理をしなくなるような気がしました。

がんばらない勇気

というのが大切だと改めて思いました。

とくに脳が不調の時は、がんばろうと考えたって、ぶっちゃけわりとどうしようもない気がします。

であれば、勇気をもってがんばらないほうが最終的には生産的であるような気もします。

まとめ

といった形で、私の最近の体調管理、あるいは体調を回復するためにしていることを記事にしてみました。(これちゃんとお題のテーマに沿ってますかね・・・?)

今回の記事で挙げたのは以下です。

  1. 寝る前に思考内容をダンプする
  2. 運動駆動開発
  3. 少し体調が良くなっても過信しない
  4. オープンソースに振る舞う
  5. 頑張らない勇気

何かしら参考になるものがあれば幸いです。

【iOS】【Swift】「RootViewController + Wireframe」で画面遷移での消耗を回避する

自己紹介

はじめまして、ペンギン村で一番やかましい住人のナガクラ(@nagakuta)です!
Slackだけでなく、ブログもやかましく更新していきます!!(宣言)

TL;DR

  • RootViewControllerAppDelegate.window.rootViewControllerに指定してから画面遷移するようにすると色々ラクだよ!
  • Wireframeも一緒に使うとテスト書くときラクだよ!!

RootViewControllerによる画面遷移

RootViewController #とは

自分がRootViewControllerについて知ったのは、ペンギン村に貼られた以下の記事でした。

medium.com

その記事のリンクにRootViewControllerについての詳細記事がありました。

medium.com

その記事中にて説明されているRootViewControllerについて簡潔にザザッと説明すると、

  • 概念として、Container ViewControllerの延長である
  • AppDelegate.window.rootViewControllerに設定するViewControllerである
  • このViewControllerを起点に画面遷移を行うようにする

となります。なにこれ便利そう😲😲😲

そして、RootViewControllerを実装するメリットをサクッと説明すると、

  • 1つのナビゲーションスタックだけで新しいViewControllerを表示したり、インタフェースなしで戻したりすることができる
  • なので、どんなにViewが重なっていようと、最下層のViewControllerをカンタンに取得、あるいは差し替えることができる
  • 実は、AppDelegate.window.rootViewControllerを直接差し替えても、差し替え前のViewControllerがメモリ解放されないらしく、差し替えるたびにメモリ領域を圧迫する😨
  • AppDelegate.window.rootViewControllerRootViewControllerに固定することで、メモリ領域の不必要な圧迫を防ぐ

の3点でしょうか。なにそれすごく便利そう😂😂😂

RootViewControllerを実装してみる

では、実際にRootViewControllerを実装してみます( ´∀`)(一部端折っている箇所があります🙇)

RootViewController.swift

final internal class RootViewController: UIViewController {

    // MARK: - Life Cycle Methods

    init() {
        super.init(nibName: nil, bundle: nil)
    }

    override viewDidLoad()
        super.viewDidLoad()

        // 起動直後に遷移させる画面を宣言
        let launchViewController: UIViewController = LaunchViewController()

        // 子ViewのViewControllerを指定
        self.addChildViewContrlller(launchViewController)

        // 子ViewをSubViewとして追加
        launchViewController.view.frame = UIScreen.main.bounds
        self.view.addSubView(launchViewController.view)

        // 子Viewの所有権を譲渡
        launchViewController.didMove(toParentViewController: self)
    }

    /// NextViewControllerに遷移
    func transitToNextViewController() {
        let nextViewController: UIViewController

        // 子ビューのChildViewControllerを削除
        let childViewController: UIViewController = self.childViewControllers.first!
        childViewController.willMove(toParentViewController: nil)
        childViewController.view.removeFromSuperView()
        childViewController.removeFromParentViewController()

        // 新しいViewControllerを子ビューとして追加
        self.addChildViewController(nextViewController)
        nextViewController.view.frame = UIScreen.main.bounds
        self.view.addSubView(nextViewController.view)
        nextViewController.didMove(toParentViewController: self)
    }

}

LaunchViewController.swift

final internal class LaunchViewController: UIViewController {

    // MARK: - Life Cycle Methods

    (中略)

    override viewDidAppear(_ animated: Bool) {
        let appDelegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
        let rootViewController: UIViewController = appDelegate.window!.rootViewController as! RootViewController

        // NextViewControllerに遷移
        rootViewController.transitToNextViewController()
    }

    (以下略)

}

AppDelegate.swift

@UIApplicationMain
internal class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // 起動直後に遷移する画面をRootViewControllerに指定する
        self.window = UIWindow(frame: UIScreen.main.bounds)
        self.window.rootViewController = RootViewController()
        self.window.makeKeyAndVisible()

        return true
    }

    (以下略)

}

これで、起動 → RootViewControllerに遷移 → (RootViewControllerの子ビューをLaunchViewControllerに) → LaunchViewControllerに遷移 → (RootViewControllerの子ビューをNextViewControllerに置き換え) → NextViewControllerに遷移が実現できました( ´∀`)bグッ!

ここからNextViewControllerをログイン画面とするなり、メインコンテンツを表示させるなりするのが一般的なiOSアプリケーションの起動後の動きになると思います。

Wireframeで画面遷移

Wireframe #とは

Wireframeというのをざっくりと説明すると、「画面遷移をViewControllerの代わりに行うための仕組み」です。

通常、画面遷移を実装する際はUIViewControllerのメソッドを利用して行うと思います。(先程のRootViewControllerでも、Viewの上に重ねるという形で画面遷移を実装しています)

しかし、画面遷移をUIViewControllerクラスで行うと、少しViewControllerが膨らんでしまいますし、ViewControllerごとに同様の処理を何回も何回も実装するのはアホくs…スマートじゃないですよね😅

そんな時に使ってみたいのがWireframeです。Wireframeで画面遷移用のメソッドを実装し、ViewControllerクラスからそのメソッドを呼び出すようにすれば、いちいち画面遷移についてあーだこーだと考えずにViewControllerは画面表示のことに専念できます👍

また、遷移先の画面を指定できるようになれば、Unitテストの際にモックのViewControllerを指定することができるようになるため、テストを書くのが格段に楽チンになります😆😆😆

Wireframeによる画面遷移を実装してみる

ではでは、Wireframeを実装してみましょう(`・ω・´)ガンバル

RootWireframe.swift

internal protocol Wireframe {
    /// 指定した画面に遷移
    func transition(to viewController: UIViewController)
}

internal struct RootViewWireframe: Wireframe {

    func transition(to viewController: UIViewController) {
        let appDelegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
        let rootViewController: RootViewController = appDelegate.window!.rootViewController as! RootViewController

        // RootViewControllerの子ビューを削除
        if !rootViewController.childViewControllers.isEmpty {
            rootViewController.childViewControllers.forEach { (childViewController: UIViewController) in
                childViewController.willMove(toParentViewController: nil)
                childViewController.view.removeFromSuperView()
                childViewController.removeFromParentViewController()
        }

        // 以下はRootViewController.viewDidLoadメソッドの内容をそのまま移植
        rootViewController.addChildViewController(viewController)
        viewController.view.frame = UIScreen.main.bounds
        rootViewController.view.addSubView(viewController.view)
        viewController.didMove(toParentViewController: rootView)
    }

}

これでWireframeの実装ができました👍

では、RootViewControllerの画面遷移をWireframeに移譲します(`・ω・´)シャキーン

RootViewController.swift

final internal class RootViewController: UIViewController {

    let wireframe: RootViewWireframe = RootViewWireframe()

    (中略)

    override viewDidLoad() {
        super.viewDidLoad()

        // Wireframeによる画面遷移
        let childViewController: UIViewController = UIViewController()
        self.wireframe.transition(to childViewController)
    }

    (以下略)
}

LaunchViewController.swift

final internal class LaunchViewController: UIViewController {

    let wireframe: RootViewWireframe = RootViewWireframe()

    (中略)

    override func viewDidAppear(_ animated: Bool) {
        let nextViewController: UIViewController = UIViewController()
        self.wireframe.transition(to nextViewController)
    }

    (以下略)

}

これで、Wireframeによる画面遷移が実装できました!(∩´∀`)∩ワーイ

これで、AppDelegate.window.rootViewControllerの差し替えも容易になり、重なりまくった子ビューから最下層のViewを取得するのもRootViewController.childViewControllers.firstで取得できるようになります💪💪💪

あとがき

RootViewController + Wireframe による画面遷移」、いかがだったでしょうか😃😃😃

これからも、「楽に、最小効率で」を目的に勉強したTipsをこのブログで公開していきたいなと思います(`・ω・´)
次回更新もお楽しみに!!!

シェルスクリプトで現在のブランチ名を取得する(git rev-parse)

ペンギン村の住人、tobi462です。

今回はシェルスクリプトなどのプログラムからgitの情報を取得する際に、rev-parseという便利なコマンドがあったので、その紹介と教訓について書いてみます。

Tl;Dr

$ git rev-parse --abbrev-ref HEAD
master

冴えないやり方

実のところあとからググったらいくらでも記事があったのですが、最初は以下のように書いていました。

$ git branch | grep '*' | sed -e 's/* //'

まぁ、結果は同じになると思いますが、明らかに rev-parse を使ったやり方のほうがスッキリしています。

rev-parse って?

与えられたものが何かを調べるコマンドらしく、なんだかいろいろ出来るようです。 Git - git-rev-parse Documentation

以下では HEAD が指すコミットハッシュ値を取得しています。

$ git rev-parse HEAD
154cc76c53b6236247f0143272870447d336f4cf

$ git rev-parse --short HEAD
154cc76

どうやら普段のGit操作というよりは、Gitの情報を取得したい時につかえるコマンドという印象です。

--abbrev-ref って?

オンラインマニュアルによれば、

A non-ambiguous short name of the objects name. The option core.warnAmbiguousRefs is used to select the strict abbreviation mode.

とのことで、 どうやら曖昧でない短い名前を返すようです。言い換えると取得された名前を使用して参照すれば、確実に1つのハッシュ値に限定できるということでしょう。

まとめ(あるいは教訓)

普段利用しているようなGitコマンド(e.g. branchstatus)は人間向けのUIとしての側面を持っているので、プログラム的に何か扱いたい時はrev-parseを使うのが良さそう、というのが今回の教訓でした。