Search code examples
swiftswiftuicollections

Custom generic View fails to conform to View


I am trying to implement a generic View that can display an horizontally scrollable paged list of items, given a collection.

Current state

The non generic version works smoothly, and if i try to abstract it

import SwiftUI

struct PagedListView<Element, Content> where Element: Hashable, Content: View  {
    
    /// The array of items to distribute along the various pages of the list
    var data: Array<Element>
    
    var itemsPerPage: Int
    
    /// Build the view for a single item of a page
    public var content: (Element) -> Content
    
    private var numPages: Int {
        return Int(ceil(Double(data.count) / Double(itemsPerPage)))
    }
    
    private func rangeForPage(_ page: Int) -> Range<Int> {
        return (page*itemsPerPage)..<min(data.count, page*itemsPerPage+itemsPerPage)
    }
    
    var body: some View {
        TabView {
            ForEach(0..<numPages, id: \.self) { pageNum in 
                page(pageNum)
            }
            .padding(.bottom, 30)
        }
        .tabViewStyle(.page)
    }
    
    @ViewBuilder
    private func page(_ page: Int) -> some View {
        VStack(alignment: .leading, spacing: 0) {

            // TODO: pass a keypath to the view, to define the id instead of \.self, like List
            ForEach(data[rangeForPage(page)], id: \.self) { itemData in
                content(itemData)
            }
            Spacer().padding(0)
        }
        .frame(maxWidth: .infinity, alignment: .leading)
    }
}

The Problem

This compiles, but usage

struct UsageView: View {
    var body: some View {
        VStack { // *** COMPILE ERROR HERE ***
            PagedListView(data: [0,1,2,3,4,5,6,7], itemsPerPage: 3) { item in
                Text("Item: \(item)")
            }
        }
    }
}

triggers the following error

Static method 'buildBlock' requires that 'PagedListView<Int, Text>' conform to 'View'

on the containing View, I can't grasp what is wrong.

I am fairly sure it has to to with the generic constraitns on protocols but i can't go by exclusions or it does not compile at all How can i understand what the issue is here?


Solution

  • The error message is describing exactly what is wrong: PagedListView doesn't conform to the protocol View. Just conform to it in your declaration:

    struct PagedListView<Element, Content>: View where Element: Hashable, Content: View  {
    

    Notice that we added :View before the where clause, to ensure that the PagedListView conforms to the protocol View.