List #1
List는 여러 데이터를 각각의 열로써 수직으로 배치하는 View다.
애플의 모든 플랫폼을 지원하며, 각각에 알맞게 표시한다.
SwiftUI의 List는 UIKit의 TableView와 같은 기능을 한다.
StaticList
struct StaticList: View {
var body: some View {
List {
HStack {
Text("Hello, World!")
Text("Hello, World!")
}
Text("Hello, World!")
Image(systemName: "star")
Toggle(isOn: .constant(true)) {
Text("On")
}
}
}
}
List에 Embed 된 View를 각각의 개별 Cell로 구성한다.
DynamicList
struct DynamicList: View {
var items = Product.sampleList
var body: some View {
VStack {
List(items, id: \.name) { item in
Text(item.name)
}
}
}
}
struct Product {
let name: String
let summary: String
let category: String
let price: Int
}
extension Product {
static var sampleList: [Product] {
return [
Product(name: "MacBook Air",
summary: """
Apple M1 칩(8코어 CPU, 8코어 GPU, 16코어 Neural Engine)
8GB 통합 메모리
512GB SSD 저장 장치¹
True Tone이 탑재된 Retina 디스플레이
Magic Keyboard
Touch ID
Force Touch 트랙패드
Thunderbolt/USB 4 포트 2개
""",
category: "Mac",
price: 1_630_000),
.
.
.
Product(name: "Apple Watch Series 7",
summary: """
45mm
상시표시형 Retina 디스플레이
알루미늄 / 스텐인리스 스틸 / 티타늄
GPS + Cellular
""",
category: "Apple Watch",
price: 659_000)
]
}
}
DynamicList을 구현할 때는 전달되는 데이터가 'identifiable' 프로토콜을 채용한 상태여야 한다.
그렇지 아니한 경우 List의 id 파라미터에 이를 대신할 구분자를 전달해 줘야 한다.
위의 코드에서는 sampleList의 name이 그 역할을 한다.
IdentifiableList
struct DynamicIdentifiableList: View {
var items = AppleProduct.sampleList
var body: some View {
VStack {
List(items) { item in
Text(item.name)
}
}
}
}
struct AppleProduct: Identifiable, Hashable {
let id = UUID()
let name: String
let summary: String
let category: String
let price: Int
}
extension AppleProduct {
static var sampleList: [AppleProduct] {
return [
AppleProduct(name: "MacBook Air",
summary: """
Apple M1 칩(8코어 CPU, 8코어 GPU, 16코어 Neural Engine)
8GB 통합 메모리
512GB SSD 저장 장치¹
True Tone이 탑재된 Retina 디스플레이
Magic Keyboard
Touch ID
Force Touch 트랙패드
Thunderbolt/USB 4 포트 2개
""",
category: "Mac",
price: 1_630_000),
.
.
.
AppleProduct(name: "Apple Watch Series 7",
summary: """
45mm
상시표시형 Retina 디스플레이
알루미늄 / 스텐인리스 스틸 / 티타늄
GPS + Cellular
""",
category: "Apple Watch",
price: 659_000)
]
}
}
데이터 구조체에 id 속성을 추가하고, UUID를 저장한다.
구조체는 identifiable, hashable 프로토콜을 채용해야 한다.
이 경우 identifiable 프로토콜을 인식하고, id 속성이 그 역할을 자동으로 하기 때문에,
List의 파라미터에 id를 따로 지정하지 않아도 된다.
Section
Content 외에 Header와 Footer를 추가할 수 있다.
StaticList
struct SectionedList: View {
var items = CategorizedProduct.sampleList
var body: some View {
VStack {
List {
Section {
Text("1")
Text("2")
}
Section {
Text("3")
Text("4")
Text("5")
} header: {
Text("Header")
} footer: {
Text("Footer")
}
}
}
}
}
List 내에서 Section에 Embed 해 사용하고,
각각의 Section은 header와 footer를 할당할 수 있다.
header는 대문자로 표시된다는 특징이 있다.
DynamicList
struct SectionedList: View {
var items = CategorizedProduct.sampleList
var body: some View {
VStack {
List {
ForEach(items) { section in
Section {
ForEach(section.list) { item in
Text(item.name)
}
} header: {
Text(section.header)
} footer: {
if let footer = section.footer {
Text(footer)
}
}
}
}
}
}
}
DynamicList는 ForEach를 사용한다.
Foreach는 전달된 데이터를 열거하는 View로 DynamicList와 GridView에 자주 사용된다.
전달된 itmes의 section 대로 분리하고,
section에 포함된 list의 name 속성으로 데이터를 표시한다.
struct CategorizedProduct: Identifiable, Hashable {
let id = UUID()
let header: String
let footer: String?
let list: [AppleProduct]
}
extension CategorizedProduct {
static var sampleList: [CategorizedProduct] {
return [
CategorizedProduct(header: "iPhone",
footer: "Lorem Ipsum",
list: AppleProduct.sampleList.filter { $0.category == "iPhone" }),
CategorizedProduct(header: "iPad",
footer: nil,
list: AppleProduct.sampleList.filter { $0.category == "iPad" }),
CategorizedProduct(header: "Mac",
footer: nil,
list: AppleProduct.sampleList.filter { $0.category == "Mac" }),
CategorizedProduct(header: "Apple Watch",
footer: nil,
list: AppleProduct.sampleList.filter { $0.category == "Apple Watch" })
]
}
}
Section 구성을 위해 ForEach에 전달된 데이터의 구조는 위와 같다.
동적 할당을 위한 id속성과 Sectio의 구분자 역할을 할 header와 footer가 존재하며,
해당 Section에 표시될 데이터가 배열로 저장된다.
결과는 위와 같다.
Customizing
List는 배경색, 테두리 색, 여백뿐만 아니라,
기본 디자인 까지도 변경할 수 있다.
ListStyle
- .insetGroup
List의 기본 스타일이다.
- .grouped
가장 큰 특징을 각 모서리의 여백과 라운드 처리가 사라졌다는 것이다.
- .plain
가장 기교 없는 평면적인 디자인이다.
Section의 구분도 header나 footer가 없다면 선의 굵기로만 미세하게 구분된다.
- .inset
현행 버전 기준 plain과 동일한 ui를 보여준다.
- .sidebar
sidebar 형태의 List 디자인이다.
mac과 iPad 등에서 왼쪽에 표시되며, section을 접었다 펴는 기능이 기본으로 제공된다.
- .automatic
플랫폼에 어울리는 형태로 자동으로 적용된다.
Cell 여백
listRowInsets
struct CustomizingList: View {
var body: some View {
VStack {
List {
Section() {
Text("Hello, List!")
.listRowInsets(.init(top: 0, leading: 100, bottom: 0, trailing: 0))
Text("List Row Insets")
Text("List Row Background")
Text("List Row Separator")
Text("List Row Separator Tint")
} header: {
Text("first header")
}
.listRowInsets(.init(top: 0, leading: 60, bottom: 0, trailing: 0))
Section() {
Text("One")
Text("Two")
}header: {
Text("second header")
}
Section() {
Text("Custom Header")
}
}
.listStyle(.automatic)
}
}
}
List의 cell에 표시되는 모든 View 혹은 Section 전체에 여백을 설정할 수 있다.
첫 번째 Section 전체에 leading 60의 여백이 적용된 상태에서,
첫 번째 Cell에 해당하는 TextView에는 leading 100의 여백이 적용된 것을 볼 수 있다.
이를 통해 Section과 Cell에 중복으로 inset이 설정된 경우 Cell의 inset이 우선권이 높음을 확인할 수 있다.
Cell 배경
listRowBackground
struct CustomizingList: View {
var body: some View {
VStack {
List {
Section() {
Text("Hello, List!")
.listRowInsets(.init(top: 0, leading: 30, bottom: 0, trailing: 0))
Text("List Row Insets")
Text("List Row Background")
Text("List Row Separator")
Text("List Row Separator Tint")
} header: {
Text("first header")
}
.listRowInsets(.init(top: 0, leading: 60, bottom: 0, trailing: 0))
Section() {
Text("One")
.listRowBackground(Color(.cyan))
Text("Two")
}header: {
Text("second header")
}
.listRowBackground(Color(.gray))
Section() {
Text("Custom Header")
}
}
.listStyle(.automatic)
}
}
}
List 혹은 Cell의 배경색을 변경할 수 있다.
listRowInsets와 마찬가지로 Cell과 Section에 동시에 존재할 경우 Cell의 Background가 우선 적용된다.
Cell 테두리
listRowSeperator
struct CustomizingList: View {
var body: some View {
VStack {
List {
Section() {
Text("Hello, List!")
.listRowInsets(.init(top: 0, leading: 30, bottom: 0, trailing: 0))
Text("List Row Insets")
Text("List Row Background")
Text("List Row Separator")
.listRowSeparator(.hidden)
Text("List Row Separator Tint")
} header: {
Text("first header")
}
.listRowInsets(.init(top: 0, leading: 60, bottom: 0, trailing: 0))
Section() {
Text("One")
.listRowBackground(Color(.cyan))
Text("Two")
}header: {
Text("second header")
}
.listRowBackground(Color(.gray))
Section() {
Text("Custom Header")
}
}
.listStyle(.automatic)
}
}
}
Cell 간의 구분을 담당하는 separator를 숨기거나 보이게 조절할 수 있다.
또한 edges 파라미터로 상, 하 둘 중 하나를 지정하는 것도 가능하다.
사진에서는 첫 번째 Section의 네 번째 Cell의 separator를 모두 없앤 결과이다.
Cell 테두리 색
listRowSeperatorTint
struct CustomizingList: View {
var body: some View {
VStack {
List {
Section() {
Text("Hello, List!")
.listRowInsets(.init(top: 0, leading: 30, bottom: 0, trailing: 0))
Text("List Row Insets")
Text("List Row Background")
Text("List Row Separator")
.listRowSeparator(.hidden)
Text("List Row Separator Tint")
} header: {
Text("first header")
}
.listRowInsets(.init(top: 0, leading: 60, bottom: 0, trailing: 0))
.listRowSeparatorTint(Color(.systemBlue))
Section() {
Text("One")
.listRowBackground(Color(.cyan))
Text("Two")
}header: {
Text("second header")
}
.listRowBackground(Color(.gray))
Section() {
Text("Custom Header")
}
}
.listStyle(.automatic)
}
}
}
Cell 구분 선의 색을 변경할 수 있다.
첫 번째 Section의 Cell 구분 선이 파란색으로 변경된 것을 확인할 수 있다.
CustomHeader
SectionHeader는 별도로 구성한 View를 표시하는 것이 가능하다.
struct CustomHeaderView: View {
let title: String
let imageName: String
var body: some View {
Label(title, systemImage: imageName)
.font(.title)
.frame(minHeight: 60)
}
}
Header를 Label로 구현해 60 포인트의 높이로 표시한다.
struct CustomizingList: View {
var body: some View {
VStack {
List {
Section() {
Text("Hello, List!")
.listRowInsets(.init(top: 0, leading: 30, bottom: 0, trailing: 0))
Text("List Row Insets")
Text("List Row Background")
Text("List Row Separator")
.listRowSeparator(.hidden)
Text("List Row Separator Tint")
} header: {
Text("first header")
}
.listRowInsets(.init(top: 0, leading: 60, bottom: 0, trailing: 0))
.listRowSeparatorTint(Color(.systemBlue))
Section() {
Text("One")
.listRowBackground(Color(.cyan))
Text("Two")
}header: {
Text("second header")
}
.listRowBackground(Color(.gray))
Section() {
Text("Custom Header")
} header: {
CustomHeaderView(title: "Star", imageName: "star")
}
}
.listStyle(.automatic)
}
}
}
CustomHeaderView를 호출하면서 알맞은 파라미터를 함께 전달하면 해당 View가 Header에 표시된다.
'학습 노트 > Swift UI (2022)' 카테고리의 다른 글
28. ForEach & Grid (0) | 2022.11.09 |
---|---|
27. List #2 (0) | 2022.11.09 |
25. StateObject (0) | 2022.11.02 |
24. Observable Object & Environment Object (0) | 2022.11.01 |
23. State & Binding (0) | 2022.10.27 |