2

I have a UITextView, to which I am adding some Custom UI. This custom UI is laid out based on the position of the text inside the UITextView.

I am using the firstRectForRange method (from UITextInput) on the UITextView. This method returns the frame of the specified text range in the textView.

However, it does not work correctly right after setting the attributedText. It takes a minimal amount of time before it will return the expected value. This period could very likely be the time until the first frame with the updated text is rendered and displayed on the screen.

I am currently wrapping the code that lays out the custom UI into a dispatch_async(dispatch_get_main_queue(), ...) block, which delays its execution until after the frame is rendered (by about 9ms).

What does UITextView do inbetween setting the attributedString and displaying the next frame on the screen? Is there a better place / method call for me to hook into?
Is there any way for me to force running that action synchronously?

Things I have tried calling:
(after setting attributedText; before calling firstRectForRange)

  • Calling setNeedsLayout and layoutIfNeeded before firstRectForRange.
  • Calling drawViewHierarchyInRect(self.bounds, afterScreenUpdates: true)
Tim Bodeit
  • 9,673
  • 3
  • 27
  • 57

1 Answers1

0

Are you attempting to set the attributedText from a background thread? I would guess that's the problem. All interface changes are supposed to happen on the main thread. From personal experience, changing things from a background thread occasionally works, is often slower, and even more often doesn't work at all.

If that is the case, the dispatch_async is exactly what you want to do. It makes it very easy to process data in the background, then quickly hop onto the main thread for the rendering. If you want to do more processing after setting the string and finding the firstRectForRange you could use a dispatch_sync to make your background thread wait for the rendering, or dispatch back onto another thread after rendering.

As for what the UITextView does while rendering the attributedString, I'm really not sure, but I'd guess the slight delay comes from the system figuring out how the attributed string is supposed to look, and how it will be rendered. For a further understanding, you probably need to dive deep into CoreText, as there is quite a lot that happens under the hood.

edit: If you are on the main thread, then I don't think there's a whole lot more you can do. Using dispatch_after maybe slightly cleaner than using dispatch_async.

One more thought. Have you tried calling setNeedsDisplay? That usually tells a view to redraw itself.

esthepiking
  • 1,647
  • 1
  • 16
  • 27