Search code examples
swiftuicompiler-errorsswiftui-foreach

SwiftUI Error: Conflicting arguments to generic parameter 'Content' ('<<hole>>' vs. '<<hole>>' vs. '<<hole>>')


I added the onTapGesture modifier to the custom view in the ForEach loop to get the view that was tapped. Before adding this decorator, the application can render normally. But once the decorator is added this strange error occurs.

Conflicting arguments to generic parameter 'Content' ('<<hole>>' vs. '<<hole>>' vs. '<<hole>>' vs. '<<hole>>' vs. '<<hole>>' vs. '<<hole>>' vs. '<<hole>>' vs. '<<hole>>')

Can anyone tell me how this error occurs and how to fix it.

I googled some related articles, but I found that while their error content was similar to mine, the code that generated the error was different from mine, such as this one.

Here's my MRE code:

The error occurred here: // ERROR POSITION .onTapGesture(perform: vm.selectTag(tag))

import SwiftUI
import Foundation

struct ContentView: View {
    @StateObject var vm = TagEditViewModel()
    
    @State var visionTags: [Tag] = []
    
    @State var showAddNewTagLibrarySheet: Bool = false
    
    var body: some View {
        GeometryReader { geometryReader in
            VStack(alignment: .leading) {
                VStack(alignment: .leading) {
                    if (vm.groupedSelectedTags.count == 0) {
                        Text("Select Tag")
                            .foregroundColor(Color(UIColor.lightGray))
                    } else {
                        ForEach(vm.groupedSelectedTags, id: \.self) { tags in
                            HStack {
                                ForEach(tags) { tag in
                                    TagItem(title: tag.title )
                                }
                            }
                        }
                    }
                }
                .padding()
                .frame(width: geometryReader.size.width, alignment: .leading)
                .background(
                    RoundedRectangle(cornerRadius: 10.0)
                        .fill(Color(UIColor.systemBackground))
                )
                VStack(alignment: .leading) {
                    ForEach(vm.groupedAllAvailableTags, id: \.self) { tags in
                        HStack {
                            ForEach(tags) { tag in
                                TagLibraryItem(title: tag.title )
                                    // ERROR POSITION .onTapGesture(perform: vm.selectTag(tag))
                            }
                        }
                    }
                    Button(action: {
                        showAddNewTagLibrarySheet.toggle()
                    }, label: {
                        HStack {
                            Image(systemName: "plus")
                            Text("New Tag")
                        }
                    })
                    .font(.callout)
                    .foregroundColor(Color.black)
                    .padding()
                    .frame(height: /*@START_MENU_TOKEN@*/24.0/*@END_MENU_TOKEN@*/)
                    .background(Color.white)
                    .frame(alignment: .center)
                    .cornerRadius(100)
                }
                .padding(.vertical)
                .frame(width: geometryReader.size.width, alignment: .leading)
                .background(
                    RoundedRectangle(cornerRadius: 10)
                        .fill(Color(UIColor.secondarySystemBackground))
                )
            }
            .background(
                Color(UIColor.secondarySystemBackground)
            )
            .onAppear {
                let tag1 = Tag(id: UUID(), title: "Tag1")
                let tag2 = Tag(id: UUID(), title: "Tag2")
                let tag3 = Tag(id: UUID(), title: "Tag3")
                let tag4 = Tag(id: UUID(), title: "Tag4")
                visionTags.append(tag1)
                visionTags.append(tag2)
                visionTags.append(tag3)
                visionTags.append(tag4)
                vm.calculateTagGroup(of: visionTags, withContainerWidth: geometryReader.size.width)
            }
        }
    }
}

struct Tag: Hashable, Identifiable {
    var id: UUID
    var title: String
}

struct TagLibrary: Hashable, Identifiable {
    var id: UUID
    var title: String
}

struct TagItem: View {
    @State var title = ""
        
    var body: some View {
        Text(title)
            .modifier(TagStyle())
    }
}

struct TagStyle: ViewModifier {
    func body(content: Content) -> some View {
        content
            .font(.callout)
            .foregroundColor(Color.black)
            .padding()
            .frame(height: /*@START_MENU_TOKEN@*/24.0/*@END_MENU_TOKEN@*/)
            .background(Color.gray)
            .frame(alignment: .center)
            .cornerRadius(100)
    }
}

struct TagLibraryItem: View {
    @State var title = ""
        
    var body: some View {
        Text(title)
            .modifier(TagStyle())
    }
}

class TagEditViewModel: ObservableObject {
    /**
     Selected tags
     */
    @Published var groupedSelectedTags: [[Tag]] = []
    
    /**
     All avaliable tags
     */
    @Published var groupedAllAvailableTags: [[TagLibrary]] = []
    
    
    func calculateTagGroup(of tags: [Tag], withContainerWidth containerWidth: CGFloat) {
        
        // width of taga of a row
        var width: CGFloat = 0
        
        // a group of tag array, each element is tag array which represent tags in a row
        var tagArrayGroup: [[Tag]] = []
        
        // tags in a row
        var tagsOfLine = [Tag]()
        
        for tag in tags {
            // calculate a width of tag
            let labelWidth = calculateTagWidth(of: tag.title )
            
            // if tag width plus width of tags that already in row plus 32 is less than container width, append the tag to this row. otherwise append the tag into the next row
            if (width + labelWidth + 32) < containerWidth {
                width += labelWidth
                tagsOfLine.append(tag)
            } else {
                width = labelWidth
                tagArrayGroup.append(tagsOfLine)
                tagsOfLine.removeAll()
                tagsOfLine.append(tag)
            }
        }
        
        if tags.count > 0 {
            tagArrayGroup.append(tagsOfLine)
        }
        
        groupedSelectedTags = tagArrayGroup
        
        print("groupedSelectedTags.count = \(groupedSelectedTags.count)")
        
        calculateTagLibraryGroup(withContainerWidth: containerWidth)
    }
    
    private func calculateTagWidth(of title: String) -> CGFloat {
        if (title.count == 0) {
            return 0
        }
        
        let label = UILabel()
        label.text = title
        label.sizeToFit()
        // 32 is for the padding on horizontal
        return label.frame.size.width + 32
    }
    
    func calculateTagLibraryGroup(withContainerWidth containerWidth: CGFloat) {
        do {
            var tagLibrarys: [TagLibrary] = []
            
            let tagLib1 = TagLibrary(id: UUID(), title: "Tag1")
            let tagLib2 = TagLibrary(id: UUID(), title: "Tag2")
            let tagLib3 = TagLibrary(id: UUID(), title: "Tag3")
            let tagLib4 = TagLibrary(id: UUID(), title: "Tag4")
            let tagLib5 = TagLibrary(id: UUID(), title: "Tag5")
            let tagLib6 = TagLibrary(id: UUID(), title: "Tag6")
            let tagLib7 = TagLibrary(id: UUID(), title: "Tag7")
            let tagLib8 = TagLibrary(id: UUID(), title: "Tag8")
            let tagLib9 = TagLibrary(id: UUID(), title: "Tag9")
            
            tagLibrarys.append(tagLib1)
            tagLibrarys.append(tagLib2)
            tagLibrarys.append(tagLib3)
            tagLibrarys.append(tagLib4)
            tagLibrarys.append(tagLib5)
            tagLibrarys.append(tagLib6)
            tagLibrarys.append(tagLib7)
            tagLibrarys.append(tagLib8)
            tagLibrarys.append(tagLib9)
            
            var width: CGFloat = 0
            
            var tagArrayGroup: [[TagLibrary]] = []
            
            var tagsOfLine = [TagLibrary]()
            
            for tag in tagLibrarys {
                
                let label = UILabel()
                label.text = tag.title
                label.sizeToFit()
                let labelWidth = label.frame.size.width + 32
                
                if (width + labelWidth + 32) < containerWidth {
                    width += labelWidth
                    tagsOfLine.append(tag)
                } else {
                    width = labelWidth
                    tagArrayGroup.append(tagsOfLine)
                    tagsOfLine.removeAll()
                    tagsOfLine.append(tag)
                }
            }
            tagArrayGroup.append(tagsOfLine)
            
            groupedAllAvailableTags = tagArrayGroup
        }
    }
    
    func selectTag(_ selectedTag: Tag) {
        
    }
}

struct TagEditView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Solution

  • I think there are two reasons why it is not compiling:

    1. The onTapGesture needs to be passed a function, but you are trying to call a function.
    2. The function you are trying to call is TagEditViewModel.selectTag which takes a Tag as parameter, but you are trying to pass a TagLibrary (because the ForEach is iterating over an array of TagLibrary).

    With these changes it compiles:

    ForEach(vm.groupedAllAvailableTags, id: \.self) { tags in
        HStack {
            ForEach(tags) { tag in
                TagLibraryItem(title: tag.title )
                    .onTapGesture { vm.selectTag(tag) }
            }
        }
    }
    
    func selectTag(_ selectedTag: TagLibrary) { // TagLibrary instead of Tag
    
    }