229 lines
8.2 KiB
Swift
229 lines
8.2 KiB
Swift
// swiftlint:disable all
|
|
// swiftformat:disable all
|
|
//
|
|
// IconDrawable.swift
|
|
// https://github.com/home-assistant/Iconic
|
|
//
|
|
// Copyright © 2019 The Home Assistant Authors
|
|
// Licensed under the Apache 2.0 license
|
|
// For more information see https://github.com/home-assistant/Iconic
|
|
|
|
import UIKit
|
|
import CoreText
|
|
|
|
/** A wrapper class for Objective-C compatibility. */
|
|
public final class Iconic: NSObject { }
|
|
|
|
/** The IconDrawable protocol defines the complete interface of an Iconic icon's capabilities. */
|
|
public protocol IconDrawable {
|
|
|
|
/** The icon font's family name. */
|
|
static var familyName: String { get }
|
|
|
|
/** The icon font's total count of available icons. */
|
|
static var count: Int { get }
|
|
|
|
/** The icon's name. */
|
|
var name: String { get }
|
|
|
|
/** The icon's unicode. */
|
|
var unicode: String { get }
|
|
|
|
/**
|
|
Creates a new instance with the specified icon name.
|
|
If there is no valid name is recognised, this initializer falls back to the first available icon.
|
|
|
|
- parameter iconName: The icon name to use for the new instance.
|
|
*/
|
|
init(named iconName: String)
|
|
|
|
/**
|
|
Returns the icon as an attributed string with the given pointSize and color.
|
|
|
|
- parameter pointSize: The size of the font.
|
|
- parameter color: The tint color of the font.
|
|
*/
|
|
func attributedString(ofSize pointSize: CGFloat, color: UIColor?) -> NSAttributedString
|
|
|
|
/**
|
|
Returns the icon as an attributed string with the given pointSize, color and padding.
|
|
|
|
- parameter pointSize: The size of the font.
|
|
- parameter color: The tint color of the font.
|
|
- parameter edgeInsets: The edge insets to be used as horizontal and vertical padding.
|
|
*/
|
|
func attributedString(ofSize pointSize: CGFloat, color: UIColor?, edgeInsets: UIEdgeInsets) -> NSAttributedString
|
|
|
|
/**
|
|
Returns the icon as an image with the given size and color.
|
|
|
|
- parameter size: The size of the image, in points.
|
|
- parameter color: A tint color for the image.
|
|
*/
|
|
func image(ofSize size: CGSize, color: UIColor?) -> UIImage
|
|
|
|
/**
|
|
Returns the icon as an image with the given size, color and padding.
|
|
|
|
- parameter size: The size of the image, in points.
|
|
- parameter color: The tint color of the image.
|
|
- parameter edgeInsets: The edge insets to be used as padding values.
|
|
*/
|
|
func image(ofSize size: CGSize, color: UIColor?, edgeInsets: UIEdgeInsets) -> UIImage
|
|
|
|
/**
|
|
Creates and returns the icon font object for the specified size.
|
|
|
|
- parameter fontSize: The size (in points) to which the font is scaled.
|
|
*/
|
|
static func font(ofSize fontSize: CGFloat) -> UIFont
|
|
|
|
/**
|
|
Registers the icon font with the font manager.
|
|
Note: an exception will be thrown if the resource (ttf/otf) font file is not found in the bundle.
|
|
*/
|
|
static func register()
|
|
|
|
/**
|
|
Unregisters the icon font from the font manager.
|
|
*/
|
|
static func unregister()
|
|
}
|
|
|
|
/** This extension adds the required default implementation for Iconic to work. */
|
|
extension IconDrawable {
|
|
|
|
public func attributedString(ofSize pointSize: CGFloat, color: UIColor?) -> NSAttributedString {
|
|
|
|
let font = Self.font(ofSize: pointSize)
|
|
|
|
var attributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font : font]
|
|
|
|
if let color = color {
|
|
attributes[NSAttributedString.Key.foregroundColor] = color
|
|
}
|
|
|
|
return NSAttributedString(string: unicode, attributes: attributes)
|
|
}
|
|
|
|
public func attributedString(ofSize pointSize: CGFloat, color: UIColor?, edgeInsets: UIEdgeInsets) -> NSAttributedString {
|
|
|
|
let aString = attributedString(ofSize: pointSize, color: color)
|
|
let mString = NSMutableAttributedString(attributedString: aString)
|
|
|
|
let range = NSRange(location: 0, length: mString.length)
|
|
|
|
mString.addAttribute(NSAttributedString.Key.baselineOffset, value: edgeInsets.bottom-edgeInsets.top, range: range)
|
|
|
|
let leftSpace = NSAttributedString(string: " ", attributes: [NSAttributedString.Key.kern: edgeInsets.left])
|
|
let rightSpace = NSAttributedString(string: " ", attributes: [NSAttributedString.Key.kern: edgeInsets.right])
|
|
|
|
mString.insert(rightSpace, at: mString.length)
|
|
mString.insert(leftSpace, at: 0)
|
|
|
|
return mString
|
|
}
|
|
|
|
public func image(ofSize size: CGSize, color: UIColor?) -> UIImage {
|
|
|
|
return image(ofSize: size, color: color, edgeInsets: .zero)
|
|
}
|
|
|
|
public func image(ofSize size: CGSize, color: UIColor?, edgeInsets: UIEdgeInsets) -> UIImage {
|
|
|
|
let pointSize = min(size.width, size.height)
|
|
let aString = attributedString(ofSize: pointSize, color: color)
|
|
let mString = NSMutableAttributedString(attributedString: aString)
|
|
|
|
var rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
|
|
rect.origin.y -= edgeInsets.top
|
|
rect.size.width -= edgeInsets.left + edgeInsets.right
|
|
rect.size.height -= edgeInsets.top + edgeInsets.bottom
|
|
|
|
let paragraphStyle = NSMutableParagraphStyle()
|
|
paragraphStyle.alignment = .center
|
|
|
|
let range = NSRange(location: 0, length: mString.length)
|
|
|
|
mString.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: range)
|
|
|
|
// Renders the attributed string as image using Text Kit
|
|
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
|
|
mString.draw(in: rect)
|
|
let image = UIGraphicsGetImageFromCurrentImageContext()
|
|
UIGraphicsEndImageContext()
|
|
|
|
return image!
|
|
}
|
|
|
|
public static func font(ofSize fontSize: CGFloat) -> UIFont {
|
|
|
|
// Needs a default size, since zero would return a system font object.
|
|
let size = (fontSize == 0) ? 10.0 : fontSize
|
|
|
|
return UIFont(name: familyName, size: size)!
|
|
}
|
|
|
|
public static func register() {
|
|
|
|
// No need to register the font more than once
|
|
if UIFont.familyNames.map({ $0.replacingOccurrences(of: " ", with: "") }).contains(familyName) {
|
|
return
|
|
}
|
|
|
|
guard let url = resourceUrl() else {
|
|
print("Unable to register font '\(familyName)' beacuse URL was nil!")
|
|
return
|
|
}
|
|
var error: Unmanaged<CFError>? = nil
|
|
let descriptors = CTFontManagerCreateFontDescriptorsFromURL(url as CFURL) as NSArray?
|
|
|
|
guard let descriptor = (descriptors as? [CTFontDescriptor])?.first else {
|
|
assertionFailure("Could not retrieve font descriptors of font at path '\(url)',")
|
|
return
|
|
}
|
|
|
|
let font = CTFontCreateWithFontDescriptorAndOptions(descriptor, 0.0, nil, [.preventAutoActivation])
|
|
let fontName = CTFontCopyPostScriptName(font) as String
|
|
|
|
// Registers font dynamically
|
|
if CTFontManagerRegisterFontsForURL(url as CFURL, .process, &error) == false || error != nil {
|
|
if let error = error?.takeUnretainedValue(), CFErrorGetDomain(error) == kCTFontManagerErrorDomain, CFErrorGetCode(error) == CTFontManagerError.alreadyRegistered.rawValue {
|
|
// this is fine
|
|
} else {
|
|
assertionFailure("Failed registering font with the postscript name '\(fontName)' at path '\(url)' with error: \(String(describing: error)).")
|
|
}
|
|
}
|
|
|
|
print("Font '\(familyName)' registered successfully!")
|
|
}
|
|
|
|
public static func unregister() {
|
|
|
|
// No need to unregister if the font isn't registered
|
|
if UIFont.familyNames.contains(familyName) == false {
|
|
return
|
|
}
|
|
|
|
guard let url = resourceUrl() else {
|
|
print("Unable to unregister font '\(familyName)' beacuse URL was nil!")
|
|
return
|
|
}
|
|
var error: Unmanaged<CFError>? = nil
|
|
|
|
if CTFontManagerUnregisterFontsForURL(url as CFURL, .none, &error) == false || error != nil {
|
|
assertionFailure("Failed unregistering font with name '\(familyName)' at path '\(url)' with error: \(String(describing: error)).")
|
|
}
|
|
|
|
print("Font '\(familyName)' unregistered successfully!")
|
|
}
|
|
|
|
fileprivate static func resourceUrl() -> URL? {
|
|
let extensions = ["otf", "ttf"]
|
|
let bundle = Bundle(for: Iconic.self)
|
|
|
|
return extensions.compactMap { bundle.url(forResource: familyName, withExtension: $0) }.first
|
|
}
|
|
}
|
|
|