List
에 대해 알아보도록 합시다.
List
List
는 단일 열에 정렬 된 데이터 행을 표시하는 컨테이너입니다.
struct ContentView: View {
var body: some View {
List {
Text("첫번째 리스트")
Text("두번째 리스트")
Text("세번째 리스트")
}
}
}
정적 콘텐츠
List
의 생성자에 원하는 뷰를 전달하면 하나씩 각 row
에 담아 표현합니다. 여기서 뷰 하나는 row
하나에 해당합니다.
UIKit에서는 UITableView에서 내용을 표시할 뷰를 셀이라고 불렀다면, SwiftUI에서는 row
라고 표현합니다.
다음과 같이 텍스트가 아닌 다른 이미지 뷰를 넣어 봐도 동일하게 List
를 작성 할 수 있습니다.
List {
Text("List")
Image("seogun").resizable().scaledToFit().frame(width: 100, height: 100)
Rectangle().frame(width: 100, height: 100)
Circle().frame(width: 100, height: 100).foregroundColor(.yellow)
}
동적 콘텐츠
Range <Int>
동적 콘텐츠를 표현하는 첫번째 방법은 Range<Int
> 타입의 값을 넘겨주는 것입니다. 예를들어 1
부터 100
까지의 숫자를 출력하려면 다음과 같이 작성 하면 됩니다.
List(o..<100) {
Text("\($0)")
}
RandomAccessCollection
두 번째 방법은 RandomAccessCollecion
프로토콜을 준수하는 데이터를 제공하는 것입니다. 이경우에는 데이터의 각 요소들을 구분하고 식별할 수 있도록 반드시 다음 2
가지 방법 중 하나를 선택하여 id
값을 제공해야만 합니다.
첫 번째, id 식별자 지정
첫 번째는 id
로 사용할 값을 직접 인수로 제공하는 것입니다. SwifUI
에서는 이것을 간단히 self
라고 입력할 수도 있습니다.
.List(["A", "B", "C", "D"], id: \.self) { ... }
두 번째, identifiable 프로토콜 채택
두 번째 방법은 매개 변수에 id
를 전달하는 대신 데이터 타입 자체에 identifiable
프로토콜을 채택하는 것입니다. 타입 자체에 id
프로퍼티를 만들고 이것을 식별자로 삼게 됩니다,
우선 Ghibli
라는 struct
를 만들고 Identifiable
을 매개변수 타입으로 넣어준 후,
원하는 상수와 식별가능한 코드 id = UUID()
를 작성합니다.
UUID?? 유형, 인터페이스 및 기타 항목을 식별하는 데 사용할 수 있는 보편적으로 고유한 값입니다.
struct Ghibli: Identifiable {
let name: String
let id = UUID()
}
private var ghibli = [
Ghibli(name: "포뇨"),
Ghibli(name: "소스케"),
Ghibli(name: "소피아"),
Ghibli(name: "센"),
Ghibli(name: "하쿠"),
Ghibli(name: "토토로")
]
이제 ContentView
에 List
와 위에 작성한 배열을 넣어줘야 합니다.
struct ContentView: View {
var body: some View {
List(ghibli) {
Text($0.name).font(.system(size: 20))
}
}
}
이처럼 identifiale
프로토콜을 준수한다면, 이미 식별자가 있으므로 리스트에 id
를 제공하지 않아도 무방합니다.
정적 콘텐츠와 동적 콘텐츠 조합
ForEach
를 이용하면 이 두 가지를 조합하는 것도 가능합니다.
ForEach
SwiftUI
에서 ForEach
는 List
처럼 id
로 식별할 수 있는 데이터를 받아서 동적으로 뷰를 생성하는 역할을 합니다. 전달받은 매개변수도 RandomAccessCollection
이나 Range<Int
> 타입을 사용한다는 점도 같습니다. 그래서 아래 코드는 같은 결과를 보여주게 됩니다.
List {
ForEach(0..<30) {
Text("\($0)")
}
}
List(1..<30) {
Text("\($0)")
}
하지만 리스트에서는 정적인 뷰도 포함할 수 있기 때문에, ForEach
와 함께 사용하면 정적 + 동적 콘텐츠를 조합할 수 있는 것입니다.
List {
Text("목록") // 하나의 Row를 차지하는 정적 뷰
ForEach(0..<30) { // 30개의 동적 뷰 생성
Text("\($0)")
}
}
조합
이제 동적과 정적 콘텐츠를 조합한 예제를 살펴보도록 하겠습니다.
struct ContentView: View {
let drink = ["스프라이트", "콜라", "환타", "오렌지주스"]
let snack = ["프링글스", "엄마손파이", "포카칩"]
var body: some View {
List {
Text("음료수")
ForEach(drink, id: \.self) {
Text("\($0)")
}
Label("과자", systemImage: "star.fill").font(.largeTitle)
ForEach(snack, id: \.self) {
Text("\($0)")
}
}
}
}
Section
섹션에는 header
와 footer
를 생략하거나 추가할 수 있고, 둘 중 하나만 사용할 수 있습니다.
struct ContentView: View {
let drink = ["스프라이트", "콜라", "환타", "오렌지주스"]
let snack = ["프링글스", "엄마손파이", "포카칩"]
var body: some View {
let titles = ["음료수", "과자"] // 분류 제목
let data = [drink, snack] //위에 정의한 목록 데이터
return List {
ForEach(data.indices) { index in // data에 포함된 횟수만큼 섹션 생성
Section(header: Text(titles[index]), footer: HStack { Spacer(); Text("\(data[index].count)건")}
) {
ForEach(data[index], id: \.self) {
Label($0, systemImage: "leaf.fill")
// Text($0)
}
}
}
}
// .listStyle(GroupedListStyle())
}
}
ListStyle
ListStyle
을 사용하기위해서는 List { ... }
/ .listStyle()
처럼 리스트 밖에 스타일을 추가해줘야 합니다.
List { ... }
.listStyle(DefaultListStyle())
ListStyle 종류
사실 ListStyle
의 종류는 다양하게 많이 있습니다. 일단 IOS
에서 사용 가능한 스타일종류를 보자면 6가지 정도 됩니다.
DefaultListStyle()
- 기본 리스트 스타일GroupedListStyle()
- 각 섹션을 분리된 그룹으로 묶어 표현하는 스타일InsetGroupedListStyle()
PlainListStyle()
- 데이터 목록을 각 행마다 하나씩 나열하는 형태의 기본 스타일InsetListStyle()
SidebarListStyle()
스타일을 확인 하기 위해 일단 코드를 작성해주도록 하겠습니다.
struct TaskRow: View {
var body: some View {
Text("Hello, World!")
}
}
struct ContentView: View {
var body: some View {
List {
Section(header: Text("Header"), footer: Text("footer"), content: {
TaskRow()
TaskRow()
TaskRow()
})
Section {
TaskRow()
TaskRow()
TaskRow()
}
}
//listStyle을 정해줌
.listStyle(DefaultListStyle())
}
}
스타일 스크린샷을 확인해보면 Default
와 Plain
은 똑같은 화면인 것을 확인 할 수 있는데,
-
Default
는 플랫폼의 기본적인 동작 /appearance
인ListStyle
.-
Plain
은 그냥plain list
의 동작 /appearance
인ListStyle
.
insetListStyle
도 똑같아 보이지만 Default
/Plain
에 비해 Header
부분 Leading space
가 더 큽니다.
Sidebar
리스트 스타일은 보시다시피 섹션을 접었다 펼 수 있는데, footer
부분은 사라지는것을 확인 할 수 있습니다.
onDelete / onMove
List
내부에있는 텍스트를 옮기거나 삭제할 수 도 있습니다. 이것을 사용하려면 .onDelete(perform: )
와 .onMove(perform: )
를 사용하고 함수를 정의해줘야 합니다.
onDelete 코드
func removeList(at offsets: IndexSet) {
users.remove(atOffsets: offsets)
}
onMove 코드
func moveList(from source: IndexSet, to destination: Int) {
users.move(fromOffsets: source, toOffset: destination)
}
위 코드를 사용하기 위해서는 List
를 NavigationView
로 감싸고, 그 옆에 EditButton을 추가해줘야 합니다. 이 버튼이 없으면 Text
를 슬라이스 해서 삭제는 할 수 있지만 Move
는 할 수 없습니다.
.toolbar { EditButton() }
.navigationBarItems(trailing: EditButton())
struct ContentView: View {
@State private var users = ["포뇨", "소스케", "서근"]
var body: some View {
NavigationView {
List {
ForEach(users, id: \.self) { user in
Text(user)
}
.onDelete(perform: removeList)
.onMove(perform: moveList)
}
.navigationTitle("학생")
.toolbar { EditButton() }
}
}
//함수구현부
func removeList(at offsets: IndexSet) {
users.remove(atOffsets: offsets)
}
func moveList(from source: IndexSet, to destination: Int) {
users.move(fromOffsets: source, toOffset: destination)
}
}
Selection in Lists
목록 데이터의 Identifiable
단일 인스턴스에 바인딩합니다.Id
유형은 단일 선택 목록을 생성합니다. Set
에 바인딩하면 여러 선택 항목을 지원하는 목록이 생성됩니다.
다음 예제 에서는 위 예제에 다중 선택을 추가하는 방법을 사용하려고 합니다. 목록 아래의 Text view
에는 현재 선택된 항목 수가 표시됩니다.
첫번째, @State
변수를 추가해 줍니다.
struct ContentView: View {
@State private var multiSelection = Set<UUID>()
var body: some View {
}
}
두번째, NavigationView
를 생성 후 List
에 multiSelection
을 추가해줍니다.
List(ghibli, selection: $multiSelection)
세번째, NavigationBarTitle
을 이용하여 이름을 설정해주고, 그 옆에 .toolbar { EditButton() }
를 추가해 선택 가능한 버튼을 만들어 줍니다.
NavigationView {
List(ghibli, selection: $multiSelection) {
Text($0.name).font(.system(size: 20))
}
.navigationBarTitle("Studio Ghibli")
.toolbar { EditButton() }
}
네번째, 화면 하단에 몇개가 선택 되었는지 표시해줍니다.
var body: some View {
NavigationView { ... }
Text("\(multiSelection.count)selections")
}
전체 코드
import SwiftUI
struct Ghibli: Identifiable {
let name: String
let id = UUID()
}
private var ghibli = [
Ghibli(name: "포뇨"),
Ghibli(name: "소스케"),
Ghibli(name: "소피아"),
Ghibli(name: "센"),
Ghibli(name: "하쿠"),
Ghibli(name: "토토로")
]
struct ContentView: View {
@State private var multiSelection = Set<UUID>()
var body: some View {
NavigationView {
List(ghibli, selection: $multiSelection) {
Text($0.name).font(.system(size: 20))
}
.navigationBarTitle("Studio Ghibli")
.toolbar { EditButton() }
}
Text("\(multiSelection.count) selections")
}
}
리스트의 여백 설정 listRowInsets
list
에는 기본적으로 padding
이 들어가있습니다. 이미지를 적용시켜보면 더 쉽게 볼 수 있습니다. 이 여백을 설정하고 싶으면 listRowInsets
를 사용하면 됩니다.
.listRowInsets(EdgeInsets.init())
///
.listRowInsets(EdgeInsets.init(top: CGFloat, leading: CGFloat, bottom: CGFloat, trailing: CGFloat))
예시와 함께 확인해보도록 하겠습니다.
우선 Mycard
라는 파일을 만들어서 아래와 같이 코드를 작성해주겠습니다.
//MyCard
struct MyCard: View {
var MyImage: String
var MyText: String
var Houre: Int
var Min: Int
var BgColor: Color
var body: some View {
HStack {
Image(systemName: "\(MyImage)")
.font(.system(size: 50))
.foregroundColor(.white)
.frame(width: 90)
.padding()
VStack(alignment: .leading, spacing: 0){
Divider().opacity(0)
Text("\(MyText)")
.font(.title)
.fontWeight(.bold)
.foregroundColor(.white)
.padding(.bottom, 10)
.lineLimit(1)
Text("예상 소요 시간 : \(Houre)시간 \(Min)분")
.font(.footnote)
.foregroundColor(.white)
}
}
.frame(height: 100, alignment: .center)
.background(BgColor)
}
}
그리고 MyList
라는 새로운 파일을 생성해서 위 이미지를 호출합니다.
struct MyList: View {
var body: some View {
List {
Section(header: Text("오늘 할일")) {
ForEach(1...5, id: \.self) { index in
MyCard(MyImage: "scribble.variable", MyText: "SwiftUI : Text \(index)", Houre: 1, Min: 20 , BgColor: Color(#colorLiteral(red: 0.1960784346, green: 0.3411764801, blue: 0.1019607857, alpha: 1)))
}
}
//.listRowInsets(EdgeInsets.init())
.listRowInsets(EdgeInsets.init(top: 10, leading: 10, bottom: 10, trailing: 10))
}
.listStyle(GroupedListStyle())
}
}
위 코드에서 .listRowInsets(EdgeInsets.init())
가 있을 때 와 없을 때를 비교 해 볼 수 있습니다.
읽어주셔서 감사합니다🤟
'SWIFTUI > View layout' 카테고리의 다른 글
SwiftUI : FixedSize - View의 크기를 동일한 너비/높이로 (1) | 2021.03.14 |
---|---|
SwiftUI : DisclosureGroup (Toggle / Slider) (0) | 2021.03.10 |
SwiftUI : TabView / TabViewStyle (0) | 2021.03.07 |
SwiftUI : Lottie Animation(애니메이션) (1) | 2021.02.10 |
SwiftUI : List를 이용해서 Grid형식 만들기 (0) | 2021.02.09 |