Skip to content
This repository has been archived by the owner on Aug 10, 2024. It is now read-only.

Commit

Permalink
Test
Browse files Browse the repository at this point in the history
  • Loading branch information
RelativeJoe committed Jul 28, 2023
1 parent c6e8691 commit c789aeb
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 49 deletions.
22 changes: 9 additions & 13 deletions Sources/MediaUI/Utitlities/ImageHelpers/ImageDownsampler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,16 @@ public actor ImageDownsampler {
self.image = image
}
//MARK: - Functions
public func downsampled(width: CGFloat?, height: CGFloat?, scale: CGFloat = 3) -> UNImage? {
return downsampled(for: ImageSizeBuilder(width: width, height: height), scale: scale)
}
public func downsampled(width: CGFloat?, scale: CGFloat = 3) -> UNImage? {
return downsampled(for: ImageSizeBuilder(width: width), scale: scale)
}
public func downsampled(height: CGFloat?, scale: CGFloat = 3) -> UNImage? {
return downsampled(for: ImageSizeBuilder(height: height), scale: scale)
}
public func downsampled(for sizeBuilder: ImageSizeBuilder, scale: CGFloat = 3) -> UNImage? {
public func downsampled(using builder: ImageSizeBuilder, scale: CGFloat = 3) -> (image: UNImage, size: CGSize)? {
guard let imageData = imageData ?? image?.data(.high), let image = image ?? UNImage(data: imageData) else {
return nil
}
let size = sizeBuilder.build(for: image)
return downsampled(for: builder.build(for: image))
}
public func downsampled(for size: CGSize, scale: CGFloat = 3) -> (image: UNImage, size: CGSize)? {
guard let imageData = imageData ?? image?.data(.high) else {
return nil
}
let maxPixelDimensions = max(size.width, size.height) * scale
let downsamplingOptions = [
kCGImageSourceCreateThumbnailFromImageAlways: true,
Expand All @@ -51,9 +47,9 @@ public actor ImageDownsampler {
return nil
}
#if canImport(AppKit)
return UNImage(cgImage: cgImage, size: size)
return (UNImage(cgImage: cgImage, size: size), size)
#else
return UNImage(cgImage: cgImage)
return (UNImage(cgImage: cgImage), size)
#endif
}
}
97 changes: 61 additions & 36 deletions Sources/MediaUI/Views/DownsampledImage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,76 +12,101 @@ public struct DownsampledImage<PlaceholderContent: View, ImageContent: View>: Vi
//MARK: - Properties
@Environment(\.displayScale) private var displayScale
@State private var image: UNImage?
private var data: Data?
@State private var imageSize: CGSize?
private let rawImage: UNImage?
private let data: Data?
private let placeholder: PlaceholderContent
private let width: CGFloat?
private let height: CGFloat?
private let imageBuilder: (Image) -> ImageContent
private let content: (Image, CGSize) -> ImageContent
//MARK: - View
public var body: some View {
if width != nil && height != nil {
content(width: width, height: height)
if let image, let imageSize {
content(Image(unImage: image), imageSize)
.state(width != nil || height != nil) { view in
view
.frame(width: width, height: height)
}
}else {
GeometryReader { proxy in
content(width: proxy.size.width, height: proxy.size.height)
if width != nil || height != nil {
placeholderContent(width: width, height: height)
}else {
GeometryReader { proxy in
placeholderContent(width: proxy.size.width, height: proxy.size.height)
}
}
}
}
@ViewBuilder func content(width: CGFloat?, height: CGFloat?) -> some View {
if let image {
imageBuilder(Image(unImage: image))
.frame(width: width, height: height)
}else {
placeholder
.onAppear {
Task.detached(priority: .userInitiated) {
let downsampler = ImageDownsampler(data: data, image: image)
let sizeBuilder = ImageSizeBuilder(width: width, height: height)
image = await downsampler.downsampled(for: sizeBuilder, scale: displayScale)
}
@ViewBuilder func placeholderContent(width: CGFloat?, height: CGFloat?) -> some View {
placeholder
.onAppear {
Task.detached(priority: .userInitiated) {
let downsampler = ImageDownsampler(data: data, image: rawImage)
let sizeBuilder = ImageSizeBuilder(width: width, height: height)
let result = await downsampler.downsampled(using: sizeBuilder, scale: displayScale)
image = result?.image
imageSize = result?.size
}
}
}
}
}

//MARK: - Public Initializer
public extension DownsampledImage where PlaceholderContent == LoadingView, ImageContent == Image {
///MediaUI: Initialize a DownsampledImage from Data.
///MediaUI: Creates a DownsampledImage from Data.
init(data: Data?) {
self.data = data
self.placeholder = LoadingView()
self.width = nil
self.height = nil
self.imageBuilder = { image in
self.init(data: data) { image, _ in
image
.resizable()
}
}
///MediaUI: Initialize a DownsampledImage from a UNImage.
///MediaUI: Creates a DownsampledImage from an UNImage.
init(image: UNImage?) {
self._image = State(initialValue: image)
self.init(image: image) { image, _ in
image
.resizable()
}
}
///MediaUI: Creates a DownsampledImage from the specified named asset..
init(_ assetName: String) {
self.init(image: UNImage(named: assetName))
}
}

//MARK: - Public Initializer
public extension DownsampledImage where PlaceholderContent == LoadingView {
///MediaUI: Creates a DownsampledImage from Data.
init(data: Data?, @ViewBuilder content: @escaping (Image, CGSize) -> ImageContent) {
self.data = data
self.placeholder = LoadingView()
self.width = nil
self.height = nil
self.rawImage = nil
self.content = content
}
///MediaUI: Creates a DownsampledImage from an UNImage.
init(image: UNImage?, @ViewBuilder content: @escaping (Image, CGSize) -> ImageContent) {
self.rawImage = image
self.placeholder = LoadingView()
self.width = nil
self.height = nil
self.data = nil
self.imageBuilder = { image in
image
.resizable()
}
self.content = content
}
///MediaUI: Creates a DownsampledImage from the specified named asset..
init(_ assetName: String, @ViewBuilder content: @escaping (Image, CGSize) -> ImageContent) {
self.init(image: UNImage(named: assetName), content: content)
}
}

//MARK: - Public Modifiers
public extension DownsampledImage {
///DownsampledImage: Adds a placeholder View if no Image can be displayed.
func placeholder<NewPlaceholderContent: View>(@ViewBuilder placeholder: () -> NewPlaceholderContent) -> DownsampledImage<NewPlaceholderContent, ImageContent> {
DownsampledImage<NewPlaceholderContent, ImageContent>(data: data, placeholder: placeholder(), width: width, height: height, imageBuilder: imageBuilder)
DownsampledImage<NewPlaceholderContent, ImageContent>(rawImage: rawImage, data: data, placeholder: placeholder(), width: width, height: height, content: content)
}
///DownsampledImage: Provides the frame used to downsample the image.
func frame(width: CGFloat? = nil, height: CGFloat? = nil) -> Self {
DownsampledImage(data: data, placeholder: placeholder, width: width, height: height, imageBuilder: imageBuilder)
}
func build<NewImageContent: View>(@ViewBuilder builder: @escaping (Image) -> NewImageContent) -> DownsampledImage<PlaceholderContent, NewImageContent> {
DownsampledImage<PlaceholderContent, NewImageContent>(data: data, placeholder: placeholder, width: width, height: height, imageBuilder: builder)
DownsampledImage(rawImage: rawImage, data: data, placeholder: placeholder, width: width, height: height, content: content)
}
}
20 changes: 20 additions & 0 deletions Sources/MediaUI/Views/Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// Extensions.swift
//
//
// Created by Joe Maghzal on 22/07/2023.
//

import SwiftUI

public extension View {
func frame(minSize: CGSize, alignment: Alignment = .center) -> some View {
frame(minWidth: minSize.width, minHeight: minSize.height, alignment: alignment)
}
func frame(size: CGSize, alignment: Alignment = .center) -> some View {
frame(width: size.width, height: size.height, alignment: alignment)
}
func frame(maxSize: CGSize, alignment: Alignment = .center) -> some View {
frame(maxWidth: maxSize.width, maxHeight: maxSize.height, alignment: alignment)
}
}

0 comments on commit c789aeb

Please sign in to comment.