diff --git a/Sources/Compound/Form Styles/FormButtonStyle.swift b/Sources/Compound/Form Styles/FormButtonStyle.swift index d42e87b..4ee8f11 100644 --- a/Sources/Compound/Form Styles/FormButtonStyle.swift +++ b/Sources/Compound/Form Styles/FormButtonStyle.swift @@ -58,7 +58,6 @@ public enum FormRowAccessory: View { case .selected(let isSelected): if isSelected { CompoundIcon(\.check) - .font(.system(size: 24)) .foregroundColor(.compound.iconPrimary) .accessibilityAddTraits(.isSelected) } diff --git a/Sources/Compound/Icons/CompoundIcon.swift b/Sources/Compound/Icons/CompoundIcon.swift index c1b6b1c..efea15a 100644 --- a/Sources/Compound/Icons/CompoundIcon.swift +++ b/Sources/Compound/Icons/CompoundIcon.swift @@ -17,31 +17,60 @@ import CompoundDesignTokens import SwiftUI -/// A view that displays an icon from Compound. The icon behaves -/// similarly to an SF Symbol whereby it will scale to match the font -/// given to it by the `font` modifier, as well as with Dynamic Type. +/// A view that displays an icon from Compound. The icon defaults to a size of 24pt +/// and scales with Dynamic Type, relative to any font given to it by the `font` modifier. public struct CompoundIcon: View { + /// The size of the icon. + public enum Size { + /// An icon size of 16pt. + case xSmall + /// An icon size of 20pt. + case small + /// An icon size of 24pt. + case medium + /// A custom icon size. + case custom(CGFloat) + + var value: CGFloat { + switch self { + case .xSmall: return 16 + case .small: return 20 + case .medium: return 24 + case .custom(let size): return size + } + } + } + + private var size: Size private var image: Image /// Creates an icon using a key path from the Compound tokens. - public init(_ icon: KeyPath) { + /// + /// - Parameters: + /// - icon: The icon to show. + /// - size: The size of the icon. Defaults to `.medium` (24pt). + public init(_ icon: KeyPath, size: Size = .medium) { image = .compound[keyPath: icon] + self.size = size } - /// Creates an icon using a custom image for preview purposes - /// in the Inspector app. + /// Creates an icon using a custom image to allow assets from outside + /// of Compound to scale in the same way as icons. /// - /// If using this initializer with any other image, make sure that its - /// dimensions match those of the compound icons otherwise it - /// will likely not behave as expected. - public init(customImage: Image) { + /// - Parameters: + /// - customImage: The image that should be displayed + /// - size: The size of the icon. Defaults to `.medium` (24pt). + /// + /// ** Note:** The image should have a square frame or it may end up distorted. + public init(customImage: Image, size: Size = .medium) { image = customImage + self.size = size } public var body: some View { image .resizable() - .modifier(CompoundIconStyle()) + .modifier(CompoundIconStyle(size: size)) } } @@ -53,6 +82,8 @@ public struct CompoundIcon: View { private struct CompoundIconStyle: ViewModifier { @Environment(\.font) private var font + let size: CompoundIcon.Size + private var fontSize: FontSize { FontSize.reflecting(font ?? .body) ?? .style(.body) } @@ -60,7 +91,7 @@ private struct CompoundIconStyle: ViewModifier { func body(content: Content) -> some View { let fontSize = fontSize content - .modifier(CompoundIconFrame(fontSize: fontSize.value, textStyle: fontSize.style)) + .modifier(CompoundIconFrame(fontSize: size.value, textStyle: fontSize.style)) } } @@ -79,6 +110,21 @@ private struct CompoundIconFrame: ViewModifier { } } +public extension Label { + /// Creates a label with an icon from Compound and a title generated from a string. + /// - Parameters: + /// - title: A string used as the label’s title. + /// - icon: The icon to use from Compound. + /// - size: The size of the icon. Defaults to `.medium` (24pt). + init(_ title: some StringProtocol, icon: KeyPath, size: CompoundIcon.Size = .medium) where Title == Text, Icon == CompoundIcon { + self.init { + Text(title) + } icon: { + CompoundIcon(icon, size: size) + } + } +} + struct CompoundIcon_Previews: PreviewProvider { static var previews: some View { form @@ -124,26 +170,26 @@ struct CompoundIcon_Previews: PreviewProvider { static var buttons: some View { VStack { Button { } label: { - Label { Text("Heading Large") } icon: { - CompoundIcon(\.userProfile) + Label { Text("Body Large") } icon: { + CompoundIcon(\.userProfile, size: .medium) } } - .font(.compound.headingLG) .buttonStyle(.borderedProminent) Button { } label: { - Label { Text("Body Large") } icon: { - CompoundIcon(\.userProfile) + Label { Text("Body Small") } icon: { + CompoundIcon(\.userProfile, size: .small) } } + .font(.compound.bodySM) .buttonStyle(.borderedProminent) Button { } label: { - Label { Text("Body Small") } icon: { - CompoundIcon(\.userProfile) + Label { Text("Body xSmall") } icon: { + CompoundIcon(\.userProfile, size: .xSmall) } } - .font(.compound.bodySM) + .font(.compound.bodyXS) .buttonStyle(.borderedProminent) } } diff --git a/Sources/Compound/List/ListRowAccessory.swift b/Sources/Compound/List/ListRowAccessory.swift index aab00f7..2a70b17 100644 --- a/Sources/Compound/List/ListRowAccessory.swift +++ b/Sources/Compound/List/ListRowAccessory.swift @@ -27,12 +27,10 @@ public enum ListRowAccessory: View { switch self { case .navigationLink: CompoundIcon(\.chevronRight) - .font(.system(size: 24)) .foregroundColor(.compound.iconTertiaryAlpha) .flipsForRightToLeftLayoutDirection(true) case .selected: CompoundIcon(\.check) - .font(.system(size: 24)) .foregroundColor(.compound.iconPrimary) .accessibilityAddTraits(.isSelected) .padding(.vertical, -4)