Story Board 抜きで、コードオンリーで iOS アプリの UI を作る

  • 87
    Like
  • 0
    Comment
More than 1 year has passed since last update.

はじめに

これは自分用のメモでもありますが、同時にもし「Story Board 面倒くさい!ソースコードのみで UI 作りたい!」というような方がいらっしゃいましたら、ご参考になれればと思います。
また、ソースコードは GitHub に公開しております。

なぜコードで UI 作るか

ぶっちゃけ言いますと自分 Story Board 使えないだけです。はい。そもそも以前 Interface Builder の時代からまともにそういったツール使ったことなかったし、要素配置とかページ遷移とかもどうやって作ればいいのかわからないし、今まではゲームを作ってきたから iOS 特有の画面遷移とかも特に使ったことなかったし、あとマウスよりもキーボードのほうが速いってのもありますね。まあ要するに自分にとってソースコードのほうが画面配置がわかりやすいです。

実際作ってみる

目標と手段

まあ何事もまずは目標を決めるところからですね。この記事を作るにあたってなるべく簡単なものにしたいと思いますので、とりあえず一番基本なところ:自前の UIView サブクラスを作って、そのサブクラスで定番の「Hello World!」を画面のド真ん中に表示したいと思います。ここで肝になっているのは、どんなデバイスであろうと、必ず「画面サイズ」から、必要な要素の配置すべき場所を計算して、画面に表示するところです。ですので今後どんな画面サイズの iOS 端末が出ようと、必ずその端末のド真ん中に表示するはずです。

プロジェクトを作る

目標と手段が決まったら、次は実際手を動かすところですね。はい、Xcode を立ち上げましょう。もちろん Swift ですので、最新の Xcode(現時点ではバージョン 6.1.1 です)で作りましょう。「ファイル」→「New」→「Project」で、iOS の Single View Application を選びます。
スクリーンショット

そして Language は Swift、Devices は Universal にします。
スクリーンショット

あとは場所とか決めて OK 押せばプロジェクトが作成されます。ここで必要に応じて Git などのバージョン管理システムを導入することもおすすめします。

Story Board をプロジェクトから削除する

初期プロジェクトが出来上がったら、次は Story Board を実際プロジェクトから削除しましょう。だって使わないもん(笑)。削除するものは2箇所あります、まずはプロジェクトの Info から Main storyboard file base name という項目をまるごと削除します。
スクリーンショット

あとは Main.storyboard をプロジェクトから削除します。
スクリーンショット

自前の UIView サブクラスを作る

Xcode の左下から「+」ボタンを押して、「New File」を選びます。
スクリーンショット

そして Cocoa Touch Class を選びます。
スクリーンショット

その次に Class の項目に適当に好きな名前(ここでは MainView にしています)をつけて、Sublcass of の項目は「UIView」にしましょう。
スクリーンショット

あとは保存場所とか選べば(当たり前ですが普通はプロジェクトの本体の下に置きます)MainView という UIView のサブクラスが作られます。
スクリーンショット

自前の UIView サブクラスを編集

初期ファイルが出来上がったら次に実際このサブクラスを作りましょう。まずは「Hello World!」を表示するラベル「mainLabel」をクラスの直下に宣言しておきます

MainView.swift
    let mainLabel: UILabel

次に frame からの init メソッドを作ります。

MainView.swift
    override init(frame: CGRect) {

        // MainView のフレームサイズを定数に入れておきます。
        let mainViewFrame = frame

        // ラベルのサイズを設定しておきます。
        let mainLabelSize = CGSizeMake(240, 30)

        // MainView のフレームサイズとラベルのサイズからラベルがちょうど中央に来るように場所を計算します。
        let mainLabelOrigin = CGPointMake(
            (mainViewFrame.width - mainLabelSize.width) / 2,
            (mainViewFrame.height - mainLabelSize.height) / 2
        )

        // ラベルのフレームを定数に設定しておきます。
        let mainLabelFrame = CGRectMake(mainLabelOrigin.x, mainLabelOrigin.y, mainLabelSize.width, mainLabelSize.height)

        // 「mainLabel」を作ります。
        mainLabel = UILabel(frame: mainLabelFrame)

        // 「mainLabel」の表示テキストを設定します。
        mainLabel.text = "Hello World!"

        // 「mainLabel」の表示テキストのセンタリングを設定します。
        mainLabel.textAlignment = .Center

        // 「mainLabel」の設定が全て終わりましたので、親クラスを初期化します。
        super.init(frame: frame)

        // MainView クラスの背景色を白に設定します。
        self.backgroundColor = UIColor.whiteColor()

        // MainView に「mainLabel」を表示します。
        self.addSubview(mainLabel)
    }

