|
public struct WrappableTagsView<Content: View>: View { |
|
private let tags: [TagsModel] |
|
private let color: Color |
|
private let item: (TagsModel, Color) -> Content |
|
|
|
@State private var totalHeight = CGFloat.zero |
|
|
|
public init( |
|
tags: [TagsModel], |
|
color: Color, |
|
item: @escaping (TagsModel, Color) -> Content |
|
) { |
|
self.tags = tags |
|
self.color = color |
|
self.item = item |
|
} |
|
|
|
public var body: some View { |
|
GeometryReader { geometry in |
|
generateContent(in: geometry, for: tags, color: color) |
|
.background(viewHeightReader($totalHeight)) |
|
} |
|
.frame(height: totalHeight) |
|
} |
|
|
|
private func generateContent(in geo: GeometryProxy, for tags: [TagsModel], color: Color) -> some View { |
|
var width = CGFloat.zero |
|
var height = CGFloat.zero |
|
|
|
/// moving tags to a new line when they do not fit on the screen |
|
return ZStack(alignment: .topLeading) { |
|
ForEach(tags, id: \.id) { tag in |
|
self.item(tag, color) |
|
.padding([.horizontal, .vertical], 4) |
|
.fixedSize(horizontal: false, vertical: true) |
|
.linelimit(1) |
|
.alignmentGuide(.leading) { dimension in |
|
if abs(width - dimension.width) > geo.size.width { |
|
width = 0 |
|
height -= dimension.height |
|
} |
|
let result = width |
|
if tag.id == tags.last?.id { |
|
width = 0 |
|
} else { |
|
width -= dimension.width |
|
} |
|
return result |
|
} |
|
.alignmentGuide(.top) { _ in |
|
let result = height |
|
if tag.id == tags.last?.id { |
|
height = 0 |
|
} |
|
return result |
|
} |
|
} |
|
} |
|
} |
|
|
|
private func viewHeightReader(_ binding: Binding<CGFloat>) -> some View { |
|
GeometryReader { geometry -> Color in |
|
let rect = geometry.frame(in: .local) |
|
DispatchQueue.main.async { |
|
binding.wrappedValue = rect.size.height |
|
} |
|
return .clear |
|
} |
|
} |
|
} |
Description
The WrapTagsView component is designed to display a series of tags that wrap onto new lines when they exceed the width of their container. This component is particularly useful in user interfaces where a list of tags, labels, or categories needs to be presented in a space-efficient and visually appealing manner.
Usage
How to use the
WrappableTagsView
component in a SwiftUI application: