[iOS] Layout - 01

Sangwon Shin·2021년 12월 19일
0

iOS

목록 보기
7/9

🤷‍♂️ 갑자기 왜 Layout?

iOS 공부를 처음 시작하고 지금까지 항상 view 를 구성할 때 스토리보드에서 Auto Layout 을 잡아 줬었습니다.(항상 짱구😿)

이번에는 스토리보드 없이 코드만으로 View 를 구성하고 Auto Layout을 설정하는 방법에 대해서 공부 해보겠습니다.


🏗 Custom View

TableView Cell, CollectionView Cell 들을 Custom 하게 만들 때 xib형태로 만들었습니다.

Apple에서 xib 연결을 지원하는건 UIViewController, UITableViewCell, UICollectionViewCell 3가지 입니다.

이와 마찬가지로 xib 형태로 Custom 하게 View를 만들 수 있습니다. 하지만 차이점이 있습니다.

앞선 Custom cell 을 만들때는 xib -> nib 파일로 변환시키는 Unarchiving 과정에서 init(coder: NSCoder) 를 통해 객체를 생성하지 않았지만 Custom View 생성을 위해서는 해당 이니셜라이저를 통해서 객체를 생성해야 합니다.

❗️그럼 왜 custom cell 만들 땐 안하나요? -> awakefFromNib 에서 초기화 해준다고 하는데 좀 더 학습이 필요합니다.


위와 같은 형태의 View 를 반복적으로 사용해야 하는 경우가 있습니다. 우선 view 를 만들고 레이아웃 설정을 해줍니다.

그리고 UIView 를 상속받는 클래스 파일을 하나 만들어서 연결해줍니다.
❗️File's Owner 에서 연결해야 합니다.
❗️Safe Area Layout 가이드 설정해제, Simulator size - Freefrom

import UIKit

    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var imageLabel: UILabel!
    @IBOutlet weak var contentLabel: UILabel!
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        loadView()
        loadUI()
    }
    
    func loadView() {
        let view = UINib(nibName: "NameCardView", bundle: nil).instantiate(withOwner: self, options: nil).first as! UIView
        self.addSubview(view)
        view.frame = bounds
        view.backgroundColor = .systemCyan
        view.clipsToBounds = true
        view.layer.cornerRadius = 10
    }
    
    func loadUI() {
        imageView.image = UIImage(systemName: "star.fill")
        imageLabel.textAlignment = .center
        imageLabel.text = "TEST"
        contentLabel.text = "PLZ"
    }
}

그리고 스토리보드에 view 를 하나 추가하고 레이아웃을 설정한 뒤 앞서 작성한 클래스를 상속받도록 하면 결과를 확인할 수 있습니다.

  • ❗️view.frame = bounds 를 적어주지 않으면 스토리보드에서 설정한 레이아웃이 적용되지 않습니다.
  • ❗️ xib -> nib 변환과정을 통해 view를 추가하고 난 뒤에 view에 추가한 객체들을 설정 해야합니다.

이제 위의 과정을 스토리보드를 사용하지 않고, 코드만 이용해서 만들어보겠습니다.

먼저, 커스텀뷰를 코드로 작성해보겠습니다.


class NameCardViewCode: UIView {
    
    let imageView = UIImageView()
    let imageLabel = UILabel()
    let contentLabel = UILabel()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        loadLayout()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func loadLayout() {
        self.addSubview(imageView)
        self.addSubview(imageLabel)
        self.addSubview(contentLabel)
        
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageLabel.translatesAutoresizingMaskIntoConstraints = false
        contentLabel.translatesAutoresizingMaskIntoConstraints = false
        
        imageLabel.textAlignment = .center
        imageLabel.font = .boldSystemFont(ofSize: 20)
        contentLabel.font = .systemFont(ofSize: 15)
        
        NSLayoutConstraint.activate([
            imageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 18),
            imageView.centerYAnchor.constraint(equalTo: centerYAnchor),
            imageView.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 0.5),
            imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor, multiplier: 0.5)
        ])
        
        NSLayoutConstraint.activate([
            imageLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 18),
            imageLabel.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 18),
            imageLabel.widthAnchor.constraint(equalTo: imageView.widthAnchor)
        ])
        
        NSLayoutConstraint.activate([
            contentLabel.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 20),
            contentLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
            contentLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 18)
        ])
        
    }
 
}

스토리보드가 아닌 코드로만 구성하기 때문에 xib -> nib 형태로의 변환이 필요하지 않기 때문에 nib 을 load 하는 과정이 필요하지 않고, Coder를 통해서 클래스를 생성할 필요도 없습니다.

🤷‍♂️그런데 왜 required init?(coder: NSCoder) 을 명시했나요?

UIView, UIViewController 는 NSCoding 프로토콜을 채택하고 있는데 여기서 init?(coder: NSCoder) 을 명시했기 때문에 required 를 붙여서 init?(coder: NSCoder) 구현해야만 합니다.

그리고 Auto Layout 을 설정해야 합니다.

  • Frame Based Layout
  • NSLayoutContraints
  • NSLayoutAnchor

세 가지를 이용해서 레이아웃 설정을 할 수 있는데 이번에는 NSLayoutAnchor 를 이용했습니다.

커스텀뷰를 만들었으니, ViewController 에서 코드를 통해 위의 커스텀뷰를 호출하고 화면에 불러오도록 하겠습니다.

class ViewController: UIViewController {
    
    let nameCardView = NameCardViewCode()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        nameCardView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(nameCardView) 
        view.backgroundColor = .white
        setNameCardView()
    }
    
    func setNameCardView() {
        nameCardView.backgroundColor = .systemCyan
        nameCardView.imageView.image = UIImage(systemName: "star")
        nameCardView.imageLabel.text = "TEST"
        nameCardView.contentLabel.text = "PLZ"
        
        NSLayoutConstraint.activate([
            nameCardView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
            nameCardView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 18),
            nameCardView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -18),
            nameCardView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.3)
        ])
    }

}

커스텀뷰를 만들때와 마찬가지로, 커스텀뷰 클래스를 이용해 인스턴스를 만들고 해당 객체의 레이아웃만 설정해주면 결과를 확인할 수 있습니다.


🏷 P.S.

아직까지는 코드로 레이아웃을 잡는게 익숙하지는 않습니다.. 물론 스토리보드로 설정하는것도 늘 새롭습니다ㅎ

다음에는 Navigation, TabBar 로 구성된 조금은 복잡한 View 를 코드를 이용해서 구성해보고, 레이아웃은 SnapKit 을 이용해서 설정해보도록 하겠습니다.

전체코드는 깃허브를 통해서 확인 할 수 있습니다.

profile
개발자가 되고싶어요

0개의 댓글