스파르타 코딩 클럽 - iOS 스타터 6기/본 캠프

22. 스파르타 코딩 클럽 - 코드베이스 UI

seongpil Heo 2025. 3. 27. 20:52

 🎯 코드베이스 UI

  • 코드 베이스란 스토리보드 같은 인터페이스 빌더를 사용하지 않고, 코드 작성으로만 UI 구성을 하는 것
  • 스토리보드에서 하던 뷰의 size, constraint 및 모든 속성들을 코드로 작성한다.
  • 팀끼리 협업할 때, github에 코드를 올리고 서로 코드 리뷰를 하게 되는데, 스토리보드로 작성한 UI를 github에 올린 것보다, swift로 작성한 UI 코드를 github에 올린 것이 가독성이 더 좋음.
  • 다만 스토리보드처럼 눈에 보이는 상태로 UI를 구성하는 것이 아니기 때문에, 코드 베이스로 UI를 작성하면 반드시 실행시켜서 확인해야 한다는 단점이 존재.

좌 : 스토리보드 UI를 github에 올린 모습 / 우 : 코드베이스 UI를 github에 올린 모습


 📚 스토리보드 삭제

🧐 스토리보드를 일절 사용하지 않고, 코드만으로 UI를 구성하기 위해 프로젝트에서 스토리보드를 완전히 삭제합니다.

 

1. Main이라는 이름의 스토리보드 삭제. Move to Trash로 삭제할 것

 

2. into.plist라는 파일에서 ctrl+F로 검색 → main 검색해서 Storyboard Name 항목 삭제

 

3. 프로젝트 파일에서 TARGETS 선택 후 → Build Settings로 이동 → ctrl+F로 main 검색 → UIKit Main Storyboard File Base Name 항목 삭제

 

4. 앱에게 맨 처음 시작할 뷰를 알려줘야 하므로 SceneDelegate.swift 파일에 다음과 같은 코드 작성

 

코드 및 설명

// SceneDelegate.swift

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
		// 윈도우. 앱에 반드시 한 개는 필요한 가장 근본이 되는 뷰. 이 위에 뷰가 쌓이기 시작.
    var window: UIWindow?

		// 앱을 시작할때 세팅해줄 코드를 작성하는 곳.
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // UIWindow 객체 생성.
        guard let windowScene = (scene as? UIWindowScene) else { return }
        let window = UIWindow(windowScene: windowScene)
        
        // window 에게 루트 뷰 지정. 
        window.rootViewController = ViewController()
        
         // 이 메서드를 반드시 작성해줘야 윈도우가 활성화 됨.
        window.makeKeyAndVisible()
        self.window = window
    }

 


 🛠️ 코드베이스 UI 실습 - NSLayoutConstraint 사용

UI Label, Button, Image

 

1. UI Label

import UIKit

class ViewController: UIViewController {

    let label = UILabel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureUI()
    }

    private func configureUI() {
        view.backgroundColor = .white
        label.text = "안녕하세요"
        label.textColor = .black
        label.translatesAutoresizingMaskIntoConstraints = false // 오토레이아웃을 활성화 시키기 위해 작성하는 코드
        
        view.addSubview(label)
        NSLayoutConstraint.activate([
            label.widthAnchor.constraint(equalToConstant: 80),
            label.heightAnchor.constraint(equalToConstant: 40),
            label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            label.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        ])
    }

}

 

2. UI Button

import UIKit

class ViewController: UIViewController {

    let button = UIButton()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureUI()
    }

    private func configureUI() {
        view.backgroundColor = .white
        
        button.setTitle("Click", for: .normal)
        button.setTitleColor(.white, for: .normal)
        button.backgroundColor = .red
        button.translatesAutoresizingMaskIntoConstraints = false
        button.layer.cornerRadius = 10
        
        view.addSubview(button)
        NSLayoutConstraint.activate([
            button.widthAnchor.constraint(equalToConstant: 120),
            button.heightAnchor.constraint(equalToConstant: 60),
            button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        ])
    }

}

 

3. UI ImageView

import UIKit

class ViewController: UIViewController {

