스토리보드 vs 코드베이스
size
, constraint
및 모든 속성들을 코드로 작성한다.스토리보드 삭제
-> 스토리보드를 일절 사용하지 않고, 코드만으로 UI를 구성하기 위해 프로젝트에서 스토리보드를 완전히 삭제합니다.
Main이라는 이름의 스토리보드 삭제. Move to Trash로 삭제할 것.
into.plist라는 파일에서 ctrl+F로 검색 → main 검색해서 Storyboard Name 항목 삭제.
프로젝트 파일에서 TARGETS 선택 후 → Build Settings로 이동 → ctrl+F로 main 검색 → UIKit Main Storyboard File Base Name 항목 삭제.
앱에게 맨 처음 시작할 뷰를 알려줘야 하므로 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
}
NSLayoutConstraint
leadingAnchor
trailingAnchor
topAnchor
bottomAnchor
widthAnchor
heightAnchor
centerXAnchor
: 뷰에 가로선 그었을 때 그 중간을 의미.centerYAnchor
: 뷰에 세로선 그었을 때 그 중간을 의미.NSLayoutConstraint.activate([제약조건들])
: 제약조건들을 넣고 코드 작성하면 제약조건 활성화.UILabel
▪️ NSLayoutConstraint를 이용해서 UILabel 그려보기.
let label = UILabel()
코드로 라벨을 선언.configureUI()
라는 메서드를 정의하고, 그 안에 UI를 세팅하는 코드들을 담습니다.view
는 ViewController가 기본적으로 갖고 있는 기본 view를 의미.view.addSubview(label)
text
, textColor
등.label.translatesAutoresizingMaskIntoConstraints = false
이 코드는 오토 레이아웃을 활성화시키기 위해 작성 필요.NSLayoutConstraint.activate
안에 제약조건들을 작성.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)
])
}
}
UIButton
▪️ NSLayoutConstraint를 이용해서 UIButton 그려보기.
let button = UIButton()
코드로 버튼을 선언.setTitle
, setTitleColor
, backgroundColor
등 속성을 코드로 부여.layer.cornerRadius
속성 사용.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)
])
}
}
UIImageView
▪️ NSLayoutConstraint를 이용해서 UIImageView 그려보기.
let imageView = UIImageView()
코드로 이미지 뷰를 선언.image
, backgroundColor
, contentMode
등 속성을 코드로 부여.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 을 사용할 때보다 편하게 코드를 작성할 수 있게 도와준다.
🧑🏻💻 현업에서 가장 많이 사용하는 필수 라이브러리 중 하나.
Swift Package Manager (SPM)
Swift Package Manager
란, 프로젝트에 서드파티 라이브러리를 가져와서 사용할 수 있도록 도와주는 도구.Cocoapods
, Carthage
등 다른 도구도 있지만, SPM
이 애플에서 지원하는 퍼스트 파티 도구.SPM
을 통해서 SnapKit
을 프로젝트에 추가해 봅시다.<프로젝트에 SPM으로 서드파티 라이브러리 추가하는 방법>
프로젝트 파일에서 TARGETS 선택 → General → Frameworks, Libraries 영역에서 십자(+) 버튼 클릭
Add Other… 클릭 후 Add Package Dependency 클릭.
구글에 사용할 라이브러리 검색 → github 들어가서 Code → HTTPS 복사.
검색창에 사용할 라이브러리의 주소 붙여 넣기 → Add Package.
import SnapKit
이 가능해진 것 확인.UILabel
▪️ SnapKit을 이용해서 UILabel 그려보기.
let label = UILabel()
코드로 라벨을 선언.label.translatesAutoresizingMaskIntoConstraints = false
이 코드가 SnapKit
에서는 필요하지 않음.NSLayoutConstraint.activate
로 작성하던 제약조건들을 SnapKit
문법으로 작성.inset
: 슈퍼뷰의 경계로부터 내부 여백을 설정합니다.offset
: 특정 뷰나 위치로부터 외부 여백을 설정합니다.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 {
$0.width.equalTo(80)
$0.height.equalTo(40)
$0.centerX.equalToSuperview()
$0.centerY.equalToSuperview()
}
}
}
아래처럼 작성할 수도 있지만, Swift 문법에서 클로저의 인자는 $0으로 축약할 수 있기 때문에 축약.
// (1) 축약하지 않은 코드.
label.snp.makeConstraints { make in
make.width.equalTo(80)
make.height.equalTo(40)
make.centerX.equalToSuperview()
make.centerY.equalToSuperview()
}
// (2) 축약할 수 있으니 이렇게 축약합시다.
label.snp.makeConstraints {
$0.width.equalTo(80)
$0.height.equalTo(40)
$0.centerX.equalToSuperview()
$0.centerY.equalToSuperview()
}
같은 내용의 NSLayoutConstraint 와 비교했을 때 SnapKit 코드가 가독성이 더 좋음.
구성해야 할 레이아웃 종류들이 많아질수록 체감이 커짐.
translatesAutoresizingMaskIntoConstraints를 신경 쓰지 않아도 됨.
// (1) NSLayoutConstraint 로 작성한 코드.
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) SnapKit 으로 작성한 코드.
label.snp.makeConstraints {
$0.width.equalTo(80)
$0.height.equalTo(40)
$0.centerX.equalToSuperview()
$0.centerY.equalToSuperview()
}
UIButton
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.centerX.equalToSuperview()
$0.centerY.equalToSuperview()
}
}
}
💡 IBAction 에서 했었던, 버튼 클릭 이벤트 추가하는 방법.
button.addTarget(**self**, action: **#selector**(buttonClicked), for: .touchDown)
#selector()
내부에 버튼 클릭 시 어떤 로직을 수행할 건지 담긴 메서드 작성.for:
버튼의 어떤 이벤트에 로직을 수행할 것인지.touchDown
: 사용자가 버튼을 터치하는 순간 발생.touchUpInside
: 사용자가 버튼을 터치한 후 손가락을 떼는 순간 발생.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
// 버튼 클릭 이벤트 추가.
button.addTarget(self, action: #selector(buttonClicked), for: .touchDown)
view.addSubview(button)
button.snp.makeConstraints {
$0.width.equalTo(120)
$0.height.equalTo(60)
$0.centerX.equalToSuperview()
$0.centerY.equalToSuperview()
}
}
// #selector() 안에 넣기 위해서는 @objc 키워드 붙여야 함.
@objc
private func buttonClicked() {
print("버튼이 클릭되었음.")
}
}
UIImageView
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.equalTo(300)
$0.height.equalTo(300)
$0.centerX.equalToSuperview()
$0.centerY.equalToSuperview()
}
}
}
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) }
imageView.snp.makeConstraints {
$0.width.equalTo(300)
$0.height.equalTo(300)
$0.centerX.equalToSuperview()
$0.centerY.equalToSuperview()
}
label.snp.makeConstraints {
$0.centerX.equalToSuperview()
$0.top.equalTo(imageView.snp.bottom).offset(16)
}
}
}
import UIKit
import SnapKit
class ViewController: UIViewController {
let imageView = UIImageView()
let imageView2 = UIImageView()
let label = UILabel()
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) }
imageView.snp.makeConstraints {
$0.width.equalTo(160)
$0.height.equalTo(160)
$0.leading.equalToSuperview().inset(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.equalTo(160)
$0.height.equalTo(160)
$0.trailing.equalToSuperview().inset(16)
$0.centerY.equalToSuperview()
}
label2.snp.makeConstraints {
$0.centerX.equalTo(imageView2.snp.centerX)
$0.top.equalTo(imageView2.snp.bottom).offset(16)
}
}
}
UI 디버깅 팁
코드 실행 중에 (런타임에) 화살표 표시한 버튼을 누르면 뷰의 계층구조를 파악하며 디버깅할 수 있음.
동글님,,,!!!!!! 벨로그 구경왔는데 상당한 , , , 썸넬 장인이시군뇨!!!!! 넘 뽀짝하고 재미있어요 ㅎㅅㅎ 직접 만드시는건가욥 !!!!!