Skip to content

Commit

Permalink
fixes the issue where WebImage is constantly rerendered
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuatbrown committed Jan 2, 2025
1 parent bd30f9b commit 133be4a
Showing 1 changed file with 94 additions and 20 deletions.
114 changes: 94 additions & 20 deletions Nos/Views/Components/AvatarView.swift
Original file line number Diff line number Diff line change
@@ -1,30 +1,104 @@
import SwiftUI
import Logger
import SDWebImageSwiftUI

struct AvatarView: View {
let imageUrl: URL?
let size: CGFloat
private let id: String

var imageUrl: URL?
var size: CGFloat
init(imageUrl: URL?, size: CGFloat) {
self.imageUrl = imageUrl
self.size = size
self.id = imageUrl?.absoluteString ?? "empty-avatar-\(UUID())"
}

var body: some View {
WebImage(
url: imageUrl,
content: { image in
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: size, height: size)
.clipShape(Circle())
},
placeholder: {
Image.emptyAvatar
ManagedImageView(imageUrl: imageUrl, size: size, id: id)
}
}

private struct ManagedImageView: View {
let imageUrl: URL?
let size: CGFloat
let id: String

@StateObject private var loader = ImageLoader()

var body: some View {
Group {
if let image = loader.image {
Image(uiImage: image)
.resizable()
.renderingMode(.original)
.aspectRatio(contentMode: .fill)
.frame(width: size, height: size)
.clipShape(Circle())
} else {
Color.gray
}
)
}
.frame(width: size, height: size)
.clipShape(Circle())
.id(id)
.task(id: imageUrl?.absoluteString) {
await loader.load(url: imageUrl)
}
}
}

private class ImageLoader: ObservableObject {
@Published var image: UIImage?
private var cancellable: SDWebImageCombinedOperation?

func load(url: URL?) async {
await MainActor.run {
cancel()
}

guard let url = url else {
await MainActor.run {
image = nil
}
return
}

// Check cache first
if let cachedImage = SDImageCache.shared.imageFromCache(forKey: url.absoluteString) {
await MainActor.run {
image = cachedImage
}
return
}

// Load from network
await withCheckedContinuation { continuation in
Task { @MainActor in
cancellable = SDWebImageManager.shared.loadImage(
with: url,
options: [.retryFailed, .refreshCached],
progress: nil
) { [weak self] image, _, _, _, _, _ in
Task { @MainActor in
self?.image = image
continuation.resume()
}
}
}
}
}

func cancel() {
cancellable?.cancel()
cancellable = nil
}

deinit {
cancel()
}
}

// Make view identity stable
extension ManagedImageView: Equatable {
static func == (lhs: ManagedImageView, rhs: ManagedImageView) -> Bool {
lhs.id == rhs.id && lhs.size == rhs.size && lhs.imageUrl == rhs.imageUrl
}
}

Expand All @@ -39,9 +113,9 @@ struct AvatarView_Previews: PreviewProvider {
AvatarView(imageUrl: avatarURL, size: 87)
}
VStack {
AvatarView(size: 24)
AvatarView(size: 45)
AvatarView(size: 87)
AvatarView(imageUrl: nil, size: 24)
AvatarView(imageUrl: nil, size: 45)
AvatarView(imageUrl: nil, size: 87)
}
}
}

0 comments on commit 133be4a

Please sign in to comment.