애플 공식문서 링크: Apple Developer Documentation
애플 공식문서에서는 다음과 같이 설명되어 있습니다.
A structure that computes views on demand from an underlying collection of identified data.
-> 식별된 데이터의 기본 컬렉션에서 요청 시, 뷰를 계산하는 데이터구조체입니다.
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public struct ForEach<Data, ID, Content> where Data : RandomAccessCollection, ID : Hashable {
/// The collection of underlying identified data that SwiftUI uses to create
/// views dynamically.
public var data: Data
/// A function to create content on demand using the underlying data.
public var content: (Data.Element) -> Content
}
설명과 정의된 걸 보니, 제가 알던 고차함수의 ForEach과 1:1 매칭이 되는 개념은 아니였네요.
아직까지는 위 정의가 와닿지 않습니다. 좀 더 공식문서를 읽어볼게요.
Use ForEach to provide views based on a RandomAccessCollection of some data type. Either the collection’s elements must conform to Identifiable or you need to provide an id parameter to the ForEach initializer.
-> ForEach를 뷰를 제공할 때, 사용합니다. 그런데 그 뷰는 “RandomAccessCollection”의 데이터 타입인 뷰 입니다. 컬렉션의 구성요소들은 Identifiable
을 준수하고 파라미터로 id값을 반드시 주어야 합니다.
음. 뷰를 만들 때 사용하는구나. 그리고 궁금한 점이 생겼습니다.
“RandomAccessCollection” 이녀석만 알면 좀 이해가 될 것 같습니다.
protocol RandomAccessCollection where Self.Indices : RandomAccessCollection, Self.SubSequence : RandomAccessCollection
음 이친구는 프로토콜이고, 랜덤한 인덱스를 순회하는데 효율적으로 할 수 있도록 도와주는 컬렉션 프로토콜이라고 합니다.
(애플이 잘 만들었으니 가져다 쓰기만 해. 뭐 이런 느낌의 프로토콜)
내용은 이번 글의 주제와 벗어나서 작성하진 않지만, 내용에 흥미로운 내용이 많습니다.
다시 본론으로 돌아와서 ForEach에 대해서 정리해보겠습니다.
ForEach
Identifiable
을 통해서 각각의 Content를 구분해줘야만 사용 가능하다.RandonAccessCollection
이라는 프로토콜을 통해 성능향상이 기대할 수 있다.그러면 애플이 제시한 예시를 한 번 보겠습니다.
private struct NamedFont: Identifiable {
let name: String
let font: Font
var id: String { name }
}
private let namedFonts: [NamedFont] = [
NamedFont(name: "Large Title", font: .largeTitle),
NamedFont(name: "Title", font: .title),
NamedFont(name: "Headline", font: .headline),
NamedFont(name: "Body", font: .body),
NamedFont(name: "Caption", font: .caption)
]
var body: some View {
ForEach(namedFonts) { namedFont in
Text(namedFont.name)
.font(namedFont.font)
}
}
먼저 모델 선언을 보겠습니다.
private struct NamedFont: Identifiable {
let name: String
let font: Font
var id: String { name }
}
Identifiable
을 채택한 구조체입니다. 동시에, id
라는 변수를 설정하고 그 연산프로퍼티는 name을 리턴하도록 했습니다. 이를 통해서 name이라는 상수가 id값으로 활용 가능하겠네요.구조체타입을 바탕으로 해당 타입으로 배열을 선언하여 값을 할당합니다.
private let namedFonts: [NamedFont] = [
NamedFont(name: "Large Title", font: .largeTitle),
NamedFont(name: "Title", font: .title),
NamedFont(name: "Headline", font: .headline),
NamedFont(name: "Body", font: .body),
NamedFont(name: "Caption", font: .caption)
]
마지막으로 실제 UI에는 어떻게 적용되었는지 보겠습니다.
var body: some View {
ForEach(namedFonts) { namedFont in
Text(namedFont.name)
.font(namedFont.font)
}
}
ForEach(값) { 각각의값 in }
이런 형식으로 구성되어 있네요.Identifiable
을 채택하고 있는 구조체이기 떄문에 가능합니다.var body: some View {
ForEach(namedFonts, id: \.self) { namedFont in
Text(namedFont.name)
.font(namedFont.font)
}
}
처음에는 익숙하지 않지만, 이 ForEach 문법 덕분에 SwiftUI 의 많은 코드들이 깔끔해지고 편리하게 작성이 가능하더라구요. (UITableViewDelegate ㅃ2)
UIKit에서 UITableView, UICollectionView를 사용할 때, 주로 ForEach 위 문법이 사용됨을 기억하시면, 언제 사용하시면 될지 감이 점점 잡히실겁니다.^^
읽어주셔서 감사합니다~!