Skip to content

Commit

Permalink
Markdown: Add zoomable image previews (#101)
Browse files Browse the repository at this point in the history
* Add image preview struct and environment key

* Make preview zoomable
  • Loading branch information
anian03 authored Nov 14, 2024
1 parent 8fcf177 commit c3cd7a5
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 3 deletions.
76 changes: 73 additions & 3 deletions Sources/ArtemisMarkdown/ArtemisImageProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,79 @@ struct ArtemisImageProvider: ImageProvider {
if url?.absoluteString.contains("local://") == true {
Self.assetProvider.makeImage(url: url)
} else {
ArtemisAsyncImage(imageURL: url) {}
.scaledToFit()
.frame(maxWidth: .infinity, maxHeight: 400, alignment: .leading)
if #available(iOS 18.0, *) {
ImagePreview(url: url)
} else {
ArtemisAsyncImage(imageURL: url) {}
.scaledToFit()
.frame(maxWidth: .infinity, maxHeight: 400, alignment: .leading)
}
}
}
}

@available(iOS 18.0, *)
private struct ImagePreview: View {
@Namespace private var namespace
@Environment(\.imagePreviewsEnabled) private var enabled
let url: URL?
private let id = UUID()
@State private var showPreview = false
@State private var image: UIImage?

var body: some View {
ArtemisAsyncImage(imageURL: url, onSuccess: { result in
image = result.image
}) {
}
.scaledToFit()
.frame(maxWidth: .infinity, maxHeight: 400, alignment: .leading)
.matchedTransitionSource(id: id, in: namespace)
.modifier(ConditionalTapModifier(enabled: enabled) {
showPreview = true
})
.navigationDestination(isPresented: $showPreview) {
GeometryReader { [image] proxy in
if let image {
ZoomableImagePreview(image: image)
.frame(width: proxy.size.width, height: proxy.size.height)
} else {
ArtemisAsyncImage(imageURL: url) {}
.scaledToFit()
}
}
.ignoresSafeArea()
.navigationTransition(.zoom(sourceID: id, in: namespace))
}
}

// Only add the gesture if needed, otherwise this may override other tap gestures
private struct ConditionalTapModifier: ViewModifier {
let enabled: Bool
let action: () -> Void

func body(content: Content) -> some View {
if enabled {
content
.onTapGesture(perform: action)
} else {
content
}
}
}
}

private enum ImagePreviewEnvironmentKey: EnvironmentKey {
static let defaultValue = false
}

public extension EnvironmentValues {
var imagePreviewsEnabled: Bool {
get {
self[ImagePreviewEnvironmentKey.self]
}
set {
self[ImagePreviewEnvironmentKey.self] = newValue
}
}
}
Expand Down
29 changes: 29 additions & 0 deletions Sources/ArtemisMarkdown/ZoomableImagePreview.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// ZoomableImagePreview.swift
// ArtemisCore
//
// Created by Anian Schleyer on 14.11.24.
//

import PDFKit
import SwiftUI

// Adapted from https://stackoverflow.com/a/67577296
struct ZoomableImagePreview: UIViewRepresentable {
let image: UIImage

func makeUIView(context: Context) -> PDFView {
let view = PDFView()
view.document = PDFDocument()
guard let page = PDFPage(image: image) else { return view }
view.document?.insert(page, at: 0)
view.autoScales = true
DispatchQueue.main.async {
view.minScaleFactor = max(view.scaleFactorForSizeToFit * 0.8, 0)
view.maxScaleFactor = view.minScaleFactor + 5
}
return view
}

func updateUIView(_ uiView: PDFView, context: Context) {}
}

0 comments on commit c3cd7a5

Please sign in to comment.