
SwiftUI에서 ForEach는 Array 등 컬렉션 자료형을 순환하며 View를 생성해주는 역할을 합니다.
하지만, 사용법이 다르고 매개변수로 id가 존재하는 등 기존의 forEach와 다른 점이 많아 보입니다.
SwiftUI의 ForEach에 대해서 이해하기 위해서는 먼저 SwiftUI에서 사용하는 ForEach는 기존의 반복문,
즉 Swift의 forEach()과는 비슷하게 작동하지만 본질적으로 다르다는 점을 먼저 알아야 합니다.
공식 문서를 보면,
Apple Developer Documentation
developer.apple.com
ForEach는 '식별된' 데이터들의 컬렉션에 따라, 'View'를 계산하는 구조체입니다.
기존의 swift의 forEach()가 컬렉션 내부의 요소들을 하나씩 돌며 특정 연산을 하는 것에 비하면,
식별된 데이터가 필요하고, View를 계산한다는 특징이 새로운 점입니다ㅎㅎ
어떻게 선언되어 있는지를 살펴보면,
struct ForEach<Data, ID, Content> where Data : RandomAccessCollection, ID : Hashable
다음과 같이, Data와 ID가 필요하네요!
그리고 Data로서 들어올 수 있는 자료형은 반드시 RandomAccessCollection이어야 합니다ㅎㅎ
Apple Developer Documentation
developer.apple.com
여기 들어가서 확인해보면, 만족하는 것들이 어떤 것들인지 알 수 있습니다!
하지만 저희가 사용하는 대부분의 컬렉션 자료형들 (Array, KeyValuePairs 등...)이 만족하고 있기 때문에 크게 신경쓸 필요는 없습니다.
그렇다면, 오늘 알아볼 ID는 무엇일까요?
해당 공식 문서의 개요를 따르면
ForEach는 반드시 "식별할 수 있는" 데이터를 통해 View 계산을 수행할 수 있습니다.
또한 데이터는 이 식별을 위해 반드시 Identifiable 프로토콜을 만족하거나, id 매개변수를 제공해야 한다고 합니다.
먼저 Identifiable 프로토콜을 만족하는 NameFont라는 구조체를 볼까요?
private struct NamedFont: Identifiable {
let name: String
let font: Font
var id: String { name }
}
이 구조체는 id라는 프로퍼티를 반드시 가짐으로서, Identifiable 프로토콜을 만족시키고, 이러한 구조체는 ForEach에서 사용될 수 있습니다.
하지만 우리가 사용하려고 하는 모든 자료형들을 id를 포함한 새로운 자료형으로 만들어낼 필요는 없겠죠?
만약에 그렇게 하려고 한다면, Int 하나를 사용해도
struct IDInt: Identifiable {
let num: Int
var id: String
}
이런 식으로 귀찮은 작업들을 거쳐야 하기 때문입니다... 절대 이렇게 사용할 일은 없을거에요.
자 그래서 ForEach에서는 두번째 방법인 컬렉션 타입의 각 요소에 id를 부여하는 방법을 많이 사용합니다.
이런식으로요!! ㅎㅎ
ForEach(someDataCollection, id: \.self) { data in
...
}
이렇게 하면, ForEach에서 컬렉션 내부의 각각의 data들의 id가 자동으로 정해집니다.
Int나 String 등 모든 Identifiable 프로토콜을 따르지 않는 자료형들도 id를 가지게 해주면서, ForEach문을 돌아갈 수 있게 해주는 거에요!!ㅎㅎ
하지만, 여전히 문제점이 있습니다!
완전히 같은 값이 존재하면, 이 둘은 같은 id 값을 가지기 때문에 문제가 생깁니다.
예를 하나 들어볼까요?
let flags = ["🇰🇷", "🇩🇪", "🇺🇦", "🇺🇸", "🇫🇷", "🇪🇸", "🇯🇵", "🇨🇦", "🇰🇭", "🇮🇶", "🇬🇧", "🇮🇩"]
LazyVGrid(columns: [GridItem(.adaptive(minimum: 80.0))]) {
ForEach(emogis, id: \.self) { emogi in
CardView(content: emogi)
.aspectRatio(2/3, contentMode: .fit)
}
}
이렇게 국기 카드들을 ForEach를 통해서 grid에 정렬해놓았습니다ㅎㅎ

여기서 일본 국기 카드를 한국국기로 바꾸어서, 한국 국기를 두개로 만들면 어떻게 될까요?

이렇게 한국 국기 2개 중 하나가 쏙 빠져버리죠??
두 Data의 값이 완전히 같아서 같은 ID를 가지기 때문에, 이런 문제가 생기는 것입니다.
이 문제를 해결하기 위해서는 Array의 indices를 이용해서 index를 통해서 해결하는 방법이 있습니다!
다음과 같이 값 자체를 아이디로 하는 것 대신, 컬렉션의 인덱스를 id로 해주게 되면,
같은 값이더라도 다른 id를 가질 수 있게 됩니다ㅎㅎ
ForEach(emogis.indices, id: \.self) { idx in
CardView(content: emogis[idx])
.aspectRatio(2/3, contentMode: .fit)
}
이렇게 하면,
이렇게 두개의 태극기가 딱!! 생기는 거죠!!

'iOS 개발 > SwiftUI' 카테고리의 다른 글
[SwiftUI] @escaping에 대해서 (2) | 2022.04.11 |
---|---|
[SwiftUI] inout은 어떻게 작동하는가 (C언어의 Call by Reference와 비교) (0) | 2022.03.21 |
[SwiftUI] Generics 타입 제약 (Type Constraints) 다루기 (0) | 2022.03.20 |
[SwiftUI] json parsing 방법 (0) | 2022.03.10 |
[SwiftUI] View 가독성을 늘리는 3가지 방법 (View 쪼개기) (0) | 2022.03.10 |
[SwiftUI] View protocol과 body 변수에 대한 고찰 (some 키워드를 사용하는 이유) (0) | 2022.02.25 |
[Swift UI] @State에 대한 아주 얕은 고찰 (0) | 2021.11.07 |
[SwiftUI] Swift UI에 대한 첫인상 (2) | 2021.11.07 |