FlexLayout은 부모 컨테이너인 "Flex Container" 와 하위 요소인 "Flex Items"로 구성된다.
Flex Item 또한 Flex Container가 될 수 있으며, 다른 Flex Item을 추가할 수 있다.
FlexLayout의 핵심 특징은 Item들의 width와 height를 조정하여 공간을 최대한 활용하는 것 이다.
StackView를 사용할 때 두 가지 축(Axes), 즉 주축과 그에 수직인 교차축에 대해 생각해야 한다.
주축은 StackView의 direction이며, 아이템들이 배열되는 주요 방향을 나타낸다.
이런 축 개념을 사용해 FlexLayout을 설계한다.
(Column: 세로, row: 가로)
Applies To: Flex Container
Return: Flex
Flex Contianer에 새로운 Flex Item을 추가한다.
새롭게 추가된 Flex Item의 FlexLayout Interface를 Return 받는다.
내부적으로 Flex Item에 UIView를 만들어 추가해서 flexBox가 가능하도록 만든다.
view.flex.addItem(imageView).width(100).aspectRatio(1)
View에 width가 100이고, 종횡비가 1:1로 height 100이 자동으록 계산된 imageView를 추가한다.
Apples To: Flex Container
Parameter: (flex: Flex) -> Void 클로저 타입
Return: Flex
flexBox 구조를 따르는 코드 구조를 만들기 위해 사용된다.
파라미터인 클로저를 사용해서 View의 Flex Interface를 조작할 수 있다.
Flex Interface를 사용해 다른 Flex Container, Flex Item을 추가할 수 있으며, Flex 구조에 따라 코드를 구성할 수 있다.
view.flex.addItem().define { (flex) in
flex.addItem(imageView).grow(1)
flex.addItem().direction(.row).define { (flex) in
flex.addItem(titleLabel).grow(1)
flex.addItem(priceLabel)
}
}
define을 안써도 되긴하는데, 그럼 코드가 더럽다.
let columnContainer = UIView()
columnContainer.flex.addItem(imageView).grow(1)
view.flex.addItem(columnContainer)
let rowContainer = UIView()
rowContainer.flex.direction(.row)
rowContainer.flex.addItem(titleLabel).grow(1)
rowContainer.flex.addItem(priceLabel)
columnContainer.flex.addItem(rowContainer)
flex.view 프로퍼티를 사용해 flex Item의 View에 접근할 수 있다.
주로 flex.define 메서드의 클로저에서 사용된다.
view.flex.direction(.row).padding(20).alignItems(.center).define { (flex) in
flex.addItem().width(50).height(50).define { (flex) in
flex.view?.alpha = 0.8
}
}
아니면 직접 추가 가능.
view.flex.direction(.row).padding(20).alignItems(.center).define { (flex) in
let container = UIView()
container.alpha = 0.8
flex.addItem(container).width(50).height(50)
}
Apples To: Flex Container
Values: FitContainer, adjustWidth, adjustHeight
Default: FitContainer
layoutSubViews()에서 Flex Container를 레이아웃 한 다음 Flex Container의 자식들을 layout 시키기 위해 사용된다.
adjustHeight, adjustWidth는 자식들에 의해 Container의 크기가 맞게 조정되고,
FitContainer는 Container의 크기는 유지되고 자식들이 그 안에서 배치되게끔 한다.
rootFlexContainer.flex.layout(mode: .adjustHeight)
Apples to: Flex Container
Values: Column(위->아래) / ColumnReverse(아래->위) / row(왼->오) / rowReverse(오->왼)
주 축(main-axis)를 설정하여 이 Container안의 Flex Item들이 배치되는 방향을 정의합니다.
주의할점
row와 row-reverse가 Flex Container의 layoutDirection 속성에 영향을 받는다.
view.flex.direction(.row)
view.flex.direction(.column)
flex.view?.backgroundColor = .purple
flex.addItem(view1).width(100%).aspectRatio(2.0)
flex.addItem(view2).width(100%).aspectRatio(2.0)
flex.addItem(view3).width(100%).aspectRatio(2.0)
}
Child들을 width(100%), AspectRatio(2.0), JustifyContent를 맥여놨을 때,
layout mode에 따라
FitContainer
AdjustHeight
AdjustWidth
Apples to: flex container
Values: start, end, center, spacebetween, spaceAround, spaceEvenly
현재 FlexContainer의 주축(max-axis, direction 속성)에서 주축을 따라 정렬을 정의합니다.
이 속성은 한 줄에 있는 모든 Flex Item들이 최대 크기에 도달했을 때 남은 여분의 공간을 어떻게 분배할지 정하는데 도움이 됩니다.
헷갈리는 spaceAround, spaceEvenly는 이 글에 있는 그림으로 대체 한다.
Applies to: Flex Container
Values: stretch, start, end, center, baseline
Flex Item들이 교차 축(axis)에 따라 어떻게 배치되는지를 정의한다.
justifyContent()은 메인 축, alignItems()는 교차 축을 따라 정렬되는지가 제어된다.
stretch 값을 이용해서
세로 축 정렬일 때 가로로 꽉 채우고,
가로 축 정렬일 때 세로로 꽉 채울 수 있다.
rootFlexContainerView.flex.direction(.column).alignItems(.stretch).define { (flex) in
flex.view?.backgroundColor = .purple
flex.addItem(view1).height(10%)
flex.addItem(view2).height(10%)
flex.addItem(view3).height(10%)
}
stretch 일 때
stretch로 꽉 늘려서 표기해줌
Applies to: flex Container
value: auto/stretch/start/end/center/baseline
자식 요소가 교차 축에서 어떻게 정렬되는지를 제어하며, 부모 요소의 alignItems를 무시한다.
부모 요소의 alignItems를 무시하고 자식 요소의 개별적인 정렬을 지정할 수 있도록 한다.
노란색 박스 안에 초록, 갈색 박스가 들어있는 형태.
rootFlexContainerView.flex.direction(.column).alignItems(.stretch).define { (flex) in
flex.view?.backgroundColor = .purple
flex.addItem(view1).direction(.column).height(20%).define { (flex) in
let view = UIView()
view.backgroundColor = .green
let view2 = UIView()
view2.backgroundColor = .brown
flex.addItem(view).height(30%)//.aspectRatio(1.0)
flex.addItem(view2).height(30%)//.aspectRatio(1.0)
}
flex.addItem(view2).height(20%)
flex.addItem(view3).height(20%)
}
여기서 노란색 박스 안의 초록, 갈색만 stretch가 아닌 가로 세로를 갖고 center 정렬 시키고 싶음.
노란색 박스 안의 초록, 갈색에 alignSelf(.center)를 넣어준다.
rootFlexContainerView.flex.direction(.column).alignItems(.stretch).define { (flex) in
flex.view?.backgroundColor = .purple
flex.addItem(view1).direction(.column).height(20%).define { (flex) in
let view = UIView()
view.backgroundColor = .green
let view2 = UIView()
view2.backgroundColor = .brown
flex.addItem(view).alignSelf(.center).height(30%).aspectRatio(1.0)
flex.addItem(view2).alignSelf(.center).height(30%).aspectRatio(1.0)
}
flex.addItem(view2).height(20%)
flex.addItem(view3).height(20%)
}
Applies to: Flex Container
Values: noWrap, wrap, wrapReverse
Wrap 속성은 Flex Item들의 총 넓이가 Flex Container 보다 클 때,
한 줄로 표시되는지 여러 줄로 표시되는지 결정한다.
기본적으로 Flex Container는 모든 Flex Item들을 한 줄에 맞추려 한다.
따라서 들어있는 Flex Item들의 넓이는 Flex Container의 넓이에 맞게 자동 축소된다.
이 속성으로 너비가 더 클 때 어떻게 정렬하는지 정의할 수 있다.
Applies to: Flex Container
Values: start, end, center, stretch, spaceBetween, spaceAround
교차축의 justifyContent라고 이해하자.
alignContent 속성은 교차축에서 여분의 공간이 있는 경우 flex Container의 줄들을 컨테이너 내에서 정렬한다.
이는 justifyContent가 main-axis에서 개별 항목들을 정렬하는 방식과 유사하다.
alignContent는 flexBox가 한 줄만 가지고 있는 경우에는 영향을 주지 않는다. 즉, 여러 줄이 있는 경우에만 alignContent 속성이 적용된다.
rootFlexContainerView.flex.direction(.column).wrap(.wrap).alignContent(.center).define { (flex) in
flex.view?.backgroundColor = .purple
flex.addItem(view1).height(300.0).width(100.0)
flex.addItem(view2).height(300.0).width(100.0)
flex.addItem(view3).height(300.0).width(100.0)
}
rootFlexContainerView.flex.direction(.column).wrap(.wrap).alignContent(.spaceBetween).define { (flex) in
flex.view?.backgroundColor = .purple
flex.addItem(view1).height(300.0).width(100.0)
flex.addItem(view2).height(300.0).width(100.0)
flex.addItem(view3).height(300.0).width(100.0)
}
둘 다 Flex Container의 교차 축에서 Flex Item들을 정렬하는 데 사용된다.
차이점은
alignItems는 Flex Container의 각 줄에 있는 Flex Item들을 교차축을 따라 정렬하는데 사용한다.
즉, alignItems의 요소에 따라 Flex Item들이 교차 축 방향으로 동일한 위치에 정렬되거나, 시작점이나 끝점에 정렬되거나, 가운데에 정렬된다.
alignContent는 Flex Container의 모든 줄들을 교차축에 따라 정렬하는데 사용된다. 여러 줄이 있는 경우에 사용하며, Flex Container 내에서의 줄들 사이의 간격과 위치를 조정하는데 사용된다.
즉,
alignItems
alignContent
FlexLayout은 LTR, RTL 언어를 지원한다.
start, end속성을 사용하여 뷰를 위치시킬 때, 사용자의 언어에 따라 화면의 왼쪽, 오른쪽에 아이템이 표시되는지 고려할 필요가 없다.
flex Container 또한 flex Item이 될 수 있으며, 밑에 기술된 프로퍼티도 flex Container에 적용할 수 있다.
Applies to: Flex Items
Default: 0
Flex Item이 필요한 경우 확장할 수 있는 능력을 정의한다.
flex Container 내에서 이 Item이 차지해야 하는 사용가능한 공간의 양을 지시한다.
모든 아이템에 grow값을 1로 설정하면 모든 아이템들은 컨테이너 내에서 동일한 크기로 설정된다.
하나의 아이템에 값을 2로 설정한다면, 해당 아이템은 다른 아이템들 보다 2배의 공간을 차지하게 된다.
rootFlexContainerView.flex.direction(.column).wrap(.wrap).alignItems(.start).alignContent(.spaceBetween).define { (flex) in
flex.view?.backgroundColor = .purple
flex.addItem(view1).height(300.0).width(100)
flex.addItem(view2).height(300.0).width(100)
flex.addItem(view3).height(300.0).width(100)
}
여기서 노란색 Flex Item에 grow(1)을 적용하면!
노란색 Flex Item이 사용 가능한 공간대로 늘어난다.
Apples to: Flex Item
Default: 0
shrink 속성은 main-axis에서 공간이 부족할 때, Flex Container 내의 다른 Flex Item들에 비해 해당 Flex Item이 얼마나 줄어들지를 결정한다.
shrink 값은 음의 공간을 분배할 때 flex 기준 값(flex basis)과 곱해진다.
shrink 값이 0인 경우 뷰의 크기는 main-axis 방향으로 유지되며 이로 인해 뷰가 flex Container를 넘어서는 현상이 발생할 수 있습니다.
이 때 View들에 shrink 속성을 잘 부여해주면..
rootFlexContainerView.flex.direction(.column).alignItems(.start).define { (flex) in
flex.view?.backgroundColor = .purple
flex.addItem(view1).height(300.0).width(100).shrink(10.0)
flex.addItem(view2).height(300.0).width(100).shrink(1.0)
flex.addItem(view3).height(300.0).width(100).shrink(1.0)
}
shrink 값은 비율입니다.
한 아이템의 shrink 값이 10이고 다른 아이템들의 shrink 값이 1이라면, 10인 아이템은 나머지 아이템들보다 10배 더 줄어들게 된다.
Applies to: flex Item
Default: 0
flex Item의 초기 크기를 지정한다.
grow, shrink 요소에 따라 여분의 공간이 분배되기 전의 크기다.
Nil을 지정하면 basis가 auto로 설정되며, 만약 Item에 길이가 지정되지 않은 경우, width는 그 Item의 content에 따라 결정된다.
basis( :CGFloat?)
basis( :FPercent)
Applies to: flex Items
특정 UIView를 레이아웃에서 제외하거나 포함할 수 있는 기능을 제공한다.
일반적으로, UIView.flex 속성에 처음 접근하거나 additem() 메서드를 사용하여 자식 View를 flex 컨테이너에 추가할 때 해당 UIView는 자동으로 레이아웃에 포함되는데, 이 속성을 사용해서 동적으로 레이아웃에서 제외시킬 수도 있다.
isHidden은 뷰가 숨겨지긴 하지만, 레이아웃에 영향을 주지 않으며, 해당 뷰의 영역은 다른 뷰에 의해 채워지지 않는다.
override func viewDidLoad() {
super.viewDidLoad()
// Flex container 생성
flexContainer = UIView()
view.addSubview(flexContainer)
// Flex item 생성
flexItem = UIView()
flexContainer.addSubview(flexItem)
// FlexLayout 설정
flexContainer.flex.direction(.row).justifyContent(.center).alignItems(.center)
// 데이터를 비동기로 받아오는 작업 수행
fetchData()
}
func fetchData() {
// 데이터를 비동기로 받아오는 예시
// 네트워크 요청, 데이터베이스 쿼리 등
// 데이터를 받아왔을 때 처리
// 이 예시에서는 데이터가 있는 경우 flexItem을 표시하고, 없는 경우 flexItem을 레이아웃에서 제외합니다.
let hasData = true // 데이터가 있는지 여부에 따라서 값을 설정합니다.
flexItem.isIncludedInLayout = hasData
}
Applies to: flex Items
none으로 두면 hidden되고 layout에 표기되지도 않는다.
그럼 이걸...isIncludedInLayout대신 쓰면 되는거아닌지..?
Applies to: flex Items
flex Item의 레이아웃을 변경하고자 할 때 사용된다.
Flexlayout은 flex 속성(Property)이 변경되거나 flex Container 사이즈가 변경될 때만 레이아웃됩니다.
그러나 특정한 상황에서 FlexLayout에게 특정 Flex Item의 레이아웃을 강제로 다시 계산하도록 하려면, markDirty() 메서드를 사용해 해당 아이템을 '더티' 상태로 표기해야 합니다.
더티 플래그는 flexBox 트리의 루트 까지 전파되어, 하나의 아이템이 invalidate되면 해당 아이템의 전체 하위 트리가 다시 레이아웃되게 업데이트 합니다.
예시.
UILabel의 text가 업데이트 됐을 때 label의 더티 플래그를 켜준다.
// 1) Update UILabel's text
label.text = "I love FlexLayout"
// 2) Mark the UILabel as dirty
label.flex.markDirty()
// 3) Then force a relayout of the flex container. 같이 쓸 필요는 없는데 이 예시에서 이렇게 한 것 같음
rootFlexContainer.flex.layout()
OR
setNeedsLayout()
Applies to: flex Items
지정된 frame size에서 레이아웃된 사이즈를 반환한다.
let layoutSize = viewA.flex.sizeThatFits(size: CGSize(width: 200, height: CGFloat.greatestFiniteMagnitude))
height는 그냥 큰 값으로 주었을 때 프레임 크기를 추정할 수 있습니다.
🐸 CGFloat.greatesFiniteMagnitude
CGFloat이 표현할 수 있는 가장 큰 유한한 수. 일반적으로 무한대, 아주 큰 값 의미.
주로 레이아웃 계산이나 크기 조정 같은 작업에서 사용된다.
sizeThatFits() 메서드에서 아이템의 크기를 추정할 때, 가능한 크기 범위를 제한하지 않고 아이템이 자유롭게 크기를 조정할 수 있도록 하기 위해 사용될 수 있다.
Applies to: Flex Item
뷰 자체의 속성만 고려하여 계산된 아이템의 크기.
아이템의 프레임과는 독립적으로 계산된다.
FlexLayout에서도 View의 intrinsicSize를 사용해서 뷰의 컨텐츠을 적절히 맞추는 레이아웃을 제공할 수 있다.
direction()
justifyContent()
alignItems()
alignSelf()
wrap()
alignContent()
layoutDirection()
grow()
shrink()
basis()
isIncludeInLayout()
display()
markDirty()
sizeThatFits()
intrinsicSize