SnapKit에 대해 아는만큼 정리해보기

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
공부하면서 더 추가해나갈 예정입니다. 틀린 부분 혹은 잘못된 부분이 있다면 알려주세요! :)


SnapKit

SnapKit을 사용할 때는 해석 방향이 중요

make.top.equalTo(viewProgress.snp.bottom).offset(30)

순차적으로 해석 (만들다 > 상단을 > 똑같게 > viewProgress의 하단과 > offset은 30

offset? inset?

  • offset: element와의 간격에 사용
  • inset: superView와의 간격에 사용

Q. childView가 superView로부터 top, bottom, left, right 50 spacing 각각 주려면 어떻게?

1. offset: childView의 constraint = superView의 constraint + offset

self.box.make { (make) in 
  make.top.equalToSuperView().offset(50)
  make.left.equalToSuperView().offset(50)
  make.right.equalToSuperView().offset(-50)
  make.bottom.equalToSuperView().offset(-50)
}

2. inset

self.box.make { (make) in
  make.top.left.bottom.right.equalToSuperView().inset(50)
}

위 코드는 아래와 같게 사용할 수도 있다.

self.box.make { (make) in
  make.edges.equalTo(UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50))
}

그치만 각각의 spacing이 같다면 위 방법이 더 낫다.

left, right / leading, trailing

기본적으로 leading, trailing을 사용하는게 localized 때문인데, 이와 같이 설정하면 right-to-left 순서로 읽는 지역에서는 화면이 거꾸로(flip)되어서 표시된다고 한다. 반면 left, right를 사용하면 안그럼!

  • left, right는 모든 지역에서 같은 방향으로 나오지만 leading, trailing은 오른쪽에서 왼쪽으로 읽는 지역에서는 flip된 ui가 나오게 된다.
  • text를 보여줄때에는 leading, trailing을 사용하는게 나을 것 같으며 그림, 지도와 같이 이미지가 보이는 view를 보여줄 때에는 left, right를 쓰는게 나을 것 같다

전체적인 예시를 한번 보자!

class mainVC: UIViewController {

  // 원하는 객채를 이름붙여 만들어줌 
  let nameLbl = UILabel()
  let nameTextField = UITextField()
  let changeBtn = UIButton()

  // 이름만 만들어준 객체를 실제로 뷰에 띄움 
  override func viewDidLoad() {
    super.viewDidLoad()
    self.view.addSubView(self.nameLbl)
    self.view.addSubView(self.nameTextField)
    self.view.addSubView(self.changeBtn)

    // 아래부터 띄워놓은 객체들에게 각각의 위치를 부여해줌
    self.nameLbl.snp.makeConstraints {
      // self.nameLbl은 superView로부터 center에 위치함 
      $0.center.equalToSuperView()
    }

    self.nameTextField.snp.makeConstraints {
      // self.nameTextField의 top은 superView로 부터 80 떨어짐
      // leading, trailing은 각각 24만큼 떨어짐 
      $0.top.equalToSuperView().offset(80)
      $0.leading.equalToSuperView().offset(24)
      $0.trailing.equalToSuperView().offset(-24)
  }

    self.changeBtn.snp.makeConstraints {
      // changeBtn의 top은 nameLabl의 bottom으로부터 24 떨어짐
      // leading, trailing은 nameLbl과 같게
      $0.top.equalTo(self.nameLbl.snp.bottom).offset(24)
      $0.leading.trailing.equalTo(self.nameLbl)
    }
  }
}

translatesAutoresizingMaskIntoConstraints = false

코드로 constraints를 잡으려면 translatesAutoresizingMaskIntoConstraints = false를 명시해줘야하지만
snapKit은 translatesAutoresizingMaskIntoConstraints = false를 내부에서 알아서 해준다.

따라서 굳이 우리가 이를 명시해주지 않아도 되며, 아래는 snapKit의 LayoutConstraintItem 파일에 적혀있는 코드이다.

extension LayoutConstraintItem {
  internal func prepare() {
    if let view = self as? ConstraintView {
      view.translatesAutoresizingMaskIntoConstraints = false
    }
  }
}

Anchor

1. 모든 앵커와 제약 조건 자체를 함께 연결하는 것이 가능

child.snp.makeContraints { make in 
  make.leading.top.trailing.bottom.equealToSuperView()
}

2. edges를 이용해 더욱 간편하게 사용 가능

child.snp.makeConstraints { make in 
  make.edges.equalToSuperView()
}

3. view에 inset 값을 주고싶다면 inser() 가능

child.snp.makeConstraints { make in 
  make.edges.equealToSuperView().inset(15)
}

Constraints

  • multipliedBy()
make.width.equalToSuperView().multipliedBy(0.45)

superView와 width를 같게 만들면서 0.45를 곱한 상태

암시

constraints 기준이 되는 view 값을 최대한 짧게 작성한다.
아래 코드는 다 같은 의미를 지닌다.

view.snp.makeConstraints { make in 
  make.width.equalTo(otherView.snp.width
  make.centerX.equalTo(otherView.snp.centerX)
}

view.snp.makeConstraints { make in 
  make.width.equalTo(otherView)
  make.centerX.equalTo(otherView)
}

view.snp.makeConstraints { make in 
  make.width.centerX.equalTo(otherView)
}

UpdateConstraints

constraints 기준이 될 기존에 지정한 view가 바뀌는게 아닌 constant value(상수값)만 업데이트

extension QuizVC {
  override func willTransition (
    to newCollection: UITraitCollection,
    with coordinator: UIViewControllerTransitionCOordinator
  ) {
    super.willTransition(to: newCollection, with: coordinator)

    // 현재 방향을 설정(bool)
    let isPortrait = UIDevice.current.orientation.isPortrait

    // 세로일 경우 45, 가로일 경우 65로 높이를 바꿔줌
    lblTimer.snp.updateConstraints { make in 
        make.height.equalTo(isPortrait ? 45 : 65)
    }

    // 글꼴 크기를 변경해줌 
    lblTimer.font = UIFont.systerFont(ofize: isPortrait ? 20 : 32, weight: .light)
  }
} 

RemakeConstraints

constant value만 변경되는 것이 아닌 기준이 될 view도 업데이트
기존에 존재하던 constraints는 삭제된다.

func updateConstraints {
  pView.snp.remakeConstraints { make in 
    make.top.equalTo(view.safeAreaLayoutGuide)
    make.width.equalToSuperView().multipliedby(0.1)
    make.height.equalTo(32)
    make.leading.equalToSuperView()
  }
}

lessThanOrEqualTo, priority > 아직 공부 더 필요

override func updateConstaints() {
  self.prowingBtn.snp.updateConstraints { make in
    make.center.equalTo(self);
    make.width.height.equalTo(buttonSize).priority(250)
    make.width.height.lessThanOrEqualTo(self)
  }
  super.updateConstraints()
}