Animated UIImage in UITextView with TextKit
Asked Answered
R

1

17

I have a UIImage that contains multiple image frames and a duration which was sliced together from an animated GIF file. If I load the image in a UIWebView it will animate as expected, but I want to use a UITextView instead. The obvious choice is to use TextKit with a custom NSLayoutManger and many custom NSAttributedString attributes to achieve various drawing effects. I have a single UITextView that I'd like to give a NSAttributedString that contains NSTextAttachments for all of the images I need to display inline. It's working fine except for when I load one of these animated UIImages, they do not animate through their frames. Any thoughts on how to accomplish this? I've come up with a theoretical option:

  1. For each animated image add a custom defined attribute
  2. When layout manager goes to draw backgrounds, iterate this attribute to locate the positions of the attachment.
  3. Callback to the text view to create UIImageViews with the animated image, add as a subview, and call startAnimating

I have yet to try this, but it's fairly hacky. I'm wondering if there's a better option.

In summary: I want to animate a UIImage inside a UITextView via NSTextAttachment inside a NSAttributedString without the necessity for extra UIImageView objects. You can ignore the fact that this was previously done with a UIWebView. UIImage has properties (images, duration) for setting up animated frames, which I currently have configured. If I assign it to a UIImageView it animates, if I set it as a NSTextAttachment, no animation.

I would appreciate any feedback. Thanks!

Remy answered 29/4, 2015 at 16:21 Comment(8)
Wondering the exact same thing this morning.Labana
Can you provide a screenshot, of how it looks in the WebView, that you are trying to replicate with UIImage and UITextView. This will help visualize it better and help with the answers.Janettejaneva
@Janettejaneva I can't screenshot because you won't know that the image is animating, it would just look like a static image. To clarify: I want to animate a UIImage inside a UITextView via NSTextAttachment inside a NSAttributedString without the necessity for extra UIImageView objects. You can ignore the fact that this was previously done with a UIWebView. UIImage has properties (images, duration) for setting up animated frames, which I currently have configured. If I assign it to a UIImageView it animates, if I set it as a NSTextAttachment, no animation.Remy
I understand much better now. But the comment you made to clarify would be much better integrated in the question where everyone can see it easily.Janettejaneva
If you hold references to the NSTextAttachment object and manually change the image property after a delay, do you see anything change? If so you could subclass NSTextAttachment and recreate the animation behaviour.Propraetor
@Propraetor Yeah that's an option I considered, subclass NSTextAttachment with a CADisplayLink that would analyze the animated UIImage and determine when it needed to update the image property of the text attachment. It seems only slightly less hacky than adding UIImageView subviews to the text view. I will give it a try though and see the feasibility.Remy
@Remy how did you solve this issue? I'm looking exactly for the same. Add a GIF as NSTextAttachment to the UITextView and it only stays static, like a regular image.Optician
@IvanCantarino Yeah it's currently not possible. I asked an apple engineer at WWDC and they confirmed that CATiledLayer which is what backs UITextView will not animate. The solution is to use the text attachment as a "placeholder" with a frame but no image, then add a subview on top of the text view with your animated image at the same frame.Remy
T
4

I think this behaviour is because the UITextView doesn't display the attachments using UIImageView subviews. When I observe the view hierarchy with and without the NSTextAttachments added to the UITextView, I don't see any new views (you can observe the view hierarchy using View Debugging in Xcode, or by saying [[[UIApplication sharedApplication] keyWindow] recursiveDescription]). Presumably, the UITextView just draws it's attachments directly.

That said, one way I can think of to include an animatable attachment is to create an animated UIImageView yourself as a subview of the UITextView, place it exactly where the NSLayoutManager places the NSTextAttachment, and scroll it whenever the UITextView scrolls. You can get the rectangle of the NSTextAttachment from the NSLayoutManager by calling boundingRectForGlyphRange:inTextContainer: (possibly in layoutManager:didCompleteLayoutForTextContainer:atEnd:).

Toothpaste answered 7/5, 2015 at 9:40 Comment(2)
Yes this is the "theoretical option" I spoke about in my question. It's fairly hacky and I'd like to avoid this approach if possible since it will be fragile and require a ton of extra management of those UIImageView subviews. The UITextView I'm using is contained within a UICollectionViewCell so the cell would have to manage their positions and lifecycle. When initially researching I did what you said: I inspected the subviews of the text view in the hopes it nested some image views that I could animate, but there are no subviews, all rendering is done within a CATiledLayer.Remy
If the textview is within a collection view, you'll have to manage the image views' positions and lifecycles in the same way as you do for the text view. You can create the UIImageView subview/s whenever you create the UITextView, and place them in didCompleteLayout. If the textview is within a collection view, I presume you're not using the scrolling features of the textview - if that's the case, you wouldn't need to bother to scroll the image view.Toothpaste

© 2022 - 2024 — McMap. All rights reserved.