이번 프로젝트를 진행하면서 가능한 코드로 UI를 그려보려고 했다.
import UIKit
class SplashViewController: UIViewController {
var imageView: UIImageView!
var centerYConstraint: NSLayoutConstraint?
var safeAreaView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// 이미지 뷰 생성
imageView = UIImageView(image: UIImage(named: "SplashImage"))
view.addSubview(imageView)
// 이미지 뷰의 제약 조건 설정
imageView.translatesAutoresizingMaskIntoConstraints = false
centerYConstraint = imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
NSLayoutConstraint.activate([
centerYConstraint!,
imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
imageView.widthAnchor.constraint(equalToConstant: 270),
imageView.heightAnchor.constraint(equalToConstant: 180)
])
// safeArea의 테두리를 설정
safeAreaView = UIView()
safeAreaView.backgroundColor = .clear
safeAreaView.layer.borderWidth = 8
safeAreaView.layer.cornerRadius = 10
safeAreaView.layer.borderColor = UIColor(red: 8/255, green: 30/255, blue: 92/255, alpha: 1).cgColor
view.addSubview(safeAreaView)
safeAreaView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
safeAreaView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 50),
safeAreaView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 50),
safeAreaView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -50),
safeAreaView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -50)
])
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.animate(withDuration: 1, animations: {
// 테두리의 투명도를 0으로 설정하여 흐려져서 사라지도록 함
self.safeAreaView.layer.opacity = 0
self.view.layoutIfNeeded()
}) { _ in
// 애니메이션 완료 후 safeAreaView 제거
self.safeAreaView.removeFromSuperview()
}
UIView.animate(withDuration: 1.3, animations: {
//로고 올라가는 애니메이션
self.centerYConstraint?.isActive = false
self.centerYConstraint = self.imageView.centerYAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 120)
self.centerYConstraint?.isActive = true
self.view.layoutIfNeeded()
}) { _ in
let authenticationVC = AuthenticationViewController()
authenticationVC.modalPresentationStyle = .fullScreen
// 현재 뷰 컨트롤러를 AuthenticationViewController로 전환
self.present(authenticationVC, animated: false, completion: nil)
}
}
}
imageView.translatesAutoresizingMaskIntoConstraints = false
centerYConstraint = imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
NSLayoutConstraint.activate([
centerYConstraint!,
imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
imageView.widthAnchor.constraint(equalToConstant: 270),
imageView.heightAnchor.constraint(equalToConstant: 180)
])
safeAreaView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
safeAreaView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 50),
safeAreaView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 50),
safeAreaView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -50),
safeAreaView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -50)
])
iOS에는
Autoresizing
이라는 개념이 있다.
Autoresizing
은 superView의 bounds가 변할 때 subView의 크기를 재설정 하는 것이다.
AutoresizingMask
을 사용하여Autoresizing
을 한다.
하지만Autolayout
과 같이 사용하면constraints
의 충돌을 야기하기 때문에.translatesAutoresizingMaskIntoConstraints
의 옵션을 false로 주어Autolayout
적용 시 충돌을 방지한다.(기본 = true)
스토리보드를 사용하여Autolayout
을 적용하면 자동으로 false로 변경된다.
Autolayout을 적용하는데 두 가지 방식으로 할 수 있다.
첫번째NSLayoutConstraint(item: Any, attribute: NSLayoutConstraint.Attribute, relatedBy: NSLayoutConstraint.Relation, toItem: Any?, attribute: NSLayoutConstraint.Attribute, multiplier: CGFloat, constant: CGFloat)
위와 같은 방식으로 코드를 작성하여
let topConstraint = NSLayoutConstraint(item: safeAreaView!, attribute: .top, relatedBy: .equal, toItem: view.safeAreaLayoutGuide, attribute: .top, multiplier: 1, constant: 50) let leadingConstraint = NSLayoutConstraint(item: safeAreaView!, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 50) let trailingConstraint = NSLayoutConstraint(item: safeAreaView!, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: -50) let bottomConstraint = NSLayoutConstraint(item: safeAreaView!, attribute: .bottom, relatedBy: .equal, toItem: view.safeAreaLayoutGuide, attribute: .bottom, multiplier: 1, constant: -50) NSLayoutConstraint.activate([topConstraint, leadingConstraint, trailingConstraint, bottomConstraint]) //item: 제약을 지정할 UI //attribute: 제약을 지정할 UI의 속성 //relatedBy: 제약을 지정할 UI와 제약의 기준이 되는 UI 사이의 관계 //toItem: 제약의 기준이 되는 UI //attribute: 제약의 기준이 되는 UI의 속성 //multiplier: 비율 (width에 대한 제약을 설정하는데 2를 넣으면 toItem의 width * 2가 된다.) //constraint: 상수값 (ex 높이 넓이)
이러한 방식으로 적을 수 있다.
두번째는 비교적 코드를 비교적 짧게 작성할 수 있다.safeAreaView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ safeAreaView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 50), safeAreaView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 50), safeAreaView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -50), safeAreaView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -50) ])
NSLayoutConstraint
또는safeAreaView.topAnchor.constraint
방식으로 제약조건을 작성한 뒤NSLayoutConstraint.activate([])
안에 넣어 제약조건을 활성화시킨다.
import UIKit
import SnapKit
class SplashViewController: UIViewController {
var imageView: UIImageView!
var centerYConstraint: Constraint?
var safeAreaView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// 이미지 뷰 생성
imageView = UIImageView(image: UIImage(named: "SplashImage"))
view.addSubview(imageView)
// 이미지 뷰의 제약 조건 설정
imageView.snp.makeConstraints { make in
centerYConstraint = make.centerY.equalTo(self.view.snp.centerY).constraint
make.centerX.equalTo(self.view.snp.centerX)
make.width.equalTo(270)
make.height.equalTo(180)
}
// safeArea의 테두리를 설정
safeAreaView = UIView()
safeAreaView.backgroundColor = .clear
safeAreaView.layer.borderWidth = 8
safeAreaView.layer.cornerRadius = 10
safeAreaView.layer.borderColor = UIColor(red: 8/255, green: 30/255, blue: 92/255, alpha: 1).cgColor
view.addSubview(safeAreaView)
safeAreaView.snp.makeConstraints { make in
make.edges.equalTo(self.view.safeAreaLayoutGuide).inset(UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50))
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.animate(withDuration: 1, animations: {
// 테두리의 투명도를 0으로 설정하여 흐려져서 사라지도록 함
self.safeAreaView.layer.opacity = 0
self.view.layoutIfNeeded()
}) { _ in
// 애니메이션 완료 후 safeAreaView 제거
self.safeAreaView.removeFromSuperview()
}
UIView.animate(withDuration: 1.3, animations: {
//로고 올라가는 애니메이션
self.centerYConstraint?.deactivate()
self.imageView.snp.makeConstraints { make in
self.centerYConstraint = make.centerY.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(120).constraint
}
self.view.layoutIfNeeded()
}) { _ in
let authenticationVC = AuthenticationViewController()
authenticationVC.modalPresentationStyle = .fullScreen
// 현재 뷰 컨트롤러를 AuthenticationViewController로 전환
self.present(authenticationVC, animated: false, completion: nil)
}
}
}
imageView.snp.makeConstraints { make in
centerYConstraint = make.centerY.equalTo(self.view.snp.centerY).constraint
make.centerX.equalTo(self.view.snp.centerX)
make.width.equalTo(270)
make.height.equalTo(180)
}
safeAreaView.snp.makeConstraints { make in
make.edges.equalTo(self.view.safeAreaLayoutGuide).inset(UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50))
}
확실히 snapkit을 적용하지 않은 코드보다 짧고 간단해진 것을 알 수 있다.
snapkit
을 사용하려면 원하는 View 다음에 .snp을 붙여야 된다.
snp.makeConstraints
을 사용하여 제약조건을 활성화시킬 수 있다. 클로저 안에 make 객체를 사용하여 제약조건을 만들어 주면 된다.
제약조건을 업데이트할 때는snp.updateConstraints
, 모든 제약조건을 지우고 다시 만들 때는snp.remakeConstraints
를 사용하면 된다.
🚨 추가 내용
safeAreaView.snp.makeConstraints { make in make.edges.equalTo(self.view.safeAreaLayoutGuide).inset(UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50)) }
위와 같은 코드는 아래 코드와 같은 의미인데 inset과 offset을 헷갈릴 수 있다.
safeAreaView.snp.makeConstraints { make in make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(20) make.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom).inset(20) make.leading.equalTo(self.view.safeAreaLayoutGuide.snp.leading).offset(20) make.trailing.equalTo(self.view.safeAreaLayoutGuide.snp.trailing).inset(20) }
offset은 받은 값만큼 좌표를 이동하는 것이고 ex) top에서 20이면 아래로 20, -20이면 위로 20
inset은 안쪽으로 받은 값만큼 이동하는 것이다 ex) top -> 아래, right -> 왼쪽, left -> 오른쪽
https://inuplace.tistory.com/1047
https://ios-development.tistory.com/672
https://velog.io/@wonhee010/Code%EB%A1%9C-AutoLayout%EC%9D%84-%EC%A4%98%EB%B3%B4%EC%9E%90
https://velog.io/@leeyoungwoozz/iOS-%EC%B4%88%EC%8B%AC%EC%9E%90%EC%9D%98-SnapKit-%EC%82%AC%EC%9A%A9%EA%B8%B0