본문 바로가기
🍎iOS프로그래밍/AutoLayout

오토레이아웃 Auto Layout - 4(심화)

by 둥둥동동# 2022. 1. 15.
728x90

 

순서

AutoresizingMask오류 -이동

NSLayoutConstraint - 이동

VisualForamtLanguage - 이동

NSLayoutAnchor - 이동

 

시작하기전에

 

이 실습은 인터페이스빌더로 구현된 AutoLayout을 코드로 구현하는데 목적이 있습니다

아래의 뷰는 마진으로 50을 갖습니다 

 

각 빨간색과 파란색은 redView와 blueView로 아웃렛을 선언하고 

이를 감싸고 있는 뷰는 topView로 선언했습니다

 

 

AutoresizingMask오류

 

제약을 정하지 않은 경우 Autoresizing이 적용됩니다

코드로 제약사항을 추가 할 경우 자동으로 적용된 AutoresizingMask와 충돌할 수 있으므로

꼭 아래의 코드를 추가해야 합니다

 

 

subview.translatesAutoresizingMaskIntoConstraints = false
cs

 

 

추가하지 않으면 두 제약이 서로 충돌하며 아래와 같은 오류가 나타납니다

 

 

NSLayoutConstraint

 

NSLayoutConstraint 클래스를 이용하는 방법입니다

 

init(item:attribute:relatedBy:toItem:attribute:multiplier:constant:)

 

위의 생성자를 사용하면 하나의 인스턴스(Constraints)를 정의할 수 있습니다

Constraints를 모두 정의했다면  .activate메서드를 통해 배열형태로 Constraints를 적용할 수 있습니다

item = 적용 View

attribute = NSLayout열거형 멤버를 받습니다 (.leading, .top. bottom ...)

relatedBy = .equal, .lessThanOreuqals, .greaterThanOrequals
toItem = 기준 View

multiplier = 동일 

constant =  동일

 

Margin 미적용

 

let top = NSLayoutConstraint(item: redView, attribute:.top, relatedBy: .equal, toItem: topView, attribute: .top, multiplier: 1.0, constant: 15)
let trailing = NSLayoutConstraint(item: redView, attribute:.trailing, relatedBy: .equal, toItem: topView, attribute: .trailing, multiplier: 1.0, constant: 15)
var width = NSLayoutConstraint(item: redView, attribute: .width, relatedBy: .equal, toItem: nil, attribute:.notAnAttribute, multiplier: 1.0, constant: 100)
var height = NSLayoutConstraint(item: redView, attribute: .height, relatedBy: .equal, toItem: nil, attribute:.notAnAttribute, multiplier: 1.0, constant: 100)
NSLayoutConstraint.activate([top,trailing,width,height])
        
let bottom = NSLayoutConstraint(item: blueView, attribute:.bottom, relatedBy: .equal, toItem: topView, attribute: .bottom, multiplier: 1.0, constant: 0)
let leading = NSLayoutConstraint(item: blueView, attribute:.leading, relatedBy: .equal, toItem: topView, attribute: .leading, multiplier: 1.0, constant: 0)
width = NSLayoutConstraint(item: blueView, attribute: .width, relatedBy: .equal, toItem: nil, attribute:.notAnAttribute, multiplier: 1.0, constant: 100)
height = NSLayoutConstraint(item: blueView, attribute: .height, relatedBy: .equal, toItem: nil, attribute:.notAnAttribute, multiplier: 1.0, constant: 100)
NSLayoutConstraint.activate([bottom,leading,width,height])
cs

 

 

redView에는 top 과 trailing에 constant를 15만큼, blueView에는 bottom, leading에  constant를 0만큼 준 코드입니다

경고가 뜨는 이유는 item의 reView,blueView가 옵셔널타입이기 때문입니다.

!으로 선언되어 자동으로 언래핑을 하니 동작에 문제는 없습니다

width나 height에는 대상view가 없기 때문에 item은 nil, attribute엔 .notAnAttribute 값을 갖습니다

redView의  Constant값을 모두 15로 주니 방향에 문제가 생겼는데요

이 때는 Constant값을 - 하거나 item1과 item2를 서로 변경해야 합니다

 

 

Constant는 화살표방향으로 갈 수록 +값을 갖습니다

따라서 top은 15, trailing은 -15 값을 주면 해결할 수 있습니다

 

또는 

item과 toItem을 서로 바꾸어 해결할 수 있습니다

 

let trailing = NSLayoutConstraint(item: topView, attribute:.trailing, relatedBy: .equal, toItem: redView, attribute: .trailing, multiplier: 1.0, constant: 15)
cs

 

 

 

Margin 적용

attribute 인자로 .topMargin .leading 등의 속성값을 넣으면 됩니다

redView/blueView에서 topView의 마진 까지로 이해하면 쉽습니다

따라서 redView의 trailing은 item의 속성에 .trailingMargin을 갖고

나머지 속성들은 toItem 속성에 .*Margin값을 갖습니다

 

 

 

let top = NSLayoutConstraint(item: redView, attribute:.top, relatedBy: .equal, toItem: topView, attribute: .topMargin, multiplier: 1.0, constant: 15)
let trailing = NSLayoutConstraint(item: topView, attribute:.trailingMargin, relatedBy: .equal, toItem: redView, attribute: .trailing, multiplier: 1.0, constant: 15)
var width = NSLayoutConstraint(item: redView, attribute: .width, relatedBy: .equal, toItem: nil, attribute:.notAnAttribute, multiplier: 1.0, constant: 100)
var height = NSLayoutConstraint(item: redView, attribute: .height, relatedBy: .equal, toItem: nil, attribute:.notAnAttribute, multiplier: 1.0, constant: 100)
NSLayoutConstraint.activate([top,trailing,width,height])
        
