코드로 autoLayout 적용하기(SnapKit)(nft프로젝트)

10000DOO·2023년 8월 25일
1
post-thumbnail

이번 프로젝트를 진행하면서 가능한 코드로 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)
        }
    }
}

AutoLayout코드

가운데 UIImageView 제약조건 설정 코드

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)
        ])

테두리를 나타내는 UIView 제약조건 코드

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)
        ])

📖 공부 내용

1. translatesAutoresizingMaskIntoConstraints

iOS에는 Autoresizing이라는 개념이 있다.
Autoresizing은 superView의 bounds가 변할 때 subView의 크기를 재설정 하는 것이다.
AutoresizingMask을 사용하여 Autoresizing을 한다.
하지만 Autolayout과 같이 사용하면 constraints의 충돌을 야기하기 때문에 .translatesAutoresizingMaskIntoConstraints의 옵션을 false로 주어 Autolayout 적용 시 충돌을 방지한다.(기본 = true)
스토리보드를 사용하여 Autolayout을 적용하면 자동으로 false로 변경된다.

2. NSLayoutConstraint

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([]) 안에 넣어 제약조건을 활성화시킨다.

✅ SnapKit 적용 코드

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)
        }
    }
}

AutoLayout코드

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

profile
iOS 개발자 지망생 https://github.com/10000DOO

0개의 댓글