Skip to content

Commit d4d8993

Browse files
authored
Render first level inline images (gonzalezreal#193)
* Render inline images * Update demo project with inline images * Update documentation
1 parent fdc444d commit d4d8993

File tree

17 files changed

+237
-52
lines changed

17 files changed

+237
-52
lines changed
1.16 KB
Loading
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "237-30x40.jpg",
5+
"idiom" : "universal"
6+
}
7+
],
8+
"info" : {
9+
"author" : "xcode",
10+
"version" : 1
11+
}
12+
}

Examples/Demo/Demo/ImageProvidersView.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,23 @@ struct ImageProvidersView: View {
1313
"""
1414

1515
private let otherContent = """
16-
You can use the built-in `AssetImageProvider` to load images from image assets.
16+
You can use the built-in `AssetImageProvider` and `AssetInlineImageProvider`
17+
to load images from image assets.
1718
1819
```swift
1920
Markdown {
2021
"![A dog](dog)"
22+
"A ![dog](smallDog) within a line of text."
2123
"― Photo by André Spieker"
2224
}
2325
.markdownImageProvider(.asset)
26+
.markdownInlineImageProvider(.asset)
2427
```
2528
2629
![A dog](dog)
2730
31+
An image ![dog](smallDog) within a line of text.
32+
2833
― Photo by André Spieker
2934
"""
3035

@@ -36,6 +41,7 @@ struct ImageProvidersView: View {
3641
Section("Image Assets") {
3742
Markdown(self.otherContent)
3843
.markdownImageProvider(.asset)
44+
.markdownInlineImageProvider(.asset)
3945
}
4046
}
4147
}

Examples/Demo/Demo/ImagesView.swift

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,31 @@ struct ImagesView: View {
1515
― Photo by Jennifer Trovato
1616
"""
1717

18-
private let assetContent = """
19-
You can configure a `Markdown` view to load images from the asset catalog.
18+
private let inlineImageContent = """
19+
You can also insert images in a line of text, such as
20+
![](https://picsum.photos/id/237/50/25) or
21+
![](https://picsum.photos/id/433/50/25).
2022
21-
```swift
22-
Markdown {
23-
"![This is an image](237-200x300)"
24-
}
25-
.markdownImageProvider(.asset)
23+
```
24+
You can also insert images in a line of text, such as
25+
![](https://picsum.photos/id/237/50/25) or
26+
![](https://picsum.photos/id/433/50/25).
2627
```
2728
28-
![This is an image](dog)
29+
Note that MarkdownUI **cannot** apply any styling to
30+
inline images.
2931
30-
Photo by André Spieker
32+
Photos by André Spieker and Thomas Lefebvre
3133
"""
3234

3335
var body: some View {
3436
DemoView {
3537
Markdown(self.content)
3638

39+
Section("Inline images") {
40+
Markdown(self.inlineImageContent)
41+
}
42+
3743
Section("Customization Example") {
3844
Markdown(self.content)
3945
}

Sources/MarkdownUI/Content/Inlines/Inline.swift

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -82,19 +82,23 @@ extension Array where Element == Inline {
8282
}
8383

8484
extension Inline {
85-
var image: (source: String?, alt: String)? {
86-
guard case let .image(source, children) = self else {
87-
return nil
88-
}
89-
return (source, children.text)
85+
struct Image: Hashable {
86+
var source: String?
87+
var alt: String
88+
var destination: String?
9089
}
9190

92-
var imageLink: (source: String?, alt: String, destination: String?)? {
93-
guard case let .link(destination, children) = self, children.count == 1,
94-
let (source, alt) = children.first?.image
95-
else {
91+
var image: Image? {
92+
switch self {
93+
case let .image(source, children):
94+
return .init(source: source, alt: children.text)
95+
case let .link(destination, children) where children.count == 1:
96+
guard case let .some(.image(source, children)) = children.first else {
97+
return nil
98+
}
99+
return .init(source: source, alt: children.text, destination: destination)
100+
default:
96101
return nil
97102
}
98-
return (source, alt, destination)
99103
}
100104
}

Sources/MarkdownUI/Content/Inlines/InlineImage.swift

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,6 @@ import Foundation
44
///
55
/// You can use an image inline to embed an image in a paragraph.
66
///
7-
/// Note that even if you can compose images and text as part of the same inline content, the ``Markdown``
8-
/// view is currently limited to displaying image-only paragraphs and will ignore images composed with other
9-
/// text inlines in the same block.
10-
///
11-
/// In the following example, the ``Markdown`` view will not display the image in the last paragraph, as it
12-
/// is interleaved with other text inline.
13-
///
147
/// ```swift
158
/// Markdown {
169
/// Paragraph {
@@ -23,8 +16,9 @@ import Foundation
2316
/// }
2417
/// }
2518
/// Paragraph {
26-
/// "The following image will be ignored:"
19+
/// "You can also insert images in a line of text, such as "
2720
/// InlineImage(source: URL(string: "https://picsum.photos/id/237/100/150")!)
21+
/// "."
2822
/// }
2923
/// }
3024
/// ```
8.03 KB
Loading
8.03 KB
Loading
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import SwiftUI
2+
3+
/// An inline image provider that loads images from resources located in an app or a module.
4+
public struct AssetInlineImageProvider: InlineImageProvider {
5+
private let name: (URL) -> String
6+
private let bundle: Bundle?
7+
8+
/// Creates an asset inline image provider.
9+
/// - Parameters:
10+
/// - name: A closure that extracts the image resource name from the URL in the Markdown content.
11+
/// - bundle: The bundle where the image resources are located. Specify `nil` to search the app’s main bundle.
12+
public init(
13+
name: @escaping (URL) -> String = \.lastPathComponent,
14+
bundle: Bundle? = nil
15+
) {
16+
self.name = name
17+
self.bundle = bundle
18+
}
19+
20+
public func image(with url: URL, label: String) async throws -> Image {
21+
.init(self.name(url), bundle: self.bundle, label: Text(label))
22+
}
23+
}
24+
25+
extension InlineImageProvider where Self == AssetInlineImageProvider {
26+
/// An inline image provider that loads images from resources located in an app or a module.
27+
///
28+
/// Use the `markdownInlineImageProvider(_:)` modifier to configure this image provider for a view hierarchy.
29+
public static var asset: Self {
30+
.init()
31+
}
32+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import SwiftUI
2+
3+
/// The default inline image provider, which loads images from the network.
4+
public struct DefaultInlineImageProvider: InlineImageProvider {
5+
private let urlSession: URLSession
6+
7+
/// Creates a default inline image provider.
8+
/// - Parameter urlSession: An `URLSession` instance to load images.
9+
public init(urlSession: URLSession = .shared) {
10+
self.urlSession = urlSession
11+
}
12+
13+
public func image(with url: URL, label: String) async throws -> Image {
14+
try await Image(
15+
platformImage: DefaultImageLoader.shared
16+
.image(with: url, urlSession: self.urlSession)
17+
)
18+
}
19+
}
20+
21+
extension InlineImageProvider where Self == DefaultInlineImageProvider {
22+
/// The default inline image provider, which loads images from the network.
23+
///
24+
/// Use the `markdownInlineImageProvider(_:)` modifier to configure
25+
/// this image provider for a view hierarchy.
26+
public static var `default`: Self {
27+
.init()
28+
}
29+
}

0 commit comments

Comments
 (0)