【iOS】세로 화면과 가로 화면에서 레이아웃 전환이 필요한 때에 사용할 수 있는 Tips

소개



이번에는 앱을 회전할 때 '세로 레이아웃'과 '가로 레이아웃'을 동적으로 다른 레이아웃으로 하고 싶다는 요건을 충족해야 할 때 내가 자주 구현에서 사용하는 방식을 기사로 보았습니다.

아마도 왕도의 방법으로는, 회전했을 때에 제약을 변경해, 레이아웃을 바꾸는 것이라고 생각합니다만, 그것을 하는 것이 불편한 때에 자주 사용하고 있는 방법이 됩니다.

이럴 때 사용할 수



이번 기사 속에서 구현하고 있는 예를 올리면 아래의 이미지와 같은 느낌입니다.
세로일 때는 세로 1열로 늘어서 있는 레이아웃을 채용하고 있습니다만, 옆일 때는 왼쪽에 크게 표시하는 View와 오른쪽에 2개 늘어서 있는 View로 나뉘어져 있습니다.
세로 레이아웃

가로 레이아웃


이번에는 View에 addSubView 하는 대상이 ①~③의 라벨이지만, TableView나 직접 만든 커스텀 View를 addSubView 하는 경우가 많다고 생각합니다.

구현 방법



StoryBoard 설정



세로 레이아웃

가로 레이아웃


표시하고 싶은 View(이번은 Label)를 태우기 위한 부모 View를, 세로와 가로로 따로따로 준비합니다. 이러한 상위 View는 소스 코드와 @IBOutlet 연결해야 합니다.

StoryBoard를 편집할 때, 편집하지 않는 부모 View에 대해서는, 표시되고 있으면 방해이므로, 「installed」의 체크를 해제하는 것을 추천합니다.

※빌드할 때는 체크를 해 주세요.

소스 코드



ViewRotationViewController.swift
import UIKit

class ViewRotationViewController: UIViewController {

    @IBOutlet weak private var viewFirst: UIView!
    @IBOutlet weak private var viewSecond: UIView!
    @IBOutlet weak private var viewThird: UIView!

    @IBOutlet weak var viewFirstHorizon: UIView!
    @IBOutlet weak var viewSecondHorizon: UIView!
    @IBOutlet weak var viewThirdHorizon: UIView!

    @IBOutlet weak var viewVertical: UIView!
    @IBOutlet weak var viewHorizon: UIView!

    // 子Viewとして表示するラベル(実際にアプリを作るとなると、カスタムViewやTableViewになることが多い)
    private let labelNo1 = UILabel()
    private let labelNo2 = UILabel()
    private let labelNo3 = UILabel()

    private var isPortrait: Bool {
        return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation.isPortrait ?? true
    }

    override func viewDidLoad() {
        initLabel()
        setScreen()
    }

    private func initLabel() {
        labelNo1.text = "①"
        labelNo2.text = "②"
        labelNo3.text = "③"

        labelNo1.textAlignment = .center
        labelNo2.textAlignment = .center
        labelNo3.textAlignment = .center

        labelNo1.font = .systemFont(ofSize: 60)
        labelNo2.font = .systemFont(ofSize: 60)
        labelNo3.font = .systemFont(ofSize: 60)
    }

    private func setScreen() {
        labelNo1.removeFromSuperview()
        labelNo2.removeFromSuperview()
        labelNo3.removeFromSuperview()

        if isPortrait {
            viewFirst.addSubViewFill(labelNo1)
            viewSecond.addSubViewFill(labelNo2)
            viewThird.addSubViewFill(labelNo3)
        } else {
            viewFirstHorizon.addSubViewFill(labelNo1)
            viewSecondHorizon.addSubViewFill(labelNo2)
            viewThirdHorizon.addSubViewFill(labelNo3)
        }
        // 縦Viewと横Viewの表示・非表示を切り替える
        viewVertical.isHidden = !isPortrait
        viewHorizon.isHidden = isPortrait
    }

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        coordinator.animate { [weak self] _ in
            guard let `self` = self else { return }
            // 回転時に、レイアウトの変更を行う
            self.setScreen()
        }
    }
}
addSubViewFill 라는 메서드에 대해서는 화면 가득 자식 View를 붙여 넣기 위해 UIView를 Extension했습니다.
자세한 것은 이쪽의 기사를 봐 주셨으면 합니다.
htps : // 이 m/ぇ모나데_도 t_ぉg/있어 ms/5304c0fc3fb82아 dcf1에5

UIView+Extension.swift
extension UIView {
    func addSubViewFill(_ childView: UIView) {
        self.addSubview(childView)
        childView.translatesAutoresizingMaskIntoConstraints = false
        childView.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
        childView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
        childView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0).isActive = true
        childView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0).isActive = true
    }
}

해설


viewDidLoad() (초회 표시시), viewWillTransition() (회전했을 때)에서 현재 앱의 화면 방향을 판단하여 레이아웃을 전환하는 방법을 채용하고 있습니다. ( setScreen() 를 호출한다.)

포인트로서는, setScreen() 로, 화면의 방향에 의해, 표시하는 아이 View 를 태울 수 있는 부모 View 를 변경하고 있는 점입니다.
회전할 때 setScreen() 를 호출하여 하위 View의 상위 View를 변경하고 레이아웃을 변경할 수 있습니다.

흐름으로서는, 이하와 같은 흐름입니다.
「앱이 회전」→「아이 View를 부모 Viwe로부터 분리한다」→「세로인지 옆인지를 판단」→「적절한 부모 View에 아이 View를 addSubView() 한다」

이렇게 하면 부모 View의 AutoLayout 제약을 전환하는 방법이 아니라 자식 View의 분리&붙여넣기에 의해 레이아웃 변경을 실현하고 있다는 느낌입니다.
이미지로서는, 풀로 1번 붙인 종이를, 벗기고 나서 다른 대지에 붙인다고 하는 느낌일까요.

결론



레이아웃 전환으로 다른 추천 방법이 있으면 꼭 코멘트 란에서 가르쳐 주시면 기쁩니다.
그리고는, SizeClass에 레이아웃을 완전하게 맡기는 방법이 있다고 생각합니다만, iPad는 세로와 옆의 SizeClass가 같기 때문에, 이번은 채용하지 않았습니다.

좋은 웹페이지 즐겨찾기