Antidote/Antidote/AvatarManager.swift
2024-02-22 21:43:11 +02:00

132 lines
4.3 KiB
Swift

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
import Foundation
class AvatarManager {
enum AvatarType: String {
case Normal
case Call
}
fileprivate let theme: Theme
fileprivate let cache: NSCache<AnyObject, AnyObject>
init(theme: Theme) {
self.theme = theme
self.cache = NSCache()
}
/**
Returns round avatar created from string with a given diameter. Searches for an avatar in cache first,
if not found creates it.
- Parameters:
- string: String to create avatar from. In case of empty string avatar will be set to "?".
- diameter: Diameter of circle with avatar.
- Returns: Avatar from given string with given size.
*/
func avatarFromString(_ string: String, diameter: CGFloat, type: AvatarType = .Normal) -> UIImage {
var string = string
if string.isEmpty {
string = "?"
}
let key = keyFromString(string, diameter: diameter, type: type)
if let avatar = cache.object(forKey: key as AnyObject) as? UIImage {
return avatar
}
let avatar = createAvatarFromString(string, diameter: diameter, type: type)
cache.setObject(avatar, forKey: key as AnyObject)
return avatar
}
}
private extension AvatarManager {
func keyFromString(_ string: String, diameter: CGFloat, type: AvatarType) -> String {
return "\(string)-\(diameter)-\(type.rawValue)"
}
func createAvatarFromString(_ string: String, diameter: CGFloat, type: AvatarType) -> UIImage {
let avatarString = avatarStringFromString(string)
let label = UILabel()
label.layer.borderWidth = 1.0
label.layer.masksToBounds = true
label.textAlignment = .center
label.text = avatarString
switch type {
case .Normal:
label.backgroundColor = theme.colorForType(.NormalBackground)
label.layer.borderColor = theme.colorForType(.LinkText).cgColor
label.textColor = theme.colorForType(.LinkText)
case .Call:
label.backgroundColor = .clear
label.layer.borderColor = theme.colorForType(.CallButtonIconColor).cgColor
label.textColor = theme.colorForType(.CallButtonIconColor)
}
var size: CGSize
var fontSize = diameter
repeat {
fontSize -= 1
let font = UIFont.antidoteFontWithSize(fontSize, weight: .light)
size = avatarString.stringSizeWithFont(font)
}
while (max(size.width, size.height) > diameter)
let frame = CGRect(x: 0, y: 0, width: diameter, height: diameter)
label.font = UIFont.antidoteFontWithSize(fontSize * 0.6, weight: .light)
label.layer.cornerRadius = frame.size.width / 2
label.frame = frame
return imageWithLabel(label)
}
func avatarStringFromString(_ string: String) -> String {
guard !string.isEmpty else {
return ""
}
// Avatar can have alphanumeric symbols and ? sign.
let badSymbols = (CharacterSet.alphanumerics.inverted as NSCharacterSet).mutableCopy() as! NSMutableCharacterSet
badSymbols.removeCharacters(in: "?")
let words = string.components(separatedBy: CharacterSet.whitespaces).map {
$0.components(separatedBy: badSymbols as CharacterSet).joined(separator: "")
}.filter {
!$0.isEmpty
}
var result = words.map {
$0.isEmpty ? "" : $0[$0.startIndex ..< $0.index($0.startIndex, offsetBy: 1)]
}.joined(separator: "")
let numberOfLetters = min(2, result.count)
result = result.uppercased()
return String(result[result.startIndex ..< result.index(result.startIndex, offsetBy: numberOfLetters)])
}
func imageWithLabel(_ label: UILabel) -> UIImage {
UIGraphicsBeginImageContextWithOptions(label.bounds.size, false, 0.0)
label.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}