// // Icon.swift // VDS // // Created by Matt Bruce on 1/9/23. // import Foundation import UIKit import VDSColorTokens import Combine /// An icon is a graphical element that conveys information at a glance. It helps orient /// a customer, explain functionality and draw attention to interactive elements. Icons /// should have a functional purpose and should never be used for decoration. @objc(VDSIcon) open class Icon: View { //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- required public init() { super.init(frame: .zero) } public override init(frame: CGRect) { super.init(frame: .zero) } public required init?(coder: NSCoder) { super.init(coder: coder) } //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- private var dimensions: CGSize { guard let customSize else { return size.dimensions } return .init(width: customSize, height: customSize) } //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- /// UIImageView used to render the icon. open var imageView = UIImageView().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.contentMode = .scaleAspectFill $0.clipsToBounds = true } /// Color of the icon. open var color: UIColor = VDSColor.paletteBlack { didSet { if let hex = color.hexString, !UIColor.isVDSColor(color: color) { print("icon.color is not a VDSColor. Hex: \(hex) is not a supported color") } setNeedsUpdate() } } /// Size of the icon. open var size: Size = .medium { didSet { setNeedsUpdate() }} /// This will be used to render the icon with corresponding name. open var name: Name? { didSet { setNeedsUpdate() }} /// A custom size of the icon. open var customSize: Int? { didSet { setNeedsUpdate() }} /// The natural size for the receiving view, considering only properties of the view itself. open override var intrinsicContentSize: CGSize { dimensions } //functions //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { super.setup() setContentCompressionResistancePriority(.required, for: .vertical) setContentHuggingPriority(.required, for: .vertical) setContentCompressionResistancePriority(.required, for: .horizontal) setContentHuggingPriority(.required, for: .horizontal) addSubview(imageView) imageView.pinToSuperView() backgroundColor = .clear isAccessibilityElement = true accessibilityTraits = .image } /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() //get the color for the image var imageColor = color //ensure the correct color for white/black colors if surface == .dark && color == VDSColor.paletteBlack { imageColor = VDSColor.elementsPrimaryOndark } else if surface == .light && color == VDSColor.paletteBlack { imageColor = VDSColor.elementsPrimaryOnlight } //get the image name //set the image if let name, let image = getImage(for: name.rawValue) { setImage(image: image, imageColor: imageColor) } else { imageView.image = nil } invalidateIntrinsicContentSize() } /// Resets to default settings. open override func reset() { super.reset() color = VDSColor.paletteBlack imageView.image = nil } //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- private func getImage(for imageName: String) -> UIImage? { return BundleManager.shared.image(for: imageName) } private func setImage(image: UIImage, imageColor: UIColor) { imageView.image = image.withTintColor(imageColor) } }