Skip to content

Commit

Permalink
add delegate update
Browse files Browse the repository at this point in the history
  • Loading branch information
MrWoWander committed Jun 3, 2021
1 parent ad2b541 commit 61dc27b
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 7 deletions.
104 changes: 99 additions & 5 deletions Sources/Down/AST/Styling/Stylers/AsyncImageLoad.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,29 @@ import AppKit

#endif

public protocol AsyncImageLoadDelegate
{
func textAttachmentDidLoadImage(textAttachment: AsyncImageLoad, displaySizeChanged: Bool)
}

final public class AsyncImageLoad: NSTextAttachment
{
public var imageURL: URL?

public var displaySize: CGSize?

public var maximumDisplayWidth: CGFloat?


public var delegate: AsyncImageLoadDelegate?

weak var textContainer: NSTextContainer?

private var originalImageSize: CGSize?

public init(imageURL: URL? = nil)
public init(imageURL: URL? = nil, delegate: AsyncImageLoadDelegate? = nil)
{
self.imageURL = imageURL

self.delegate = delegate
super.init(data: nil, ofType: nil)
}

Expand Down Expand Up @@ -98,16 +107,33 @@ final public class AsyncImageLoad: NSTextAttachment
self.originalImageSize = imageSize
}
#endif

DispatchQueue.main.async {
if displaySizeChanged
{
self.textContainer?.layoutManager?.setNeedsLayout(forAttachment: self)
}
else
{
self.textContainer?.layoutManager?.setNeedsDisplay(forAttachment: self)
}

// notify the optional delegate
self.delegate?.textAttachmentDidLoadImage(textAttachment: self, displaySizeChanged: displaySizeChanged)
}

}.resume()
}

#if os(macOS)
public override func image(forBounds imageBounds: CGRect, textContainer: NSTextContainer?, characterIndex charIndex: Int) -> NSImage?
{
if let image = image { return image }

guard let contents = contents, let image = NSImage(data: contents) else
{
self.textContainer = textContainer

startAsyncImageDownload()

return nil
Expand All @@ -122,6 +148,8 @@ final public class AsyncImageLoad: NSTextAttachment

guard let contents = contents, let image = UIImage(data: contents) else
{
self.textContainer = textContainer

startAsyncImageDownload()

return nil
Expand Down Expand Up @@ -149,3 +177,69 @@ final public class AsyncImageLoad: NSTextAttachment
return CGRect.zero
}
}

public extension NSLayoutManager
{
/// Determine the character ranges for an attachment
private func rangesForAttachment(attachment: NSTextAttachment) -> [NSRange]?
{
guard let attributedString = self.textStorage else
{
return nil
}

// find character range for this attachment
let range = NSRange(location: 0, length: attributedString.length)

var refreshRanges = [NSRange]()

attributedString.enumerateAttribute(NSAttributedString.Key.attachment, in: range, options: []) { (value, effectiveRange, nil) in

guard let foundAttachment = value as? NSTextAttachment, foundAttachment == attachment else
{
return
}

// add this range to the refresh ranges
refreshRanges.append(effectiveRange)
}

if refreshRanges.count == 0
{
return nil
}

return refreshRanges
}

/// Trigger a relayout for an attachment
func setNeedsLayout(forAttachment attachment: NSTextAttachment)
{
guard let ranges = rangesForAttachment(attachment: attachment) else
{
return
}

// invalidate the display for the corresponding ranges
for range in ranges.reversed() {
self.invalidateLayout(forCharacterRange: range, actualCharacterRange: nil)

// also need to trigger re-display or already visible images might not get updated
self.invalidateDisplay(forCharacterRange: range)
}
}

/// Trigger a re-display for an attachment
func setNeedsDisplay(forAttachment attachment: NSTextAttachment)
{
guard let ranges = rangesForAttachment(attachment: attachment) else
{
return
}

// invalidate the display for the corresponding ranges
for range in ranges.reversed() {
self.invalidateDisplay(forCharacterRange: range)
}
}
}
7 changes: 5 additions & 2 deletions Sources/Down/AST/Styling/Stylers/DownStyler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@ open class DownStyler: Styler {
.font: fonts.listItemPrefix,
.foregroundColor: colors.listItemPrefix]
}

private var delegate: AsyncImageLoadDelegate?

// MARK: - Life cycle

public init(configuration: DownStylerConfiguration = DownStylerConfiguration()) {
public init(configuration: DownStylerConfiguration = DownStylerConfiguration(), delegate: AsyncImageLoadDelegate? = nil) {
fonts = configuration.fonts
colors = configuration.colors
paragraphStyles = configuration.paragraphStyles
Expand All @@ -52,6 +54,7 @@ open class DownStyler: Styler {
codeBlockOptions = configuration.codeBlockOptions
itemParagraphStyler = ListItemParagraphStyler(options: configuration.listItemOptions,
prefixFont: fonts.listItemPrefix)
self.delegate = delegate
}

// MARK: - Styling
Expand Down Expand Up @@ -221,7 +224,7 @@ open class DownStyler: Styler {
}

private func styleGenericImg(in str: NSMutableAttributedString, url: URL) {
let image1Attachment = AsyncImageLoad(imageURL: url)
let image1Attachment = AsyncImageLoad(imageURL: url, delegate: delegate)
let image1String = NSAttributedString(attachment: image1Attachment)

str.setAttributedString(image1String)
Expand Down

0 comments on commit 61dc27b

Please sign in to comment.