#はじめに#
これは自分用のメモでもありますが、同時にもし「Storyboard 面倒くさい!ソースコードのみで UI 作りたい!」というような方がいらっしゃいましたら、ご参考になれればと思います。
また、ソースコードは GitHub に公開しております。
#なぜコードで UI 作るか#
ぶっちゃけ言いますと自分 Storyboard 使えないだけです。はい。そもそも以前 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 などのバージョン管理システムを導入することもおすすめします。
##Storyboard をプロジェクトから削除する##
初期プロジェクトが出来上がったら、次は Storyboard を実際プロジェクトから削除しましょう。だって使わないもん(笑)。削除するものは2箇所あります、まずはプロジェクトの Info から Main storyboard file base name という項目をまるごと削除します。
あとは Main.storyboard をプロジェクトから削除します。
##自前の UIView
サブクラスを作る##
Xcode の左下から「+」ボタンを押して、「New File」を選びます。
その次に Class の項目に適当に好きな名前(ここでは MainView にしています)をつけて、Sublcass of の項目は「UIView」にしましょう。
あとは保存場所とか選べば(当たり前ですが普通はプロジェクトの本体の下に置きます)MainView という UIView のサブクラスが作られます。
##自前の UIView サブクラスを編集##
初期ファイルが出来上がったら次に実際このサブクラスを作りましょう。まずは「Hello World!」を表示するラベル mainLabel
をクラスの直下に宣言しておきます
let mainLabel: UILabel
次に init
メソッドを作ります。
override init(frame: CGRect) {
// `mainLabel` を作ります。
self.mainLabel = UILabel()
// `mainLabel` の表示テキストを設定します。
self.mainLabel.text = "Hello World!"
// `mainLabel` の表示テキストのセンタリングを設定します。
self.mainLabel.textAlignment = .center
// `mainLabel` の設定が全て終わりましたので、親クラスを初期化します。
super.init(frame: frame)
// `MainView` クラスの背景色を白に設定します。
self.backgroundColor = .white
// `MainView` に `mainLabel` を表示します。
self.addSubview(mainLabel)
}
init
メソッドを独自に作ったり、オーバーライドしたりしたら、必ずエラーで init(coder:)
が足りないと言われるので、そのまま Fix 押せば大丈夫です
押したら勝手に下記のようなイニシャライザーが挿入されます:
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
中身がただの fatalError
なので、もし Storyboard や XIB などから MainView
を使う予定がなければそのままで大丈夫です。
最後に、ラベルがどんな画面であっても必ず真ん中に来てほしいので、レイアウトを調整します。iOS のレイアウトサイクルでは UIView#layoutSubviews()
のメソッド内で具体的なレイアウト処理を行いますので、このメソッドをオーバーライドします:
override func layoutSubviews() {
// 必ず `super` の `layoutSubviews()` を呼び出します
super.layoutSubviews()
// `mainLabel` がほしいサイズを自分のサイズから `sizeThatFits(_:)` を通して取り出します
let labelSize = self.mainLabel.sizeThatFits(self.bounds.size)
// `mainLabel` を真ん中に置くように、原点座標を先ほど取得したサイズと自分のサイズから割り出します
let x = (self.bounds.width - labelSize.width) / 2
let y = (self.bounds.height - labelSize.height) / 2
let labelOrigin = CGPoint(x: x, y: y)
// `mainLabel` のレイアウトは、`frame` に原点座標とサイズで代入して決めます
self.mainLabel.frame = CGRect(origin: labelOrigin, size: labelSize)
}
ViewController
に MainView
を入れる##
MainView
クラスの設定が終わったら、次にいよいよ ViewController
に MainView
を入れますが、やり方はとても簡単です。viewDidLoad()
メソッドの super.viewDidLoad()
の命令の下に2命令を入れるだけです。
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// 画面サイズを初期値として `MainView` クラスを `mainView` としてインスタンス化します。
let mainView = MainView(frame: self.view.bounds)
// `MainView` に自動サイズ調整用に `autoresizingMask` を設定
mainView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// `mainView` オブジェクトを表示します。
self.view.addSubview(mainView)
}
ViewController
を Root View Controller として設定##
さていよいよラストです。勝利がもう目の前に見えます。まずは AppDelegate
クラスの直下に window
宣言の直後に ViewController
クラスをインスタンス化します。
// `ViewController` を遅延生成で宣言します
private(set) lazy var viewController = ViewController()
最後は application(_:, didFinishLaunchingWithOptions:)
メソッドに ViewController
を利用するように設定します
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// アプリウィンドウを設定します。
self.window = UIWindow(frame: UIScreen.main.bounds)
// ウィンドウをヴィジブルにします。
self.window?.makeKeyAndVisible()
// ウィンドウの rootViewController を viewController に設定します。
self.window?.rootViewController = viewController
return true
}
#プレビュー#
ここまで出来たらもう勝利です。あとは実際動かしてみてちゃんと「Hello World!」が画面のど真ん中に表示されていることを確認するだけです。ここでは一応シミュレーターの iPhone 5 と iPad 2 だけ貼り付けますが、まあ理論上どんな解像度でもちゃんとド真ん中に表示してくれます。
#後記#
以上を持ちまして、自前でクラスを作って、Storyboard というやらを使わずにソースコードで画面配置することができました。あとは適当に MainView
クラスを弄れば思うどおりの動作ができてしまいます。もちろんちょっとソースコードが面倒になりますが、ボタンを4つ作って一列に綺麗に並べるなんてことも余裕で出来てしまいます。更にもう少し複雑になってしまいますがもちろん UITabBarController
のサブクラスを自作して iOS ならではの画面遷移を作ることだってできます。なにせソースコード化できなかったらそもそもアプリが作れないから Storyboard ができてソースコードでできないことなんて普通ないはずです。やりたいことによって面倒になるかもしれないだけです。