사실상 단순한 UI라서 큰차이를 느끼지 못하실껍니다. 하지만, UI의 복잡도가 올라가거나 기존 UI에 새로운 UI 컴포넌트를 추가한다고 가정해봅시다.
Xib나 Storyboard의 경우에는 기존 contraints관계를 파악한후 constraints를 끊어 낸다던가 추가에 따른 constraints를 추가해야 합니다.
constraints관계는 top, bottom, left, right, leading, trailing 및 inset, margin값 그리고 constraint priority 등을 의미합니다.
또한 이런 변화요소에 대해서 코드 검증도 받아야하지만 가독성이 좋지는 않습니다.
Autolayout을 코드로 작성했을 경우는 Xib 및 Storyboard에 비해 모듈화하기도 쉽고 체계적으로 쓸 수는 있으나 변화요소에 대한 추가작업에 대해서는 여전히 UI 컴포넌트 간의 constraints관계를 파악해야합니다.
하지만 Texture Layout API의 경우는 다른점이 다양한 LayoutSpec이라는 것이 제공됩니다. 선언적이고 명시적인 LayoutSpec을 사용함으로서 자신 또는 다른 개발자입장에서 봤을 어떠한 형태로 레이아웃이 설계되었는지 추론하기 용이할 뿐더러, 새로운 컴포넌트를 추가하는데 있어서도 영향을 받는 LayoutSpec에서 코드를 수정하면 되기 때문에 contraints관계 파악하는 거보다 상대적으로 빠르고 쉽습니다.
좀 더 이해를 돕기위해서 username과 description아래에 informationLabel을 추가한다고 가정해봅시다.
Xib 및 Storyboard의 경우
Xib 및 Storyboard 파일을 엽니다. (파일을 열고 난후 랜더링하는데 무거워서 사양에 따라서 여는데 시간이 다소 걸립니다.)
constraints관계를 파악합니다.
IBOutlet을 연결하고 constraints를 추가합니다.
코드리뷰 아니 xib 변경사항에 대해서 리뷰를 받습니다. (방법은 다양합니다. snapshot 또는 동료개발자가 직접열어 본다던가 앱에서 실행해서 검증하는 방법 etc.. )
Autolayout의 경우
constraints를 다루는 method를 찾습니다.
코드상 constraints관계를 파악합니다.
constraints추가 및 수정합니다.
동료로 부터 코드리뷰를 받습니다. (동료 역시 리뷰시 constraints관계를 파악해야합니다. )
final class RepositoryView: UIView {
let profileView = UIImageView()
let usernameLabel = UILabel()
let descLabel = UILabel()
let informationLabel = UILabel()
// ... 생략 ...
func makeRepositoryConstraints() {
usernameLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: ...)
usernameLabel.bottomAnchor.constraint(equalTo: ..., constant: ...)
usernameLabel.leadingAnchor.constraint(equalTo: ..., constant: ...)
usernameLabel.trailingAnchor.constraint(equalTo: ..., constant: ...)
descLabel.topAnchor.constraint(equalTo: ..., constant: ...)
descLabel.bottomAnchor.constraint(equalTo: ..., constant: ...)
descLabel.leadingAnchor.constraint(equalTo: ..., constant: ...)
descLabel.trailingAnchor.constraint(equalTo: ..., constant: ...)
// 1. constraints관계 파악
// 2. constraints 추가 및 수정
}
func snapKitBaseMakeRepositoryConstraints() {
profileView.snp.makeConstraints({
$0.top.bottom.leading.equalToSuperview().inset(5.0)
$0.trailing.equalTo(usernameLabel.snp.leading).inset(5.0)
$0.trailing.equalTo(descLabel.snp.leading).inset(5.0)
})
usernameLabel.snp.makeConstraints({
// 생략.
})
descLabel.snp.makeConstraints({
// 생략.
})
// 1. constraints관계 파악
// 2. constraints 추가 및 수
}
}
Texture Layout API의 경우
해당 UI의 layoutSpecThatsFit method로 갑니다.
수정해야하는 LayoutSpec 추적 및 파악합니다.
UI Components 추가합니다.
코드리뷰를 받습니다.
final class RepositoryNode: ASDisplayNode {
let profileNode = ASNetworkImageNode()
let usernameNode = ASTextNode()
let descNode = ASTextNode()
let infomationNode = ASTextNode()
// ... 생략 ...
func repositoryLayoutSpec() -> ASLayoutSpec {
// 1. 수정해야하는 LayoutSpec 추적 및 파악
let infoLayout =
ASStackLayoutSpec(
direction: .vertical,
spacing: 5.0,
justifyContent: .start,
alignItems: .stretch,
children: [
self.usernameNode,
self.descNode,
self.infomationNode
]
)
// 2. 컴포넌트 추가,ASStackLayoutSpec -> children -> infomationNode 추가.
let profileWithInfoLayout =
ASStackLayoutSpec(
direction: .horizontal,
spacing: 5.0,
justifyContent: .start,
alignItems: .stretch,
children: [
self.profileNode,
self.infoLayout
]
)
return ASInsetLayoutSpec(
insets: UIEdgeInsets,
child: profileWithInfoLayout
)
}
}
Texture랑 auto-layout의 퍼포먼스에 대한 수치적으로 비교한 자료는 없습니다.
하지만, Autolayout 에 비해 frame 기반으로 설정된 레이아웃이 퍼포먼스가 우수한건 사실입니다.
Texture가 Auto-Layout으로 설계된 레이아웃보다 랜더링하는데 있어서 퍼포먼스가 빠른 이유는 다음과 같습니다.
복잡한 레이아웃 설계를 Main-Thread가 아닌 Background Thread에서 처리하고 frame을 계산합니다.
Auto-Layout을 사용하지않고 Auto-Layout처럼 Main Thread를 방해하지 않습니다.
UICollectionView 및 UITableView의 prefetch 및 reuse cell을 하지 않고 Intelligent preloading 및 pending layout 을 사용하여 Reuse cell에서 일어나는 많은 버그와 Frame Drop을 줄임과 동시에 우수한 사용자 경험성을 제공합니다.
Image처리 및 Attributed Text Encoding에 있어서 최적화가 잘되어있습니다.
__
LayoutSpec 설계시 Tip 및 유의사항
1. 명시적으로 LayoutSpec을 설계합니다.
입문자의 경우 layoutSpec을 설계할 때 layoutSpecThatFits에 모든 레이아웃 설계사항을 다 넣는 경우가 있습니다. 협업 및 생산성 향상을 위해서 LayoutSpec 설계사항에 대해서 메서드를 분리하고 명시적으로 선언해주는 것이 좋습니다.