SwiftUI Picker で拡張機能をビルドしようとしていますが、コンパイルしようとするとコンテンツ クロージャ内で次のエラーが発生します。
コードは次のとおりです。
extension Picker where SelectionValue: Identifiable, Label == Text {
init<Data>(_ title: String, selection: Binding<SelectionValue>, items: Data, @ViewBuilder itemContent: @escaping (Data.Element) -> some View) where Data: RandomAccessCollection, Data.Element == SelectionValue {
self.init(title, selection: selection, content: {
ForEach(items) { item in
itemContent(item).tag(item)
}
})
}
}
ForEach は View に準拠する必要があるため、Content type の要件を満たす必要があります。
コンテンツを何にも制約していないため、init はコンテンツ タイプ パラメーターが任意のビューであるピッカーを作成できることが期待されています。ただし、init で作成したピッカーのコンテンツ タイプは ForEach<…> です。
これは、コンテンツに制約を追加する必要があることを意味します。
// note that you should also parameterise this with ItemContent
// you can't use (Data.Element) -> some View anymore, because you need the return type
// of that to constrain Content
init<Data, ItemContent>(
_ title: String,
selection: Binding<SelectionValue>,
items: Data,
@ViewBuilder itemContent: @escaping (Data.Element) -> ItemContent
) where
Data: RandomAccessCollection,
Data.Element == SelectionValue,
Content == ??? // <=== here
では、ForEach(items) { … } の型は何でしょうか? .tag 修飾子が返す型がわからないため、実際にはわかりません。
これを回避する簡単な方法の 1 つは、この ForEach をラップする独自のビューを作成することです。
struct PickerContentHelper<Data, Content>: View
where Data: RandomAccessCollection,
Data.Element: Hashable,
Data.Element: Identifiable,
Content: View
{
let items: Data
let itemContent: (Data.Element) -> Content
init(_ items: Data, @ViewBuilder itemContent: @escaping (Data.Element) -> Content) {
self.items = items
self.itemContent = itemContent
}
var body: some View {
ForEach(items) { item in
itemContent(item).tag(item)
}
}
}
次に、Content を PickerContentHelper<Data, ItemsContent> に制限できます。
extension Picker where SelectionValue: Identifiable, Label == Text {
init<Data, ItemContent>(
_ title: String,
selection: Binding<SelectionValue>,
items: Data,
@ViewBuilder itemContent: @escaping (Data.Element) -> ItemContent
) where
Data: RandomAccessCollection,
Data.Element == SelectionValue,
Content == PickerContentHelper<Data, ItemContent>
{
self.init(title, selection: selection) {
PickerContentHelper(items, itemContent: itemContent)
}
}
}
これは、他の多くの SwiftUI ビューで見られる一般的なパターンです。これらの「ヘルパー ビュー」の多くは、「サポートするタイプ」セクションにあります。たとえば、ShareLink には DefaultShareLinkLabel があり、init(item:subject:message:) などの初期化子は、Label タイプ パラメーターをこのタイプに制約します。