Last active
September 28, 2025 02:20
-
-
Save kylebshr/27d272d3bb86e07053bf06f39a4de6f4 to your computer and use it in GitHub Desktop.
SwiftUI Shape that will be a capsule up to a maximum corner radius. Useful for capsule UI that should adapt to a rounded rect past a certain scale.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // AdaptiveCapsule.swift | |
| // Design | |
| // | |
| // Created by Kyle Bashour on 8/21/25. | |
| // | |
| import SwiftUI | |
| /// Will draw as a capsule up to a maximum corner radius, after which it draws as a rounded rect. | |
| public struct AdaptiveCapsule: InsettableShape { | |
| public var maximumCornerRadius: CGFloat? | |
| public var style: RoundedCornerStyle | |
| private var insetAmount: CGFloat = 0 | |
| public init(maximumCornerRadius: CGFloat? = nil, style: RoundedCornerStyle = .continuous) { | |
| self.maximumCornerRadius = maximumCornerRadius | |
| self.style = style | |
| } | |
| public func path(in rect: CGRect) -> Path { | |
| let insetRect = rect.insetBy(dx: insetAmount, dy: insetAmount) | |
| var cornerRadius = min(insetRect.width, insetRect.height) / 2 | |
| if let maximumCornerRadius { | |
| let insetMaximumCornerRadius = maximumCornerRadius - insetAmount | |
| cornerRadius = min(insetMaximumCornerRadius, cornerRadius) | |
| } | |
| return Path(roundedRect: insetRect, cornerRadius: cornerRadius, style: style) | |
| } | |
| public func inset(by amount: CGFloat) -> AdaptiveCapsule { | |
| var copy = self | |
| copy.insetAmount += amount | |
| return copy | |
| } | |
| } | |
| #Preview { | |
| VStack { | |
| ZStack { | |
| RoundedRectangle(cornerRadius: 40) | |
| RoundedRectangle(cornerRadius: 40) | |
| .inset(by: 20) | |
| Text("RoundedRectangle") | |
| .foregroundStyle(.black) | |
| } | |
| ZStack { | |
| AdaptiveCapsule(maximumCornerRadius: 40) | |
| AdaptiveCapsule(maximumCornerRadius: 40) | |
| .inset(by: 20) | |
| Text("Matches RoundedRectangle Insetting") | |
| .foregroundStyle(.black) | |
| } | |
| ZStack { | |
| AdaptiveCapsule(maximumCornerRadius: nil) | |
| AdaptiveCapsule(maximumCornerRadius: nil) | |
| .inset(by: 20) | |
| Text("Capsule Behavior") | |
| .foregroundStyle(.black) | |
| } | |
| VStack { | |
| HStack { | |
| AdaptiveCapsule(maximumCornerRadius: nil) | |
| AdaptiveCapsule(maximumCornerRadius: nil) | |
| AdaptiveCapsule(maximumCornerRadius: nil) | |
| AdaptiveCapsule(maximumCornerRadius: nil) | |
| AdaptiveCapsule(maximumCornerRadius: nil) | |
| } | |
| Text("Vertical Capsules") | |
| .foregroundStyle(.black) | |
| } | |
| } | |
| .padding() | |
| .foregroundStyle(.blue.opacity(0.5)) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment