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()
}
}
I think there are two reasons why it is not compiling:
onTapGesture
needs to be passed a function, but you are trying to call a function.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
}