ViewController に MainView を入れる

MainView クラスの設定が終わったら、次にいよいよ ViewController に MainView を入れますが、やり方はとても簡単です。「viewDidLoad()」メソッドの「super.viewDidLoad()」の命令の下に2命令を入れるだけです。

ViewController.swift
        // 画面サイズを初期値として MainView クラスを mainView としてインスタンス化します。
        let mainView = MainView(frame: UIScreen.mainScreen().bounds)

        // mainView オブジェクトを表示します。
        self.view.addSubview(mainView)

ViewController を Root View Controller として設定

さていよいよラストです。勝利がもう目の前に見えます。まずは AppDelegate クラスの直下に「window」宣言の直後に ViewController クラスをインスタンス化します。

AppDelegate.swift
    // ViewController を viewController としてインスタンス化します。
    let viewController = ViewController()

最後は「application didFinishLaunchingWithOptions」メソッドに ViewController を利用するように設定します

AppDelegate.swift
        // アプリウィンドウを設定します。
        self.window = UIWindow(frame: UIScreen.mainScreen().bounds)

        // ウィンドウをヴィジブルにします。
        self.window?.makeKeyAndVisible()

        // ウィンドウの rootViewController を viewController に設定します。
        self.window?.rootViewController = viewController

プレビュー

ここまで出来たらもう勝利です。あとは実際動かしてみてちゃんと「Hello World!」が画面のど真ん中に表示されていることを確認するだけです。ここでは一応シミュレーターの iPhone 5 と iPad 2 だけ貼り付けますが、まあ理論上どんな解像度でもちゃんとド真ん中に表示してくれます。
iPhone 5
iPad 2

後記

以上を持ちまして、自前でクラスを作って、Story Board というやらを使わずにソースコードで画面配置することができました。あとは適当に MainView クラスを弄れば思うどおりの動作ができてしまいます。もちろんちょっとソースコードが面倒になりますが、ボタンを4つ作って一列に綺麗に並べるなんてことも余裕で出来てしまいます。更にもう少し複雑になってしまいますがもちろん UITabBarController のサブクラスを自作して iOS ならではの画面遷移を作ることだってできます。なにせソースコード化できなかったらそもそもアプリが作れないから Story Board ができてソースコードでできないことなんて普通ないはずです。面倒くさいだけです。

雑談

ちなみにですが、ここで記載したアプローチはあくまで全てのデバイスの画面解像度をネイティブ解像度として画面要素を配置するアプローチですが、実はもう一つのアプローチがあります。それはずばり全てのデバイスを同一解像度と見なし、最後スケーリングで表示する方法です。メリットは今後どんな解像度が出回しても考えなければならない解像度は一つだけで済むので面倒な手間が省けるところです。それに最初からある程度大きな解像度でプログラミングすれば画像がボケたりすることもあまりないです(少なくとも肉眼ではわかりづらいでしょう)。もちろんデメリットもあります、それは解像度に応じた最適化ができないことです(ツール系ならこれは致命的ですね、例えば Twitter のアプリが iPad レベルのでかい解像度でも iPhone 5 レベルの内容しか表示できなければぶっちゃけキレます)。そして最初から大きすぎる解像度で想定しちゃうと、逆に今度小さい端末で表示する時ボタンが押しづらいとか言うハプニングも予想されます。

まあ一応やり方のヒントを出しますと、辺長で scale 変数を計算し、addSubview を呼び出す前に先に subview.transform を呼び出すことです。