ForEach
요구에 따라 Identified Data의 기본 Colletion로부터 뷰를 계산하는 Structure
struct ForEach<Data, ID, Content> where Data :
RandomAccessCollection, ID :Hashable
- Data는
RandomAccessCollection을 준수해야한다. - ID는
Hashable을 준수해야한다.
ForEach에서 id를 쓰는 이유
init( _ data: Data, @ViewBuilder content: @escaping (Data.Element) -> Content )
기본 Initailizer에서 data는 Identifiable 프로토콜을 준수해야한다. (각 View들을 식별하기 위해)
하지만, [Int]와 같은 Identifiable을 준수하지 않는 데이터 유형은 data에 넣을 수 없다.
그래서 넣어보면 컴파일 에러가 생긴다. (Cannot convert value of type '[Int]' to expected argument type 'Range’)
이를 해결하기 위해 id가 존재하는 initalizer를 사용한다.
init( _ data: Data, id: KeyPath<Data.Element, ID>, @ViewBuilder content: @escaping (Data.Element) -> Content )
해당 initializer에서는 data가 꼭 Identifieable을 준수할 필요가 없다.
id가 그 역할을 대신하기 때문이다.
ForEach View를 사용할 때 ID로 \.self를 넣어주고는 한다.
struct ContentView: View { let colors: [Color] = [.red, .green, .blue] var body: some View { VStack { ForEach(colors, id: \.self) { color in Text(color.description.capitalized) .padding() .background(color) } } } }
ForEach로 생성되는 각 뷰들이 생성되거나 삭제될 때 그 부분만 업데이트하기위해 ID값을 통해서 뷰를 구별한다.
public init(_ data: Data, id: KeyPath<Data.Element, ID>, @ViewBuilder content: @escaping (Data.Element) -> Content)
ForEach 뷰에 전달하는 id인자는 keyPath로 전달하게 되는데, 참조한 keyPath를 통해
data에서 가져온 값을 unique identifier 로 결정지어 각 뷰가 구별되도록한다.
때문에 위 예시에서 \Color.self 를 생략한 \.self를 전달했으므로 각 뷰의 unique identifier는 차례대로.red, .green, .blue가 된다.
그렇다면, data의 각 요소가 같다면 어떻게 해야할까?
예를들어 colors 배열이 다음과 같은 경우를 말한다.
let colors: [Color] = [.red, .red, .blue]
한 스택 오버플로우 답변 에서는 struct 구조체로 감싸서 Identifiable 프로토콜을 준수하도록 함을 권장한다.
How to allow ForEach layout item to show duplicate item from array in SwiftUI?
다음과 같은 구조체를 생성해서 UUID() 함수를 통해 unique identifier을 생성한다.
struct MyColor: Identifiable { var id = UUID() var color: Color }
다음과 같이 keyPath를 .id로 전달해서 각 view가 unique해지도록 한다.
struct ContentView: View { let colors: [MyColor] = [.init(color: .red), .init(color: .red), .init(color: .blue)] var body: some View { VStack { ForEach(colors, id: \.id) { myColor in Text(myColor.color.description.capitalized) .padding() .background(myColor.color) } } } }