Skip to content

Instantly share code, notes, and snippets.

@avii-7
Created January 23, 2026 06:44
Show Gist options
  • Select an option

  • Save avii-7/aecf078a350d2eee36ff9279a37f5354 to your computer and use it in GitHub Desktop.

Select an option

Save avii-7/aecf078a350d2eee36ff9279a37f5354 to your computer and use it in GitHub Desktop.
LegendView just like HTML Legend and FieldSet
//
// LegentView.swift
// DifferentSmallTests
//
// Created by Arun's Macbook on 22/01/26.
//
// Support only one line for Legend Text right now.
import SwiftUI
private struct LegendShape: Shape {
private struct ShapePoints {
let x1: CGFloat // Representing left x starting point
let x2: CGFloat // Representing right x starting point
let midY: CGFloat // Will be used to move border downwards so that text looks vertically center
}
let text: String
let font: UIFont
let horizontalSpacing: CGFloat
let cornerRadius: CGFloat
func path(in rect: CGRect) -> Path {
var path = Path()
let shapePoints = getLine(rect: rect)
path.move(to: CGPoint(x: shapePoints.x1, y: shapePoints.midY))
path.addLine(to: CGPoint(x: rect.minX + cornerRadius, y: shapePoints.midY))
path.addQuadCurve(
to: CGPoint(
x: rect.minX,
y: shapePoints.midY + cornerRadius
),
control: CGPoint(x: rect.minX , y: shapePoints.midY)
)
path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY - cornerRadius))
path.addQuadCurve(
to: CGPoint(
x: rect.minX + cornerRadius,
y: rect.maxY
),
control: CGPoint(x: rect.minX , y: rect.maxY)
)
path.addLine(to: CGPoint(x: rect.maxX - cornerRadius, y: rect.maxY))
path.addQuadCurve(
to: CGPoint(
x: rect.maxX,
y: rect.maxY - cornerRadius
),
control: CGPoint(x: rect.maxX , y: rect.maxY)
)
path.addLine(to: CGPoint(x: rect.maxX, y: shapePoints.midY + cornerRadius))
path.addQuadCurve(
to: CGPoint(
x: rect.maxX - cornerRadius,
y: shapePoints.midY
),
control: CGPoint(x: rect.maxX , y: shapePoints.midY)
)
path.addLine(to: CGPoint(x: shapePoints.x2, y: shapePoints.midY))
return path
}
private func getLine(rect: CGRect) -> ShapePoints {
let rectSize = rect.size
let textSize = text.size(with: rectSize, font: font)
var x = (rectSize.width - textSize.width) / 2 - horizontalSpacing
var x1 = x + horizontalSpacing + textSize.width + horizontalSpacing
let minimumWidth = 5.0
x = max(x , rect.minX + cornerRadius + minimumWidth)
x1 = min(x1, rect.maxX - cornerRadius - minimumWidth)
return ShapePoints(
x1: x,
x2: x1,
midY: textSize.height / 2
)
}
}
struct LegentView<LegendText: View, Content: View>: View {
struct Properties {
let text: String
let font: UIFont
var horizontalSpacing: CGFloat = 10
var cornerRadius: CGFloat = 20
var lineWidth: CGFloat = 2.0
}
let properties: Properties
var configure: (Text) -> LegendText
let innerContent: () -> Content
@State private var size: CGSize = .zero
var body: some View {
ZStack(alignment: .top) {
LegendShape(
text: properties.text,
font: properties.font,
horizontalSpacing: properties.horizontalSpacing,
cornerRadius: properties.cornerRadius
)
.stroke(style: StrokeStyle(lineWidth: properties.lineWidth, lineCap: .round, lineJoin: .round))
.frame(height: size.height + properties.text.size(font: properties.font).height)
VStack(spacing: .zero) {
configure(
Text(properties.text)
.font(Font(properties.font))
)
innerContent()
.padding(.vertical, 4)
.onGeometryChange(for: CGSize.self) { proxy in
proxy.size
} action: { newValue in
self.size = newValue
}
}
}
}
}
#Preview {
LegentView(
properties: .init(
text: "Premium Benefits",
font: .boldSystemFont(ofSize: 20),
horizontalSpacing: 10, cornerRadius: 20
), configure: { text in
text.foregroundStyle(.black)
.lineLimit(1)
},
innerContent: {
VStack {
Text("You are awesome buddy 😄")
.font(.caption)
Divider()
HStack {
Text("Get 10% off on all purchases")
.font(.caption)
Image(systemName: "star")
}
.padding(.horizontal)
Divider()
HStack {
Text("Get 10% off on all purchases")
.font(.caption)
Image(systemName: "star")
}
.padding(.horizontal)
}
})
.padding()
}
extension String {
func size(with rectSize: CGSize, font: UIFont) -> CGSize {
let constraintRect = rectSize
let boundingBox = self.boundingRect(
with: constraintRect,
options: [.usesLineFragmentOrigin, .usesFontLeading],
attributes: [.font: font],
context: nil
)
let size = CGSize(width: ceil(boundingBox.width), height: ceil(boundingBox.height))
return size
}
func size(font: UIFont) -> CGSize {
let constraintRect = CGSize(
width: Double.greatestFiniteMagnitude,
height: Double.greatestFiniteMagnitude
)
let boundingBox = self.boundingRect(
with: constraintRect,
options: [.usesLineFragmentOrigin, .usesFontLeading],
attributes: [.font: font],
context: nil
)
let size = CGSize(width: ceil(boundingBox.width), height: ceil(boundingBox.height))
return size
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment