2021-02-04 12:28:16 +01:00
|
|
|
//
|
|
|
|
// AvatarConfigurableView.swift
|
|
|
|
// Mastodon
|
|
|
|
//
|
|
|
|
// Created by Cirno MainasuK on 2021-2-4.
|
|
|
|
//
|
|
|
|
|
|
|
|
import UIKit
|
|
|
|
import AlamofireImage
|
2021-06-23 14:47:49 +02:00
|
|
|
import FLAnimatedImage
|
|
|
|
import Nuke
|
2021-02-04 12:28:16 +01:00
|
|
|
|
|
|
|
protocol AvatarConfigurableView {
|
2021-02-23 08:16:55 +01:00
|
|
|
static var configurableAvatarImageSize: CGSize { get }
|
|
|
|
static var configurableAvatarImageCornerRadius: CGFloat { get }
|
2021-02-04 12:28:16 +01:00
|
|
|
var configurableAvatarImageView: UIImageView? { get }
|
|
|
|
var configurableAvatarButton: UIButton? { get }
|
2021-02-23 08:16:55 +01:00
|
|
|
func configure(with configuration: AvatarConfigurableViewConfiguration)
|
2021-02-04 12:28:16 +01:00
|
|
|
func avatarConfigurableView(_ avatarConfigurableView: AvatarConfigurableView, didFinishConfiguration configuration: AvatarConfigurableViewConfiguration)
|
|
|
|
}
|
|
|
|
|
|
|
|
extension AvatarConfigurableView {
|
|
|
|
|
2021-02-23 08:16:55 +01:00
|
|
|
public func configure(with configuration: AvatarConfigurableViewConfiguration) {
|
2021-02-04 12:28:16 +01:00
|
|
|
let placeholderImage: UIImage = {
|
2021-06-16 12:32:48 +02:00
|
|
|
guard let placeholderImage = configuration.placeholderImage else {
|
|
|
|
return AppContext.shared.placeholderImageCacheService.image(
|
|
|
|
color: .systemFill,
|
|
|
|
size: Self.configurableAvatarImageSize,
|
|
|
|
cornerRadius: Self.configurableAvatarImageCornerRadius
|
|
|
|
)
|
2021-03-10 06:36:01 +01:00
|
|
|
}
|
2021-06-16 12:32:48 +02:00
|
|
|
return placeholderImage
|
2021-02-04 12:28:16 +01:00
|
|
|
}()
|
2021-06-23 14:47:49 +02:00
|
|
|
|
2021-02-04 12:28:16 +01:00
|
|
|
// reset layer attributes
|
|
|
|
configurableAvatarImageView?.layer.masksToBounds = false
|
|
|
|
configurableAvatarImageView?.layer.cornerRadius = 0
|
|
|
|
configurableAvatarImageView?.layer.cornerCurve = .circular
|
|
|
|
|
|
|
|
configurableAvatarButton?.layer.masksToBounds = false
|
|
|
|
configurableAvatarButton?.layer.cornerRadius = 0
|
|
|
|
configurableAvatarButton?.layer.cornerCurve = .circular
|
|
|
|
|
2021-05-10 10:06:00 +02:00
|
|
|
// accessibility
|
|
|
|
configurableAvatarImageView?.accessibilityIgnoresInvertColors = true
|
|
|
|
configurableAvatarButton?.accessibilityIgnoresInvertColors = true
|
|
|
|
|
2021-02-04 12:28:16 +01:00
|
|
|
defer {
|
|
|
|
avatarConfigurableView(self, didFinishConfiguration: configuration)
|
|
|
|
}
|
2021-04-09 11:31:43 +02:00
|
|
|
|
2021-06-23 14:47:49 +02:00
|
|
|
guard let imageDisplayingView: ImageDisplayingView = configurableAvatarImageView ?? configurableAvatarButton?.imageView else {
|
2021-02-04 12:28:16 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-06-23 14:47:49 +02:00
|
|
|
// set corner radius (due to GIF won't crop)
|
|
|
|
imageDisplayingView.layer.masksToBounds = true
|
|
|
|
imageDisplayingView.layer.cornerRadius = Self.configurableAvatarImageCornerRadius
|
|
|
|
imageDisplayingView.layer.cornerCurve = Self.configurableAvatarImageCornerRadius < Self.configurableAvatarImageSize.width * 0.5 ? .continuous :.circular
|
|
|
|
|
|
|
|
// set border
|
|
|
|
configureLayerBorder(view: imageDisplayingView, configuration: configuration)
|
|
|
|
|
|
|
|
|
|
|
|
// set image
|
|
|
|
let url = configuration.avatarImageURL
|
|
|
|
let processors: [ImageProcessing] = [
|
|
|
|
ImageProcessors.Resize(
|
|
|
|
size: Self.configurableAvatarImageSize,
|
|
|
|
unit: .points,
|
|
|
|
contentMode: .aspectFill,
|
|
|
|
crop: false
|
|
|
|
),
|
|
|
|
ImageProcessors.RoundedCorners(
|
|
|
|
radius: Self.configurableAvatarImageCornerRadius
|
|
|
|
)
|
|
|
|
]
|
|
|
|
|
|
|
|
let request = ImageRequest(url: url, processors: processors)
|
|
|
|
let options = ImageLoadingOptions(
|
|
|
|
placeholder: placeholderImage,
|
|
|
|
transition: .fadeIn(duration: 0.2)
|
|
|
|
)
|
|
|
|
|
|
|
|
Nuke.loadImage(
|
|
|
|
with: request,
|
|
|
|
options: options,
|
|
|
|
into: imageDisplayingView
|
|
|
|
) { result in
|
|
|
|
switch result {
|
|
|
|
case .failure:
|
|
|
|
break
|
|
|
|
case .success:
|
|
|
|
break
|
2021-02-04 12:28:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-02 12:13:45 +02:00
|
|
|
func configureLayerBorder(view: UIView, configuration: AvatarConfigurableViewConfiguration) {
|
|
|
|
guard let borderWidth = configuration.borderWidth, borderWidth > 0,
|
|
|
|
let borderColor = configuration.borderColor else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
view.layer.masksToBounds = true
|
|
|
|
view.layer.cornerRadius = Self.configurableAvatarImageCornerRadius
|
|
|
|
view.layer.cornerCurve = .continuous
|
|
|
|
view.layer.borderColor = borderColor.cgColor
|
|
|
|
view.layer.borderWidth = borderWidth
|
|
|
|
}
|
|
|
|
|
2021-02-04 12:28:16 +01:00
|
|
|
func avatarConfigurableView(_ avatarConfigurableView: AvatarConfigurableView, didFinishConfiguration configuration: AvatarConfigurableViewConfiguration) { }
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
struct AvatarConfigurableViewConfiguration {
|
|
|
|
|
2021-02-23 08:16:55 +01:00
|
|
|
let avatarImageURL: URL?
|
|
|
|
let placeholderImage: UIImage?
|
2021-04-02 12:13:45 +02:00
|
|
|
let borderColor: UIColor?
|
|
|
|
let borderWidth: CGFloat?
|
2021-02-04 12:28:16 +01:00
|
|
|
|
2021-04-28 14:10:17 +02:00
|
|
|
let keepImageCorner: Bool
|
|
|
|
|
2021-04-02 12:13:45 +02:00
|
|
|
init(
|
|
|
|
avatarImageURL: URL?,
|
|
|
|
placeholderImage: UIImage? = nil,
|
|
|
|
borderColor: UIColor? = nil,
|
2021-04-28 14:10:17 +02:00
|
|
|
borderWidth: CGFloat? = nil,
|
2021-04-28 14:36:10 +02:00
|
|
|
keepImageCorner: Bool = false // default clip corner on image
|
2021-04-02 12:13:45 +02:00
|
|
|
) {
|
2021-02-23 08:16:55 +01:00
|
|
|
self.avatarImageURL = avatarImageURL
|
|
|
|
self.placeholderImage = placeholderImage
|
2021-04-02 12:13:45 +02:00
|
|
|
self.borderColor = borderColor
|
|
|
|
self.borderWidth = borderWidth
|
2021-04-28 14:10:17 +02:00
|
|
|
self.keepImageCorner = keepImageCorner
|
2021-02-04 12:28:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|