characterIndexAtPoint in IOS - ios

I'm new to objective-c, and I've done this in Flex/Actionscript, but hitting a wall with IOS. Is it possible to get the index of a character under a point(x,y) in a TextField or Label and then be able to modify that single character in IOS?
I have a TapGestureRecognizer coming in with a point in my view. I need to now be able to isolate and highlight just the character at the point coming in.
For example if I have the text "Hello" on screen and I ask them to click on the 'e', I need to know which letter they touch and highlight it green/red depending on if they were right or not.

UITextField (and UITextView) conforms to the UITextInput protocol which has the method characterRangeAtPoint:.
Once you have the CGPoint from the gesture, use this method on the text field to get a UITextRange. Then use the textInRange: method to get the text associated with that range.

Related

Does NSAttributedString's enumerateAttribute method's block pass character ranges or glyph ranges?

tl;dr: if I call NSAttributedString's enumerateAttribute:inRange:options:usingBlock: method, does the range argument to the block pass a glyph range or a character range?
I have a text view in my app that needs to behave like Notes.app in that when it's not in edit mode, it should show hyperlinks (and they should be tappable), and it should enter edit mode on tap. UITextView doesn't show hyperlinks when editable, but if it's not editable then it won't gain focus on tap by itself. Until iOS 11 this could be solved by using a UITapGestureRecognizer on the text view to trigger editing, but it seems something (maybe with drag and drop?) changed how gesture recognizers work with UITextView (rdar://33009324).
My new and improved solution is a custom UIGestureRecognizer that fires if it sees a tap that is not on a hyperlink, and making all other recognizers on the text view to require it to fail before firing. This is implemented by processing touches, getting their location in the text view, getting the character index for that point, enumerating the NSLinkAttributeName in the text view's textStorage, and checking if my character index overlaps with any of the hyperlinks I get back. If so, then the user tapped on a link, and this recognizer fails.
However, if the character index of the tap does intersect with a link, I need to make sure the tap actually intersects with the link's rect, because it could be that this link is the last thing in the text view's content and the touch is actually below the last line of text. I can do this by getting the rect for the range of the link and checking if it intersects with my touch point. I can use boundingRectForGlyphRange:inTextContainer: for this, but this leads to my question: is the range I have for the link a character range or a glyph range? Do I need to convert it via glyphRangeForCharacterRange:actualGlyphRange: first?
It returns a character range. NSAttributedString doesn't know anything directly about glyphs. Converting characters to glyphs can't happen until layout is performed, and NSAttributedString doesn't include layout information. For example, an NSAttributedString doesn't know how wide an area it will be drawn into, and without that, you can't know where line breaks will be, and without that you can't decide where to put ligatures.
NSAttributedString knows nothing glyphs, and yes, if you need to know where the glyphs are in a TextKit stack such as that of a UITextView, you ask the layout manager to convert for you.

Allow cursor selection anywhere in UITextView

I have a single UITextView, which lets you write multiple lines of text.
How can I let users select letters in between words without a long press?
Currently, when using the touchscreen to select characters between a word, the cursor either goes the start or end of the word:
Example: clicking on 'c' moves cursor to end of word
Example: clicking on 'a' moves cursor to start of word
Desired cursor behavior: Selection anywhere in text
The feature of allowing text selection anywhere (without the long press + magnifying glass) is found in many code editor apps for iOS, such as Coda and Pythonista.
This is not a difficult challenge to achieve.
I will preface that the issue with this approach is consistency. Remember that your users are using their fingers, rather than stilii. A finger has a large surface area, and hitting a precise pixel zone is quite difficult, especially with small text. I would suggesting playing not in the simulator, where you have a precise pointing device, but on a device.
The first challenge is to disable the default tap behavior or UITextView. This is not a difficult task - you can either "attack" the problem at the touchesBegan:withEvent: level, where you would have to understand what these touches are (single tap vs. pan vs. long press), or at the gesture recognizer level, where you'd disable the private gesture recognizers of the text view which specifically handle cursor movement in case of tap (vs the other touch types). I've done the latter for various projects, and it is possible. You could also try the approach without disabling the default behavior, but then the cursor may flicker. Test and decide.
Now to achieve what you need. Obtain the point of touch somehow (either using UIResponder API or gesture recognizer). Remember, the text view is a scroll view which includes a large subview where the content is drawn into. You have to convert this touch point, from the text view coordinate system, to the internal view's coordinate system using the convertPoint: API. Once you have that, you can use the text view's layout manager to obtain the character index at the point of touch:
NSUInteger chIdx = [self characterIndexForPoint:touchPoint inTextContainer:self.textContainer fractionOfDistanceBetweenInsertionPoints:NULL];
You can use this index to set the cursor of the text view using the selectedRange property.

iOS place cursor after word in UITextView

I have a string in a UITextView.
NSString *str = #"Hello world. What #are you #doing ?"
When I tap on the UITextView the cursor goes where I tap on the string. But I need that, suppose I tap on any character of that textview, the cursor will automatically goes to the end of that word. For e.g, I tap on the character "e" in "Hello", then the cursor will place after "Hello".
How can I do this?
You have to understand the concept and underlying structure of a touch device.
Let me explain.
When you have a textfield/ textview, inside a view and your keyboard is not showing ie. textfield is inactive. The superview might have any number of gesture recognisers other than the textfield.
Hence the OS just detects the touch which will make the UI element become firstResponder, or rather Active.
After the textfield is active, OS recognises that the other gesture pattern will come into play in this moment. And in this moment you can tap in any position (or Long tap) and then place ur cursor at any character you want.
Hope you could understand.
this should be the default behavior of textview. You not need to do any additional setup for that! If you will long press then only your cursor go at particular character otherwise it will go to end of the word. this is the default behavior.

iOS Instagram Tags Text Layout? How Is It Done?

I've always been curious how instagram gets it's tags to layout fluidly and wanted to know what goes behind this functionality? To me, it seems like they're using UIButton's and just calculating placement of the buttons line by line? Is that right? Or is there a simpler method of doing it without having to manual calculate the placement of each tag line by line / or side by side given that multiple tag's width's fit together?
Or, are they using NSAttributedStrings?
Any ideas?
Here's an example of the layout I'm referring to.
Well, I'm currently working on NSAttributedString and UITextView to get all this working, and my current code can do it. So I'm going to explain a possible version of doing this.
Here are the tips and big steps, with iOS7:
NSAttributedString, in iOS7 has a new attribute : NSLinkAttributeName. You can add it to your NSAttributedString, and with a detection method (combining: how to make #key and #key clickable in IOS7 and Get word from tap in UITextView) you can manage it. That's for the general way of doing it. For previous version, you'll have to do a little more of work, but keeping this general idea.
But since, it only for hashtags, since you can detect the world behind the tap, you can do this: Detect what word has been tapped (see previous links), detect if this word has an hashtag, and if yes, you can do whatever action you want. You can play with a NSRegularExpression to know where are the hashtags, and know when to "color" them.
Here is a simplified way to do it using UITextView and NSAttributedString:
Use regular expressions to get character ranges for links.
Add link styling to the matched ranges.
Add custom attribute to range that references tap action to execute.
On touch, if index of character is inside a matched range, change the link formatting to highlight it and run the associated tap action.
How to get index of character at location in iOS7:
- (NSUInteger) getCharIndexAt: (CGPoint) location
{
NSLayoutManager *layoutManager = self.layoutManager;
NSUInteger characterIndex = NSNotFound;
characterIndex = [layoutManager characterIndexForPoint:location inTextContainer:self.textContainer fractionOfDistanceBetweenInsertionPoints:NULL];
return characterIndex;
}
Also check out WWDC 2013 intro to TextKit Demo
ios7 text utilities

Can you detect if a textfield has highlighted text

I was wondering if there was a way to detect if a textField.text is currently highlighted. I am trying to format a phone number, and it works under the exception that the user highlights the field, then starts typing a new number immediately instead of clearing it first. The first ( does not get set because i try to detect if the field is length 0 before adding it. On a text highlight, then keypress, the length is larger than 0 so it doesnt work.
thanks
Yes, since UITextField (and I believe UITextView) adopt the UITextInput protocol, you can send messages to those with any of that protocols methods, included selectedTextRange. See UITextInput Protocol Reference.

Resources