I am trying to position a UIButton in a UITextView at a specific point in a large block of dynamically-generated text. However, it is proving devilishly difficult to get accurate coordinates in a timely fashion. The attributedText has been set with a pre-generated NSAttributedString, and right now, I am adding the button in a delegate method for NSLayoutManager, as that seemed the most logical place. However, I'm open to other options!
Here is the relevant code:
func layoutManager(layoutManager: NSLayoutManager, didCompleteLayoutForTextContainer textContainer: NSTextContainer?, atEnd layoutFinishedFlag: Bool) {
let layoutManager = textView.layoutManager
let textContainer = textView.textContainer
let text = textView.attributedText
let inset = textView.textContainerInset.top
text.enumerateAttribute(NSAttachmentAttributeName, inRange: NSMakeRange(0, text.length), options: .LongestEffectiveRangeNotRequired) { (value, range, stop) in
if (value != nil) {
let boundingRect = layoutManager.boundingRectForGlyphRange(range, inTextContainer: textContainer)
let frame = CGRectMake(boundingRect.origin.x, boundingRect.origin.y+inset, boundingRect.size.width, boundingRect.size.height)
let button = UIButton()
button.setImage(UIImage(named: "canterbury_small_dark.png"), forState: .Normal)
self.textView.addSubview(button)
button.frame = frame
}
}
}
This code works great, but it is triggered only when you scroll all the way to the bottom of the UITextView. Is there a way to ensure that all layout has been performed for the whole text without having to scroll all the way to the bottom of the view? I have tried any number of the methods in NSLayoutManager that say that they "Performs glyph generation and layout if needed," such as glyphRangeForTextContainer(), ensureLayoutForCharacterRange() and the like, but they do not appear to do what I need.
I have also tried adding the button at other places in the code, such as in viewWillAppear() and viewDidLoad(). However, when I try here, even when I have called a layout-forcing method, when I call boundingRectForGlyphRange(), it returns coordinates for the NSTextAttachment image that are not accurate to where the image actually ends up in the textView, and so the button ends up in a wildly inaccurate location.
I have no doubt that I am missing something crucial in how text layout occurs, but I am at a loss for what it is. Any help would be deeply appreciated!