diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Package.swift b/Package.swift index b1e655d..4516c2f 100644 --- a/Package.swift +++ b/Package.swift @@ -1,3 +1,4 @@ +// swift-tools-version:5.5.0 import PackageDescription let package = Package( diff --git a/README.md b/README.md index 2056486..0eb3d8b 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ But it won't always work as expected: - Doesn't fit the label height - Big top / bottom margins when the maxFontSize is huge - Not really customisable +- Can't keep font size consistent across multiple labels - ... That's why `FittableFontLabel` exists: @@ -28,6 +29,7 @@ That's why `FittableFontLabel` exists: - Supports attributed string (custom line spacing...) - Customize `maxFontSize` without using default label font size - Auto-layout compliant +- Keep font size consistent across multiple labels using 'FittableRootView' - `UILabel` extension if we want to use `UILabel` - Customisable from xibs / storyboards when using the UILabel's subclass `FittableFontLabel` - ... @@ -44,8 +46,13 @@ That's why `FittableFontLabel` exists: ![](./assets/demo_single_line.gif) +**Consistent font size across multiple labels `FittableRootView`** + +![](./assets/visual_fittable_root_view.png) + ## Usage +### FittableFontLabel ```swift let aFittableFontLabel = FittableFontLabel(frame: CGRect(x: 0, y: 0, width: 300, height: 100)) aFittableFontLabel.autoFittableFont = true @@ -59,6 +66,16 @@ Check the sample project for advanced usage. **Note:** The label `lineBreakMode` must be set to `NSLineBreakByWordWrapping` in order to work. +### FittableRootView +To get a consistent font size across multiple labels embed your FittableFontLabels in a `UIView` with the custom class `FittableRootView`. Then give each label you want to keep consistent the same link identifer. + +The FittableRootView acts as the root of a search for FittableFontLabels with link identifiers. Every FittableFontLabel with an identifier found by the search is updated to use the smallest auto adjusted font size calculated for that identifier. + +**Notes:** +- FittableRootView has an inspectable bool `searchView`. This allows you to disable the search, giving the `FittableRootView` identical behavior to a normal `UIView`. +- You can use multiple identifiers to standardize the font size across multiple sets of labels within the same view. +- When AutoAdjustFontSize is false, that label's font size is ignored in the search for smallest font size. This way if you know which label is going to be longest (i.e. the smallest font size) you can avoid computing font sizes that will go unused while still keeping font size consistent. + ## Installation - iOS 8.0+ @@ -76,6 +93,7 @@ Add `github "tbaranes/FittableFontLabel"` to your Cartfile. FittableFontLabel is available on SPM. Just add the following to your Package file: ```swift +// swift-tools-version:5.5.0 import PackageDescription let package = Package( @@ -148,7 +166,7 @@ The scale factor that determines the smallest font size to use during drawing. T @IBInspectable public var bottomInset: CGFloat = 0 ``` -These four properties allow you to set a marge in your label. That will change the rect where the font must fit. The default value is 0. +These four properties allow you to set a margin on your label. That will change the rect where the font must fit. The default value is 0. ## Contribution diff --git a/Source/FittableFontLabel.swift b/Source/FittableFontLabel.swift index e4d9954..1f41f99 100644 --- a/Source/FittableFontLabel.swift +++ b/Source/FittableFontLabel.swift @@ -43,6 +43,9 @@ open class FittableFontLabel: UILabel { @IBInspectable public var rightInset: CGFloat = 0 @IBInspectable public var topInset: CGFloat = 0 @IBInspectable public var bottomInset: CGFloat = 0 + + /// Identifier + @IBInspectable public var linkIdentifier: String? // MARK: Properties override diff --git a/Source/FittableRootView.swift b/Source/FittableRootView.swift new file mode 100644 index 0000000..f4f636f --- /dev/null +++ b/Source/FittableRootView.swift @@ -0,0 +1,70 @@ +// +// FittableRootView.swift +// +// +// Created by Tyson_Freeze on 12/31/21. +// + +import UIKit + +/// A UIView subclass to give the FittableFontLabel package a root to recursively search for UILabels within. +open class FittableRootView: UIView { + + /// If true, this view and it's subviews will be searched for any UILabels with a fontSizeLinkIdentifier. + @IBInspectable public var searchView: Bool = true + + // MARK: Private + + private var fontSizeLinks: [String : fontSizeLink] = [:] + + private class fontSizeLink { + var minimumFontSize: CGFloat = CGFloat.greatestFiniteMagnitude + var labels: [FittableFontLabel] = [] + + init(label: FittableFontLabel) { + add(label: label) + } + + func add(label: FittableFontLabel) { + labels.append(label) + if label.autoAdjustFontSize { + minimumFontSize = min(minimumFontSize, label.font.pointSize) + } + } + } + + // MARK: Life cycle + + open override func draw(_ rect: CGRect) { + if searchView { + updateFontSizeLinks(in: self) + standardizeFontSizes() + } + } + + // MARK: Search + + private func updateFontSizeLinks(in view: UIView) { + for subview in view.subviews { + if let label = subview as? FittableFontLabel { + if let fontSizeLinkIdentifier = label.linkIdentifier { + if let fontSizeLink = fontSizeLinks[fontSizeLinkIdentifier] { + fontSizeLink.add(label: label) + } else { + fontSizeLinks[fontSizeLinkIdentifier] = fontSizeLink(label: label) + } + } + } else { + updateFontSizeLinks(in: subview) + } + } + } + + private func standardizeFontSizes() { + for fontLink in fontSizeLinks.values { + for label in fontLink.labels { + label.font = label.font.withSize(fontLink.minimumFontSize) + } + } + } +} diff --git a/assets/visual_fittable_root_view.png b/assets/visual_fittable_root_view.png new file mode 100644 index 0000000..b1eb13d Binary files /dev/null and b/assets/visual_fittable_root_view.png differ