iOS/Sources/App/Utilities/ScaledFont.swift

116 lines
4.7 KiB
Swift

// Created by Keith Harrison https://useyourloaf.com
// Copyright (c) 2018 Keith Harrison. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import UIKit
/// A utility type to help you use custom fonts with
/// dynamic type.
///
/// To use supply the name of a style dictionary
/// when creating the instance. The style
/// dictionary should be stored as a property
/// list file (by default in the main bundle).
///
/// If the plist file is invalid or missing the default
/// system font is used.
///
/// The style dictionary contains an entry for each text
/// style that uses the raw string value for each
/// `UIFontTextStyle` as the key.
///
/// The value of each entry is a dictionary with two keys:
///
/// + fontName: A String which is the font name.
/// + fontSize: A number which is the point size to use
/// at the `.large` content size.
///
/// For example to use a 17 pt Noteworthy-Bold font
/// for the `.headline` style at the `.large` content size:
///
/// <dict>
/// <key>UICTFontTextStyleHeadline</key>
/// <dict>
/// <key>fontName</key>
/// <string>Noteworthy-Bold</string>
/// <key>fontSize</key>
/// <integer>17</integer>
/// </dict>
/// </dict>
///
/// You do not need to include an entry for every text style
/// but if you try to use a text style that is not included
/// in the dictionary it will fallback to the system preferred
/// font.
public struct ScaledFont {
private struct FontDescription: Decodable {
let fontSize: CGFloat
let fontName: String
}
private typealias StyleDictionary = [UIFont.TextStyle.RawValue: FontDescription]
private var styleDictionary: StyleDictionary?
/// Create a `ScaledFont`
///
/// - Parameter fontName: Name of a plist file (without the extension)
/// that contains the style dictionary used to
/// scale fonts for each text style.
/// - Parameter bundle: Bundle containing the plist file.
/// Defaults to the main bundle.
public init(fontName: String, in bundle: Bundle = Bundle.main) {
if let url = bundle.url(forResource: fontName, withExtension: "plist"),
let data = try? Data(contentsOf: url) {
let decoder = PropertyListDecoder()
self.styleDictionary = try? decoder.decode(StyleDictionary.self, from: data)
}
}
/// Get the scaled font for the given text style using the
/// style dictionary supplied at initialization.
///
/// - Parameter textStyle: The `UIFontTextStyle` for the
/// font.
/// - Returns: A `UIFont` of the custom font that has been
/// scaled for the users currently selected preferred
/// text size.
///
/// - Note: If the style dictionary does not have
/// a font for this text style the default preferred
/// font is returned.
public func font(forTextStyle textStyle: UIFont.TextStyle) -> UIFont {
guard let fontDescription = styleDictionary?[textStyle.rawValue],
let font = UIFont(name: fontDescription.fontName, size: fontDescription.fontSize) else {
return UIFont.preferredFont(forTextStyle: textStyle)
}
let fontMetrics = UIFontMetrics(forTextStyle: textStyle)
return fontMetrics.scaledFont(for: font)
}
}