
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 |