2023-04-16 17:47:06 +00:00
|
|
|
//
|
|
|
|
// AvatarImageView.swift
|
|
|
|
// ComposeUI
|
|
|
|
//
|
|
|
|
// Created by Shadowfacts on 3/4/23.
|
|
|
|
//
|
|
|
|
|
|
|
|
import SwiftUI
|
|
|
|
|
|
|
|
public struct AvatarImageView: View {
|
|
|
|
public typealias FetchAvatar = (URL) async -> UIImage?
|
|
|
|
|
|
|
|
let url: URL?
|
|
|
|
let size: CGFloat
|
|
|
|
let style: Style
|
|
|
|
let fetchAvatar: FetchAvatar
|
|
|
|
@State private var image: UIImage?
|
|
|
|
|
|
|
|
public init(url: URL?, size: CGFloat, style: Style, fetchAvatar: @escaping FetchAvatar) {
|
|
|
|
self.url = url
|
|
|
|
self.size = size
|
|
|
|
self.style = style
|
|
|
|
self.fetchAvatar = fetchAvatar
|
|
|
|
}
|
|
|
|
|
|
|
|
public var body: some View {
|
|
|
|
imageView
|
|
|
|
.resizable()
|
|
|
|
.frame(width: size, height: size)
|
2023-05-09 18:39:15 +00:00
|
|
|
.clipShape(RoundedRectangle(cornerRadius: style.cornerRadiusFraction * size, style: .continuous))
|
2023-04-23 01:16:30 +00:00
|
|
|
.task { @MainActor in
|
2023-04-20 02:20:05 +00:00
|
|
|
image = nil
|
2023-04-16 17:47:06 +00:00
|
|
|
if let url {
|
|
|
|
image = await fetchAvatar(url)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// tell swiftui that this view has changed (and therefore the task needs to re-run) when the url changes
|
|
|
|
.id(url)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private var imageView: Image {
|
|
|
|
if let image {
|
|
|
|
return Image(uiImage: image)
|
|
|
|
} else {
|
|
|
|
return placeholder
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private var placeholder: Image {
|
|
|
|
Image(systemName: style == .roundRect ? "person.crop.square" : "person.crop.circle")
|
|
|
|
}
|
|
|
|
|
|
|
|
public enum Style: Equatable {
|
|
|
|
case roundRect, circle
|
|
|
|
|
|
|
|
var cornerRadiusFraction: CGFloat {
|
|
|
|
switch self {
|
|
|
|
case .roundRect:
|
|
|
|
return 0.1
|
|
|
|
case .circle:
|
|
|
|
return 0.5
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|