【iOS】画面の向きごとに違う画像を表示したかった

5 min read読了の目安(約3100字

したかったこと

背景に画像を表示するViewControllerにおいて

  1. 縦画面と横画面で違う画像を表示したい
  2. あるトリガーで背景画像を変更したい

この2つを両立させようとしました

今回は例として、

  • 縦画面で普通の茶トラ猫が、横画面でウインクをした茶トラ猫が背景画像に出現する
  • ボタンを押すと茶トラ猫ではなくサバトラ猫に変更する

この用件で説明していきます

アプローチ1 UIImageViewひとつで頑張る

UIImageViewはレイアウトの制約と同様に画面サイズごとに異なる画像を設定できます
UIImageViewのインスペクタのImageの左にある+を押すことで各画面サイズで表示する画像を設定できます

vcat hcat

これでしたかったことの1つ目はクリアできました。では2つ目はどうでしょうか
画像の変更はコード上で行うしかありませんが、UIImageViewのOutletは画面の大きさごとには設定できません。すると以下の問題が発生します

  • サバトラ猫に変更した後に画面を回転させると、元のStoryboardの設定が適用されて茶トラ猫に戻される

以上が解決できなさそうだったためこの方法は断念しました

アプローチ2 UIImageViewをたくさん使って力技

アプローチ1では1つ目の用件はクリアできたので、その要素を取り入れつつ2つ目を実現しました
単純にUIImageViewをもう一つ増やして、茶トラ猫とサバトラ猫を表示するビューを別にしてしまえば良いのです
以下は表示する猫を切り替えるコードの実装例です

import UIKit

final class ViewController: UIViewController {
    /// 現在茶トラ猫が表示されているかどうか
    private var isTyatora = true
    /// 茶トラ猫を表示するUIImageView
    @IBOutlet weak private var tyatoraImageView: UIImageView!
    /// サバトラ猫を表示するUIImageView
    @IBOutlet weak private var sabatoraImageView: UIImageView!
    
    /// 茶トラ猫とサバトラ猫を切り替えるUIButtonのアクション
    @IBAction private func catChangeButtonPressed(_ sender: UIButton) {
        self.tyatoraImageView.isHidden = isTyatora
        self.sabatoraImageView.isHidden = !isTyatora
        self.isTyatora = !self.isTyatora
    }
}

無事用件を満たすことはできました
しかしこの方法では以下のデメリットがあります

  • UIImageViewを一つ多く保持するためメモリ使用量が増加する
  • 猫の種類が増えた時に対応が大変になっていく(可読性の観点も踏まえて)

次の方法でこれらを解消できたため、個人的ベストアプローチとして紹介します

マイベストアプローチ Asset Catalogを活用

アプローチ1では、UIImageViewのimageを画面サイズ別に設定することで画面が縦の時と横の時で別々の画像を表示しました
が、Asset Catalogから画像自体に各画面ごとの設定を行うことができました
やり方はレイアウトの時と同じです

  1. 画面サイズごとに出し分けたい画像のHeight ClassをAnyからAny & Compactにして
    hcasset
  2. 出現したCompact Heightに横画面の時の画像を設定する
    hcasset2

今回の場合、茶トラ猫とサバトラ猫の画像両方に設定することで一つのUIImageViewの画像を変更するだけで縦横に対応した画像を変更することができます
gif
以下は画像を切り返るコードの実装例です

import UIKit

final class ViewController: UIViewController {
    /// 現在茶トラ猫が表示されているかどうか
    private var isTyatora = true
    /// 猫を表示するUIImageView
    @IBOutlet weak private var catImageView: UIImageView!
    
    /// 茶トラ猫とサバトラ猫を切り替えるUIButtonのアクション
    @IBAction private func catChangeButtonPressed(_ sender: UIButton) {
        let catName = self.isTyatora ? "sabatora_cat" : "tyatora_cat"
        self.catImageView.image = UIImage(named: catName)
        self.isTyatora = !self.isTyatora
    }
}

UIImageViewは一つで済むため、猫の種類が増えても変数が増えることもありません
また、使用メモリ量もアプローチ2の時よりも減少しています

まとめ

Asset Catalogはとても便利、と思うことが多いです
色と画像をAsset Catalogで設定しておくと、コード上…よりはStoryboard上でかなり便利になります
iOS始めたてのころは(参加したプロジェクトが)UIも全てコード上で記述していたため、Storyboardを利用するようになってからはいろいろと目から鱗でした

iOS/Swift2年目に突入し、初級者向けでもいいからアウトプットを起こそうかなと思い書いてみました
SwiftUIもかなり進化を遂げてきているので、そろそろ馴染ませていきたいなと思うこの頃です