    let imageView = UIImageView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureUI()
    }

    private func configureUI() {
        view.backgroundColor = .white
        
        imageView.image = UIImage(named: "cat")
        imageView.backgroundColor = .black
        imageView.contentMode = .scaleAspectFit
        imageView.translatesAutoresizingMaskIntoConstraints = false
        
        view.addSubview(imageView)
        NSLayoutConstraint.activate([
            imageView.widthAnchor.constraint(equalToConstant: 300),
            imageView.heightAnchor.constraint(equalToConstant: 300),
            imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        ])
    }

}

 🎯 SnapKit 

💡 SnapKit 이란?
SnapKit 이란 코드베이스로 UI를 작성할 때, 조금 더 간결한 문법을 사용하도록 도와주는 서드파티 라이브러리.
NSLayoutConstraint을 사용할 때보다 편하게 코드를 작성할 수 있게 도와준다.
🧑🏻‍💻 현업에서 가장 많이 사용하는 필수 라이브러리 중 하나.

🛠️ 코드베이스 UI 실습  - SnapKit 사용

 

1. UI Label

import UIKit
import SnapKit

class ViewController: UIViewController {

    let label = UILabel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureUI()
    }

    private func configureUI() {
        view.backgroundColor = .white
        label.text = "안녕하세요"
        label.textColor = .black
        
        view.addSubview(label)
        
        label.snp.makeConstraints({ make in
            make.width.equalTo(80)
            make.height.equalTo(40)
//            make.centerX.equalToSuperview()
//            make.centerY.equalToSuperview()
            make.center.equalToSuperview() // center X,Y를 한번에 작성 가능
        })
        
    }

}

 

2. UI Button

import UIKit
import SnapKit

class ViewController: UIViewController {

    let button = UIButton()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureUI()
    }

    private func configureUI() {
        view.backgroundColor = .white
      
        button.setTitle("Click", for: .normal)
        button.setTitleColor(.white, for: .normal)
        button.backgroundColor = .red
        button.layer.cornerRadius = 10
        
        view.addSubview(button)
        
        button.snp.makeConstraints{
            $0.width.equalTo(120)
            $0.height.equalTo(60)
            $0.center.equalToSuperview()
        }
    }

}

 

3. UI ImageView

import UIKit
import SnapKit

class ViewController: UIViewController {

    let imageView = UIImageView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureUI()
    }

    private func configureUI() {
        view.backgroundColor = .white
      
        imageView.image = UIImage(named: "cat")
        imageView.backgroundColor = .black
        imageView.contentMode = .scaleAspectFit
        
        view.addSubview(imageView)
        
        imageView.snp.makeConstraints{
            $0.width.height.equalTo(300)
            $0.center.equalToSuperview()
        }
    }

}

 

4. UI Label, ImageView

import UIKit
import SnapKit

class ViewController: UIViewController {
    
    let imageView = UIImageView()
    let label = UILabel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureUI()
    }
    
    private func configureUI() {
        view.backgroundColor = .white
        
        imageView.image = UIImage(named: "cat")
        imageView.backgroundColor = .black
        imageView.contentMode = .scaleAspectFit
        
        label.text = "고양이"
        label.textColor = .black
        label.font = UIFont.boldSystemFont(ofSize: 30)
        
        [imageView, label]
            .forEach{ view.addSubview($0)} // forEach문을 사용하여 불필요한 코드 작성을 줄임
        
        imageView.snp.makeConstraints{
            $0.width.height.equalTo(300)
            $0.center.equalToSuperview()
        }
        
        label.snp.makeConstraints{
            $0.centerX.equalToSuperview()
            $0.top.equalTo(imageView.snp.bottom).offset(10)
        }
        
    }
    
}

 

 

5. UI ImageView, Label 2개씩

import UIKit
import SnapKit

class ViewController: UIViewController {
    
