Signal-iOS/SignalUI/UIKitExtensions/UIKit+Image.swift
2024-03-25 14:22:28 -05:00

128 lines
4.1 KiB
Swift

//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import SignalCoreKit
public extension UIImageView {
func setImage(imageName: String) {
guard let image = UIImage(named: imageName) else {
owsFailDebug("Couldn't load image: \(imageName)")
return
}
self.image = image
}
func setTemplateImage(_ templateImage: UIImage?, tintColor: UIColor) {
guard let templateImage else {
owsFailDebug("Missing image")
return
}
self.image = templateImage.withRenderingMode(.alwaysTemplate)
self.tintColor = tintColor
}
func setTemplateImageName(_ imageName: String, tintColor: UIColor) {
guard let image = UIImage(named: imageName) else {
owsFailDebug("Couldn't load image: \(imageName)")
return
}
setTemplateImage(image, tintColor: tintColor)
}
class func withTemplateImage(_ templateImage: UIImage?, tintColor: UIColor) -> UIImageView {
let imageView = UIImageView()
imageView.setTemplateImage(templateImage, tintColor: tintColor)
return imageView
}
class func withTemplateImageName(_ imageName: String, tintColor: UIColor) -> UIImageView {
let imageView = UIImageView()
imageView.setTemplateImageName(imageName, tintColor: tintColor)
return imageView
}
/// Creates an image view with the given theme icon, tinted with the given
/// color, and constrained to the given size if present.
/// - Parameters:
/// - icon: The ``ThemeIcon`` to display.
/// - tintColor: The color to tint the icon
/// - size: The size to constrain the image to.
/// When `nil`, no constraints are added.
/// - Returns: A `UIImageView` of the icon.
class func withTemplateIcon(
_ icon: ThemeIcon,
tintColor: UIColor,
constrainedTo size: CGSize? = nil
) -> UIImageView {
let imageView = UIImageView()
imageView.setTemplateImage(Theme.iconImage(icon), tintColor: tintColor)
if let size {
imageView.autoSetDimensions(to: size)
}
return imageView
}
}
// MARK: -
public extension UIImage {
func asTintedImage(color: UIColor) -> UIImage? {
let template = self.withRenderingMode(.alwaysTemplate)
let imageView = UIImageView(image: template)
imageView.tintColor = color
return imageView.renderAsImage(opaque: imageView.isOpaque, scale: UIScreen.main.scale)
}
/// Redraw the image into a new image, with an added background color, and inset the
/// original image by the provided insets.
func withBackgroundColor(_ color: UIColor, insets: UIEdgeInsets = .zero) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(size, true, scale)
defer {
UIGraphicsEndImageContext()
}
guard let ctx = UIGraphicsGetCurrentContext(), let image = cgImage else {
owsFailDebug("Failed to create image context when setting image background")
return nil
}
let rect = CGRect(origin: .zero, size: size)
ctx.setFillColor(color.cgColor)
ctx.fill(rect)
// draw the background behind
ctx.concatenate(CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: size.height))
ctx.draw(image, in: rect.inset(by: insets))
guard let newImage = UIGraphicsGetImageFromCurrentImageContext() else {
owsFailDebug("Failed to create background-colored image from context")
return nil
}
return newImage
}
}
// MARK: -
public extension UIView {
func renderAsImage() -> UIImage {
renderAsImage(opaque: false, scale: UIScreen.main.scale)
}
func renderAsImage(opaque: Bool, scale: CGFloat) -> UIImage {
let format = UIGraphicsImageRendererFormat()
format.scale = scale
format.opaque = opaque
let renderer = UIGraphicsImageRenderer(bounds: self.bounds,
format: format)
return renderer.image { (context) in
self.layer.render(in: context.cgContext)
}
}
}