let bottom = NSLayoutConstraint(item: blueView, attribute:.bottom, relatedBy: .equal, toItem: topView, attribute: .bottomMargin, multiplier: 1.0, constant: 0)
let leading = NSLayoutConstraint(item: blueView, attribute:.leading, relatedBy: .equal, toItem: topView, attribute: .leadingMargin, multiplier: 1.0, constant: 0)
width = NSLayoutConstraint(item: blueView, attribute: .width, relatedBy: .equal, toItem: nil, attribute:.notAnAttribute, multiplier: 1.0, constant: 100)
height = NSLayoutConstraint(item: blueView, attribute: .height, relatedBy: .equal, toItem: nil, attribute:.notAnAttribute, multiplier: 1.0, constant: 100)
NSLayoutConstraint.activate([bottom,leading,width,height])
cs

 

 

 

 

Visual Foramt Language

 

정해진 포맷에 따라 비주얼적으로 Constraint를 줄 수 있습니다

 

 

var horzFmt = "[red(==100)]|"
var vertFmt = "V:|[red(==100)]"
        
let views: [String: Any] = ["red":redView, "blue": blueView]
        
var horzConstraints = NSLayoutConstraint.constraints(withVisualFormat: horzFmt, options: [], metrics: nil, views: views)
var vertConstraints = NSLayoutConstraint.constraints(withVisualFormat: vertFmt, options: [], metrics: nil, views: views)
NSLayoutConstraint.activate(horzConstraints + vertConstraints)
        
horzFmt = "|[blue(100)]"
vertFmt = "V:[blue(100)]|"
        
horzConstraints = NSLayoutConstraint.constraints(withVisualFormat: horzFmt, options: [], metrics: nil, views: views)
vertConstraints = NSLayoutConstraint.constraints(withVisualFormat: vertFmt, options: [], metrics: nil, views: views)
NSLayoutConstraint.activate(horzConstraints + vertConstraints)
cs

 

(views딕셔너리 경고는 아까와 마찬가지로 옵셔널타입경고이므로 무시하셔도 됩니다)

 

horzFmt문자열을 해석해 보겠습니다

red : 임의로 정한 redView의 이름입니다 아래 dictionary에 정의한 값과 일치해야 합니다

(==100) : width를 100, equal로 지정합니다  equal일 때는 (100)으로 생략이 가능합니다

오른쪽 |  : 부모뷰의 trailing을 의미합니다

 

vertFmt 의 V: 는 버티컬을 의미하므로 

(100)은 hieght 100,  왼쪽 |는 top을 의미합니다

 

정리하면

"|-[red(100)]" == red뷰.leading을 부모뷰.leading의 마진에 위치하며 width는 100

"[red(150)-20-]" == red뷰.trailing을 부모뷰.leading에서 20떨어지며 width는 150

앞에 V:만 붙이면 세로로 적용할 수 있습니다

 

String과 딕셔너리를 모두 정의 했다면

NSLayoutConstraints의 type메서드를 이용해 적용시킬 수 있습니다

 

 

 

자세한 Visaul Foramt Language는 아래 공식문서를 참고해 주세요

https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/VisualFormatLanguage.html

 

Auto Layout Guide: Visual Format Language

 

developer.apple.com

 

 

 

 

NSLayoutAnchor

 

앵커를 사용해 조작합니다 

 

 

topView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: redView.trailingAnchor).isActive = true
topView.layoutMarginsGuide.topAnchor.constraint(equalTo: redView.topAnchor).isActive = true
        
redView.widthAnchor.constraint(equalToConstant: 100).isActive = true
redView.heightAnchor.constraint(equalToConstant: 100).isActive = true
        
topView.layoutMarginsGuide.leadingAnchor.constraint(equalTo: blueView.leadingAnchor).isActive = true
topView.layoutMarginsGuide.bottomAnchor.constraint(equalTo: blueView.bottomAnchor).isActive = true
        
blueView.widthAnchor.constraint(equalToConstant: 100).isActive = true
blueView.heightAnchor.constraint(equalToConstant: 100).isActive = true
cs

margin을 기준으로 배치한 코드입니다

 

 

margin없이 view를 기준으로 배치하려면 layoutMarginGuide만 없애고 배치하면 됩니다

1212

topView.trailingAnchor.constraint(equalTo: redView.trailingAnchor).isActive = true
topView.topAnchor.constraint(equalTo: redView.topAnchor).isActive = true
        
redView.widthAnchor.constraint(equalToConstant: 100).isActive = true
redView.heightAnchor.constraint(equalToConstant: 100).isActive = true
        
topView.leadingAnchor.constraint(equalTo: blueView.leadingAnchor).isActive = true
topView.bottomAnchor.constraint(equalTo: blueView.bottomAnchor).isActive = true
        
blueView.widthAnchor.constraint(equalToConstant: 100).isActive = true
blueView.heightAnchor.constraint(equalToConstant: 100).isActive = true
cs

 

https://developer.apple.com/documentation/uikit/uiview/1622520-leadinganchor/

https://developer.apple.com/documentation/appkit/nslayoutguide/1534720-leadinganchor/

 

 

참고자료

KxCoding 유튜브 강의

https://youtu.be/u2846BmOGy8

 

728x90

댓글