Created
June 28, 2020 10:03
-
-
Save apptekstudios/e5f282a67beaa85dc725d1d98ec74191 to your computer and use it in GitHub Desktop.
This file contains 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
import SwiftUI | |
struct AccessibleView: View { | |
@ScaledMetricCustom(relativeTo: .title) var someSize: CGFloat = 100 | |
@ScaledFont(customFontName: "TimesNewRomanPS-BoldMT", size: 18, relativeTo: .body) var bodyFont | |
var body: some View { | |
VStack { | |
Rectangle() | |
.frame(width: someSize, height: someSize) | |
Text("Hello world I am dynamically scaled!") | |
.fixedSize(horizontal: false, vertical: true) | |
.font(bodyFont) | |
Spacer() | |
} | |
} | |
} | |
struct AccessibleView_Previews: PreviewProvider { | |
static var previews: some View { | |
NavigationView { | |
ScrollView { | |
VStack { | |
AccessibleView() | |
AccessibleView() | |
.environment(\.sizeCategory, .extraExtraLarge) | |
AccessibleView() | |
.environment(\.sizeCategory, .accessibilityExtraLarge) | |
AccessibleView() | |
.environment(\.sizeCategory, .accessibilityExtraExtraExtraLarge) | |
} | |
} | |
.navigationBarTitle("Demo") | |
} | |
} | |
} | |
@propertyWrapper | |
struct ScaledMetricCustom<Value>: DynamicProperty where Value: BinaryFloatingPoint { | |
@Environment(\.horizontalSizeClass) var horizontalSizeClass | |
@Environment(\.verticalSizeClass) var verticalSizeClass | |
@Environment(\.sizeCategory) var contentSize | |
// Creates the scaled metric with an unscaled value and a text style to scale relative to. | |
init(wrappedValue: Value, maxValue: Value? = nil, relativeTo textStyle: Font.TextStyle = .body) { | |
self.textStyle = textStyle.uiKit | |
self.baseValue = wrappedValue | |
self.maxValue = maxValue | |
} | |
let textStyle: UIFont.TextStyle | |
let baseValue: Value | |
let maxValue: Value? | |
var traitCollection: UITraitCollection { | |
UITraitCollection(traitsFrom: [ | |
UITraitCollection(horizontalSizeClass: horizontalSizeClass?.uiKit ?? .unspecified), | |
UITraitCollection(verticalSizeClass: verticalSizeClass?.uiKit ?? .unspecified), | |
UITraitCollection(preferredContentSizeCategory: contentSize.uiKit) | |
]) | |
} | |
// The value scaled based on the current environment. | |
var wrappedValue: Value { | |
let scaled = Value(UIFontMetrics(forTextStyle: textStyle).scaledValue(for: CGFloat(baseValue), compatibleWith: traitCollection)) | |
return maxValue.map { min($0, scaled) } ?? scaled | |
} | |
} | |
@propertyWrapper | |
struct ScaledFont: DynamicProperty { | |
@ScaledMetricCustom var fontSize: CGFloat | |
private var fontDefinition: FontDefinition | |
private var maxSize: CGFloat? | |
init(systemFontOfSize fontSize: CGFloat, weight: Font.Weight, design: Font.Design, maxSize: CGFloat? = nil, relativeTo textStyle: Font.TextStyle = .body) { | |
fontDefinition = .system(weight: weight, design: design) | |
self.maxSize = maxSize | |
self._fontSize = ScaledMetricCustom(wrappedValue: fontSize, relativeTo: textStyle) | |
} | |
init(customFontName name: String, size: CGFloat, maxSize: CGFloat? = nil, relativeTo textStyle: Font.TextStyle = .body) { | |
fontDefinition = .custom(name: name) | |
self.maxSize = maxSize | |
self._fontSize = ScaledMetricCustom(wrappedValue: size, relativeTo: textStyle) | |
} | |
private enum FontDefinition { | |
case system(weight: Font.Weight, design: Font.Design) | |
case custom(name: String) | |
} | |
var wrappedValue: Font { | |
switch fontDefinition { | |
case let .custom(name): | |
if #available(iOS 14.0, *) { | |
return Font.custom(name, fixedSize: fontSize) // This is actually using the scaled value (so we pass it to fixed size)! | |
} else { | |
return Font.custom(name, size: fontSize) | |
} | |
case let .system(weight, design): | |
return Font.system(size: fontSize, weight: weight, design: design) | |
} | |
} | |
} | |
extension UserInterfaceSizeClass { | |
var uiKit: UIUserInterfaceSizeClass { | |
switch self { | |
case .compact: return .compact | |
case .regular: return .regular | |
@unknown default: return .unspecified | |
} | |
} | |
} | |
extension ContentSizeCategory { | |
var uiKit: UIContentSizeCategory { | |
switch self { | |
case .accessibilityExtraExtraExtraLarge: return .accessibilityExtraExtraExtraLarge | |
case .accessibilityExtraExtraLarge: return .accessibilityExtraExtraLarge | |
case .accessibilityExtraLarge: return .accessibilityExtraLarge | |
case .accessibilityLarge: return .accessibilityLarge | |
case .accessibilityMedium: return .accessibilityMedium | |
case .extraExtraExtraLarge: return .extraExtraExtraLarge | |
case .extraExtraLarge: return .extraExtraLarge | |
case .extraLarge: return .extraLarge | |
case .extraSmall: return .extraSmall | |
case .large: return .large | |
case .medium: return .medium | |
case .small: return .small | |
@unknown default: return .unspecified | |
} | |
} | |
} | |
extension Font.TextStyle { | |
var uiKit: UIFont.TextStyle { | |
switch self { | |
case .body: return .body | |
case .callout: return .callout | |
case .caption: return .caption1 | |
case .caption2: return .caption2 | |
case .footnote: return .footnote | |
case .headline: return .headline | |
case .largeTitle: return .largeTitle | |
case .subheadline: return .subheadline | |
case .title: return .title1 | |
case .title2: return .title2 | |
case .title3: return .title3 | |
@unknown default: return .body | |
} | |
} | |
} | |
// import PlaygroundSupport | |
// PlaygroundSupport.PlaygroundPage.current.setLiveView(AccessibleView_Previews.previews) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment