9

I'm looking to create a SpriteKit Node positioned at the location of a substring inside a UITextView. How would I retrieve the CGPoint location so I can position the SKNode there?

    let textFont = [NSFontAttributeName: UIFont(name: "GillSansMT", size: 30.0) ?? UIFont.systemFontOfSize(18.0)]
    attrString1 = NSMutableAttributedString(string: "My name is Dug.", attributes: textFont)
    textShown1 = CustomTextView(frame: CGRectMake(CGRectGetMidX(self.frame), 175 + (90 * paragraphNumber), CGRectGetWidth(self.frame) - 80, CGRectGetHeight(self.frame)-400))
    textShown1.attributedText = attrString1

    self.view?.addSubview(textShown1)
nielsbot
  • 15,922
  • 4
  • 48
  • 73
Chris
  • 471
  • 1
  • 8
  • 23
  • I don't see any "substring" in your question. What are you trying to do? – matt Feb 12 '15 at 02:24
  • sorry, I threw that up as an example. I'd like to be able to find the coordinate location of where say the "n" in the attributed string "name" is inside the textView. reason being, i want to create an SKEmitter particle node at that location. – Chris Feb 12 '15 at 02:53
  • What's `CustomTextView`? Is it a subclass of `UITextView`? If so, does it modify the way `UITextView` draws text? – rob mayoff Feb 12 '15 at 02:56
  • So, you can readily do this with Text Kit. It tells you where all the pieces of the text are. – matt Feb 12 '15 at 03:00
  • This is not exactly an example of what you're trying to do, but it illustrates how to work with the geometry of the text inside a text view: https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/bk2ch10p541textLayoutGeometry/ch23p811selfSizingTextField/ViewController.swift – matt Feb 12 '15 at 03:02

1 Answers1

19

You can use firstRectForRange(_:) method on UITextView

let textFont = [NSFontAttributeName: UIFont(name: "GillSansMT", size: 30.0) ?? UIFont.systemFontOfSize(18.0)]
let attrString1 = NSMutableAttributedString(string: "My name is Dug.", attributes: textFont)

// range of substring to search
let str1 = attrString1.string as NSString
let range = str1.rangeOfString("name", options: nil, range: NSMakeRange(0, str1.length))

// prepare the textview
let textView = UITextView(frame:CGRectMake(0,0,200,200))
textView.attributedText = attrString1

// you should ensure layout
textView.layoutManager.ensureLayoutForTextContainer(textView.textContainer)

// text position of the range.location
let start = textView.positionFromPosition(textView.beginningOfDocument, offset: range.location)!
// text position of the end of the range    
let end = textView.positionFromPosition(start, offset: range.length)!

// text range of the range
let tRange = textView.textRangeFromPosition(start, toPosition: end)

// here it is!
let rect = textView.firstRectForRange(tRange)
rintaro
  • 51,423
  • 14
  • 131
  • 139
  • Thanks. Worked like a charm. Could I ask why I need to ensure layout? – Chris Feb 12 '15 at 07:20
  • When you modify any properties of the `UITextView`, it doesn't have valid text layout *until next draw loop*. `ensureLayoutForTextContainer()` can forces that. – rintaro Feb 12 '15 at 07:55
  • thanks again, if i could ask one more q - I'm trying to find the position of the rect in the global view. I'm adding the containing UITextView's rect's frame.minY & minX, to the x & y values of the string's rect. The x works, y is way off. any thoughts on why? – Chris Feb 15 '15 at 00:29
  • whoops, forgot to mention. @rintaro – Chris Feb 18 '15 at 19:42
  • I think it's because of coordinate system mismatch between UIKit and SpriteKit. Maybe this answer might helps you: http://stackoverflow.com/a/27158803/3804019 – rintaro Feb 19 '15 at 07:24
  • firstRect in iOS 9 return 0 but works in iOS 10. Any fixes? – PoolHallJunkie Nov 17 '16 at 12:50
  • it doesn't work well if if my string located on the 2nd line or more. Anyone got issues like me? – calvin sugianto May 31 '17 at 03:22
  • This does not work unless you swap the offset of start and end. range.location should be range.length and vice versa – Flupp Mar 13 '18 at 15:14