Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Example/Example/DefaultContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import SwiftUI

struct DefaultContentView: View {

@State private var url = URL(string: "https://via.placeholder.com/1000")
@State private var url = URL(string: "https://picsum.photos/1000")
@State private var size: CGSize = .init(width: 160, height: 160)

var body: some View {
VStack {
Text("Default AsyncImage")
ScrollView {
LazyVGrid(columns: [.init(), .init()]) {
ForEach(0..<1000, id: \.self) { _ in
Expand Down
3 changes: 2 additions & 1 deletion Example/Example/DownsampleContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import AsyncDownSamplingImage

struct DownsampleContentView: View {

@State private var url = URL(string: "https://via.placeholder.com/1000")
@State private var url = URL(string: "https://picsum.photos/1000")
@State private var size: CGSize = .init(width: 160, height: 160)

var body: some View {
VStack {
Text("AsyncDownSamplingImage")
ScrollView {
LazyVGrid(columns: [.init(), .init()]) {
ForEach(0..<1000, id: \.self) { _ in
Expand Down
4 changes: 2 additions & 2 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 38 additions & 10 deletions Sources/AsyncDownSamplingImage/AsyncDownSamplingImage.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,36 @@
import SwiftUI


private enum LoadingOpacityEdge {
case upperBound
case lowerBound

var value: Double {
switch self {
case .upperBound:
return 1
case .lowerBound:
return 0.3
}
}

mutating func toggle() {
switch self {
case .upperBound:
self = .lowerBound
case .lowerBound:
self = .upperBound
}
}
}

/// AsyncDownSamplingImage is a Image View that can perform downsampling and use less memory use than `AsyncImage`.
///
/// About generics type:
///
/// - Content: View which appears when state is Successful.
/// - Placeholder: View which appears when state is Loading.
/// - Fail: View which appears when state is Failed.
/// - Content: View which appears when state is Successful.
/// - Placeholder: View which appears when state is Loading.
/// - Fail: View which appears when state is Failed.
public struct AsyncDownSamplingImage<Content: View, Placeholder: View, Fail: View>: View {

/// resource URL where you would like to fetch an image.
Expand All @@ -24,7 +48,7 @@ public struct AsyncDownSamplingImage<Content: View, Placeholder: View, Fail: Vie
public let fail: (Error) -> Fail

@State private var status: Status = .idle
@State private var loadingOpacity: CGFloat = 1.0
@State private var loadingOpacity: LoadingOpacityEdge = .upperBound

/// Standard initializer
///
Expand Down Expand Up @@ -79,19 +103,18 @@ public struct AsyncDownSamplingImage<Content: View, Placeholder: View, Fail: Vie
}
case .failed(let error):
fail(error)
case .loaded(let image):
case .loaded(let image), .reloading(let image):
content(image)
}
EmptyView()
}

var loadingView: some View {
Image(systemName: "plus") // any image is okay
.resizable()
.cornerRadius(2)
.opacity(loadingOpacity)
.opacity(loadingOpacity.value)
.animation(
Animation.easeIn(duration: 0.5).repeatForever(autoreverses: true),
Animation.easeInOut(duration: 0.7).repeatForever(autoreverses: true),
value: loadingOpacity
)
.frame(
Expand All @@ -100,12 +123,16 @@ public struct AsyncDownSamplingImage<Content: View, Placeholder: View, Fail: Vie
)
.redacted(reason: .placeholder)
.onAppear {
loadingOpacity = abs(1.0 - loadingOpacity)
loadingOpacity.toggle()
}
}

func startLoading(url: URL) {
status = .loading
if case Status.loaded(let image) = status {
status = .reloading(image)
} else {
status = .loading
}
Task {
do {
let cgImage = try await DownSampling.perform(
Expand All @@ -125,6 +152,7 @@ extension AsyncDownSamplingImage {
enum Status {
case idle
case loading
case reloading(Image)
case failed(Error)
case loaded(Image)
}
Expand Down
115 changes: 114 additions & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
@@ -1 +1,114 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"><link rel="icon" href="/AsyncDownSamplingImage/favicon.ico"><link rel="mask-icon" href="/AsyncDownSamplingImage/favicon.svg" color="#333333"><title>Documentation</title><script>var baseUrl = "/AsyncDownSamplingImage/"</script><link href="/AsyncDownSamplingImage/css/documentation-topic.3bca6578.css" rel="prefetch"><link href="/AsyncDownSamplingImage/css/documentation-topic~topic~tutorials-overview.82acfe22.css" rel="prefetch"><link href="/AsyncDownSamplingImage/css/topic.ee15af52.css" rel="prefetch"><link href="/AsyncDownSamplingImage/css/tutorials-overview.06e8bcf7.css" rel="prefetch"><link href="/AsyncDownSamplingImage/js/chunk-2d0d3105.cd72cc8e.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/documentation-topic.f62098b6.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/documentation-topic~topic~tutorials-overview.8e36e44f.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-bash.1b52852f.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-c.d1db3f17.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-cpp.eaddddbe.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-css.75eab1fe.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-custom-markdown.7cffc4b3.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-custom-swift.5cda5c20.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-diff.62d66733.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-http.163e45b6.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-java.8326d9d8.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-javascript.acb8a8eb.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-json.471128d2.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-llvm.6100b125.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-markdown.90077643.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-objectivec.bcdf5156.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-perl.757d7b6f.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-php.cc8d6c27.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-python.c214ed92.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-ruby.f889d392.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-scss.62ee18da.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-shell.dd7f411f.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-swift.84f3e88c.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/highlight-js-xml.9c3688c7.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/topic.6a1c7b7f.js" rel="prefetch"><link href="/AsyncDownSamplingImage/js/tutorials-overview.c8178b83.js" rel="prefetch"><link href="/AsyncDownSamplingImage/css/index.12bb178a.css" rel="preload" as="style"><link href="/AsyncDownSamplingImage/js/chunk-vendors.b24b7aaa.js" rel="preload" as="script"><link href="/AsyncDownSamplingImage/js/index.58e30ec4.js" rel="preload" as="script"><link href="/AsyncDownSamplingImage/css/index.12bb178a.css" rel="stylesheet"></head><body data-color-scheme="auto"><noscript><style>.noscript{font-family:"SF Pro Display","SF Pro Icons","Helvetica Neue",Helvetica,Arial,sans-serif;margin:92px auto 140px auto;text-align:center;width:980px}.noscript-title{color:#111;font-size:48px;font-weight:600;letter-spacing:-.003em;line-height:1.08365;margin:0 auto 54px auto;width:502px}@media only screen and (max-width:1068px){.noscript{margin:90px auto 120px auto;width:692px}.noscript-title{font-size:40px;letter-spacing:0;line-height:1.1;margin:0 auto 45px auto;width:420px}}@media only screen and (max-width:735px){.noscript{margin:45px auto 60px auto;width:87.5%}.noscript-title{font-size:32px;letter-spacing:.004em;line-height:1.125;margin:0 auto 35px auto;max-width:330px;width:auto}}#loading-placeholder{display:none}</style><div class="noscript"><h1 class="noscript-title">This page requires JavaScript.</h1><p>Please turn on JavaScript in your browser and refresh the page to view its content.</p></div></noscript><div id="app"></div><script src="/AsyncDownSamplingImage/js/chunk-vendors.b24b7aaa.js"></script><script src="/AsyncDownSamplingImage/js/index.58e30ec4.js"></script></body></html>
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<link rel="icon" href="/AsyncDownSamplingImage/favicon.ico">
<link rel="mask-icon" href="/AsyncDownSamplingImage/favicon.svg" color="#333333">
<title>Documentation</title>
<script>var baseUrl = "/AsyncDownSamplingImage/"</script>
<link href="/AsyncDownSamplingImage/css/documentation-topic.3bca6578.css" rel="prefetch">
<link href="/AsyncDownSamplingImage/css/documentation-topic~topic~tutorials-overview.82acfe22.css" rel="prefetch">
<link href="/AsyncDownSamplingImage/css/topic.ee15af52.css" rel="prefetch">
<link href="/AsyncDownSamplingImage/css/tutorials-overview.06e8bcf7.css" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/chunk-2d0d3105.cd72cc8e.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/documentation-topic.f62098b6.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/documentation-topic~topic~tutorials-overview.8e36e44f.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-bash.1b52852f.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-c.d1db3f17.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-cpp.eaddddbe.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-css.75eab1fe.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-custom-markdown.7cffc4b3.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-custom-swift.5cda5c20.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-diff.62d66733.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-http.163e45b6.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-java.8326d9d8.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-javascript.acb8a8eb.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-json.471128d2.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-llvm.6100b125.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-markdown.90077643.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-objectivec.bcdf5156.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-perl.757d7b6f.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-php.cc8d6c27.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-python.c214ed92.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-ruby.f889d392.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-scss.62ee18da.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-shell.dd7f411f.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-swift.84f3e88c.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/highlight-js-xml.9c3688c7.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/topic.6a1c7b7f.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/js/tutorials-overview.c8178b83.js" rel="prefetch">
<link href="/AsyncDownSamplingImage/css/index.12bb178a.css" rel="preload" as="style">
<link href="/AsyncDownSamplingImage/js/chunk-vendors.b24b7aaa.js" rel="preload" as="script">
<link href="/AsyncDownSamplingImage/js/index.58e30ec4.js" rel="preload" as="script">
<link href="/AsyncDownSamplingImage/css/index.12bb178a.css" rel="stylesheet">
</head>

<body data-color-scheme="auto"><noscript>
<style>
.noscript {
font-family: "SF Pro Display", "SF Pro Icons", "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: 92px auto 140px auto;
text-align: center;
width: 980px
}

.noscript-title {
color: #111;
font-size: 48px;
font-weight: 600;
letter-spacing: -.003em;
line-height: 1.08365;
margin: 0 auto 54px auto;
width: 502px
}

@media only screen and (max-width:1068px) {
.noscript {
margin: 90px auto 120px auto;
width: 692px
}

.noscript-title {
font-size: 40px;
letter-spacing: 0;
line-height: 1.1;
margin: 0 auto 45px auto;
width: 420px
}
}

@media only screen and (max-width:735px) {
.noscript {
margin: 45px auto 60px auto;
width: 87.5%
}

.noscript-title {
font-size: 32px;
letter-spacing: .004em;
line-height: 1.125;
margin: 0 auto 35px auto;
max-width: 330px;
width: auto
}
}

#loading-placeholder {
display: none
}

</style>
<div class="noscript">
<h1 class="noscript-title">This page requires JavaScript.</h1>
<p>Please turn on JavaScript in your browser and refresh the page to view its content.</p>
</div>
</noscript>
<div id="app"></div>
<script src="/AsyncDownSamplingImage/js/chunk-vendors.b24b7aaa.js"></script>
<script src="/AsyncDownSamplingImage/js/index.58e30ec4.js"></script>
</body>

</html>