Swift4 コードでレイアウトを組む②

エンジニアの佐山です。

こちらの記事は前回の記事の続編となります。

note.welks.co.jp

前回はコードだけでレイアウトを組んだ際に、デバイスを横向きにするとレイアウトが崩れてしまいました。今回は前回とは違う方法でその問題を解決したいと思います。

こちらのリポジトリを参考に解説を加えました。

プロジェクトを作成

プロジェクト名は適当で構いません。
前回の記事の「Storyboardを破棄」まで終わらしておいてください。

AppDelegate.swift

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
  
  // ベースとなるwindowを作成
  self.window = UIWindow(frame: UIScreen.main.bounds)
      
  // 基準となるコントローラーを設定
  window?.rootViewController = ViewController()
      
  window?.makeKeyAndVisible()
      
  return true
}

BaseView.swiftの作成

ベースになるビューファイルを作成していきます。

f:id:sayamaken0402:20180515234127p:plain

フォルダをクリックし、ファイルを新規作成→Cocoa Touch ClassでNextを押します。

f:id:sayamaken0402:20180515234259p:plain

ClassをBaseViewにし、 SubClass ofをUIViewにして、nextを押し、Createすると以下のようなBaseView.swiftファイルが出来上がるかと思います。

import UIKit

class BaseView: UIView {

    /*
    // Only override draw() if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func draw(_ rect: CGRect) {
        // Drawing code
    }
    */

}

では作成したBaseView.swiftにベースとなるビューを書いていきます。

import UIKit

class BaseView: UIView {    
}

初期状態(コメントアウトは削除した)は以上のような感じかと思います。 ここにinitメソッドを使って初期化処理を書いていきます。

import UIKit

class BaseView: UIView {

    // イニシャライザメソッドのinitをオーバーライドする
    override init(frame: CGRect) {
    }
}

以上まで書くと、xcodeが以下のようにエラーを吐きます。 f:id:sayamaken0402:20180515235131p:plain

エラーをクリックすると以下のように展開されるので、

f:id:sayamaken0402:20180515235128p:plain

xcodeのエラーに従い、以下のようにします。

import UIKit

class BaseView: UIView {

    // イニシャライザメソッドのinitをオーバーライドする
    override init(frame: CGRect) {
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}

まだエラーを吐きます。

f:id:sayamaken0402:20180515235124p:plain

xcodeに従いましょう。xcodeは神です。

import UIKit

class BaseView: UIView {

    // イニシャライザメソッドのinitをオーバーライドする
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        // 背景色を設定
        self.backgroundcolor = .white
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}

以上でエラーはなくなりました。

シュミレーターに表示させてみる

viewDidLoad内にまた処理を書いていきましょう。

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // BaseView.swiftを作成
        let baseView = BaseView(frame: self.view.bounds)
        
        // 自動でリサイズしてくれる設定
        baseView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        
        // viewにサブクラスとしてBaseViewを追加
        self.view.addSubview(baseView)
    }
}

以下のようなまっさらなアプリが立ち上がるかと思います。

f:id:sayamaken0402:20180516000337p:plain

BaseViewにラベルを設置する

BaseView.swiftを開き、initメソッドを以下のようにしていきましょう。

override init(frame: CGRect) {
        super.init(frame: frame)
        
  // 背景色を白にする
  self.backgroundColor = .white
        
  // ラベルに文字列を設定
  label.text = "Hello World!!"
        
  // ラベルの文字をセンター寄せに
  label.textAlignment = .center
        
  // ラベルの文字サイズを変更
  label.font = UIFont.systemFont(ofSize: 30)
        
  // ラベルを表示させる
  self.addSubview(label)
}

上記ではまだ表示されません。

layoutSubViewsメソッドを作成する

layoutSubviewsメソッドを作成していきます。 layoutSubviewsメソッドとは、viewのframeが変化した時に呼び出されるメソッドらしいです。 詳しくは公式ドキュメントを参照ください。

ではBaseView.swiftinitメソッドの下にlayoutSubviewsメソッドを加えます。

import UIKit

class BaseView: UIView {

    let label = UILabel()
    
    // イニシャライザメソッドのinitをオーバーライドする
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        // 背景色を白にする
        self.backgroundColor = .white
        
        // ラベルに文字列を設定
        label.text = "Hello World!!"
        
        // ラベルの文字をセンター寄せに
        label.textAlignment = .center
        
        // ラベルの文字サイズを変更
        label.font = UIFont.systemFont(ofSize: 30)
        
        // ラベルを表示させる
        self.addSubview(label)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func layoutSubviews() {
        
    }
    
}

こんな感じ

以下のようにさらに追加していきます。

override func layoutSubviews() {
  super.layoutSubviews()
        
  // ラベルのサイズを取得しておく
  let labelSize = self.label.sizeThatFits(self.bounds.size)
        
  let x = (self.bounds.width - labelSize.width) / 2
  let y = (self.bounds.height - labelSize.height) / 2
  let labelOrigin = CGPoint(x: x, y: y)
      
  // frameにラベルをセットしレイアウトに渡す
  self.label.frame = CGRect(origin: labelOrigin, size: labelSize)
}

上記でビルドをしてシュミレータを確認しましょう。

f:id:sayamaken0402:20180516002326p:plain

横向きでも...

f:id:sayamaken0402:20180516002323p:plain

きっちり崩れず表示されるようになりました!