    let imageView = UIImageView()
    let label = UILabel()
    let imageView2 = UIImageView()
    let label2 = UILabel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureUI()
    }
    
    private func configureUI() {
        view.backgroundColor = .white
        
        imageView.image = UIImage(named: "cat")
        imageView.backgroundColor = .black
        imageView.contentMode = .scaleAspectFit
        label.text = "고양이"
        label.textColor = .black
        label.font = UIFont.boldSystemFont(ofSize: 30)
        
        imageView2.image = UIImage(named: "dog")
        imageView2.backgroundColor = .black
        imageView2.contentMode = .scaleAspectFit
        label2.text = "강아지"
        label2.textColor = .black
        label2.font = UIFont.boldSystemFont(ofSize: 30)
        
        [imageView, label, imageView2, label2]
            .forEach{ view.addSubview($0)} // forEach문을 사용하여 불필요한 코드 작성을 줄임
        
        imageView.snp.makeConstraints {
            $0.width.height.equalTo(160)
            $0.leading.equalToSuperview().offset(16) // 리딩에서 바깥쪽으로 16만큼
            $0.centerY.equalToSuperview()
        }
        
        label.snp.makeConstraints {
            $0.centerX.equalTo(imageView.snp.centerX)
            $0.top.equalTo(imageView.snp.bottom).offset(16)
        }
        
        imageView2.snp.makeConstraints {
            $0.width.height.equalTo(160)
            $0.trailing.equalToSuperview().inset(16) // 트레일링에서 안쪽으로 16만큼
            $0.centerY.equalToSuperview()
        }
        
        label2.snp.makeConstraints {
            $0.centerX.equalTo(imageView2.snp.centerX)
            $0.top.equalTo(imageView2.snp.bottom).offset(16)
        }
    }

 ✓ TIL

현업에서는 스토리보드 UI 방식보다 코드베이스 UI를 주로 사용

NSLayoutConstraint 보다 SnapKit을 주로 사용

NSLayoutConstraint 사용시 

translatesAutoresizingMaskIntoConstraints = false 를 작성하고

NSLayoutConstraint.activate([ 
	imageView.widthAnchor.constraint(equalToConstant: 300)
    ])
    
형식으로 사용한다
SnapKit 사용시

translatesAutoresizingMaskIntoConstraints = false 를 작성할 필요가 없고

import SnapKit 작성 후

imageView.snp.makeConstraints{
        $0.width.height.equalTo(300)
        $0.center.equalToSuperview()
    }
    
형태로 사용한다.

 

Button의 Action 코드를 구현할 때는

 private func configureUI() {
        view.backgroundColor = .white
      
        button.setTitle("Click", for: .normal)
        button.setTitleColor(.white, for: .normal)
        button.backgroundColor = .red
        button.layer.cornerRadius = 10
        button.addTarget(self, action: #selector(buttonClicked), for: .touchDown)
        
        view.addSubview(button)
        
        button.snp.makeConstraints{
            $0.width.equalTo(120)
            $0.height.equalTo(60)
            $0.center.equalToSuperview()
    }
}  
  
// #selector안에 넣기 위해서는 @objc
    @objc
    private func buttonClicked() {
        print("버튼이 클릭되었음.")
}

 

2개 이상의 UI를 구현할 때는 forEach문을 사용하여 불필요한 코드 작성을 줄인다.

[imageView, label, imageView2, label2]
            .forEach{ view.addSubview($0)} // forEach문을 사용하여 불필요한 코드 작성을 줄임

 

제약사항을 걸 때는

imageView.snp.makeConstraints {
            $0.width.height.equalTo(160)
            $0.leading.equalToSuperview().offset(16) // 리딩에서 바깥쪽으로 16만큼
            $0.centerY.equalToSuperview()
    }
    
imageView2.snp.makeConstraints {
            $0.width.height.equalTo(160)
            $0.trailing.equalToSuperview().inset(16) // 트레일링에서 안쪽으로 16만큼
            $0.centerY.equalToSuperview()
    }
  • offset()inset()을 사용
  • width와 height값이 같을때는 width.height.equalTo(160)처럼 한줄에 작성 가능
  • euqalToSuperView는 제일 근본이 되는 View에 크기를 맞출 때 사용한다
  • 왼쪽은 leading 오른쪽은 trailing