Add tap gesture recogniser on a specific word in a UITextView - ios

I have a UITextView with some text. This text is an Attributed String(NSAttributedString). There are certain portions of the text that i have set to bold, and want to add a TapGestureRecogniser to those specific words only.
Till now, i have been using the textViewDidChangeSelection method of the UITextView delegate. But this is causing issues in other parts of the project.
Is there a more direct approach to this ?

You can only add a GestureRecognizer to a view, not to some words.
It's a quite complex task, there's no easy solution for it.
I can think in some approaches, for example:
Place a transparent view on top of the bold words to get the Tap.
Detect the Tap in all the UITextView, and then calculate based on the position of the touch and the position of the bold words if it hit one.
Both options requiere a lot of code and a lot of edge cases where it can fail.
As I said, it's a really complex situation, you may want to keep using textViewDidChangeSelection and fix the issues, we can help you.

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.

Cursor issue while creating Custom keyboard with UIView

My requirement was to make a application specific keyboard i.e with a different theme and different key contents as compared to the default keyboard. But the way Apple states to implement keyboard requires user effort to add it through Settings->General->Keyboard which I don't want.
So, for that I tried to implement it using a UIView and making it the inputView for UITextView and it is working perfectly fine except for one issue. I added a functionality to add a linebreak in the textView. For that the code I used is
textView.text = "\(textView.text)\n"
Now, whenever I add a linebreak through keyboard, line is changed, but a new cursor appears to blink above the old cursor. I don't know how to get rid of that.
To explain the problem in a better way, here's a GIF image of the issue.
Please help.

How to get Range value of text in a UITextView from tapped location?

I have a UITextView with some content of text. I need to get the exact range value of character/string from tapped location. I set TapGestureRecognizer for UITextView. In its action method i need to get the Range of tapped location. How its possible? I tested with some answers from Stackoverflow but they were not perfect. Thanks in advance.
Have you looked at the UITextView documentation?
https://developer.apple.com/library/ios/documentation/uikit/reference/uitextview_class/Reference/UITextView.html#//apple_ref/occ/instp/UITextView/selectedRange
You can use this selectedRange method to determine the range of text that is selected by the user.
Late edit: this was just recently downvoted, and gave me a chance to re-read my answer and I'd downvote my answer from 3 years ago too.
Updated answer:
If there is a single tap, the tap action itself should have the info you need. At the core here, you'll need the UITouch events themselves, such as to calculate where the touch occurred in relation to where your text should be. This will let you know what part of text was covered by the user's finger.
I'm honestly not sure if any 3rd party solutions exist for this, but I'd bet cocoapods.org would be a good place to start with keyword UITextView tap, if not Github itself with keywords: cocoapod uitextview.
If I were implementing such a solution, with UITouch events in hand, using the properties described in the docs I'd use The location of the touch within the view or window
given by the class.
By manipulating on one hand CGPoints from: https://developer.apple.com/documentation/uikit/uitouch/1618116-location, you can then use String mechanisms for determining size of text in your given font.
While this solution is for a UILabel, it can either:
(a) be used in a different way but hopefully quite similarly in a UITextView
OR
(b) I have seen a dummy UILabel get used simply to calculate text sizes in features requiring this kind of precision. This is usually quite effective, since changing the string of a UILabel isn't expensive and a UILabel has built in properties for size calculation based on text and font without needing to do a layout pass.
Again, apologies if the first original answer seemed sassy, it does come off that way I see now. I will confirm, though, that my original answer still stands (the selectedRange mechanism) SHOULD the desired solution be in terms of a long press. For a custom solution based on a single touch down event, though, collecting UITouch events through a delegate callback and then processing those with knowledge of what String is present is the best approach from what I can tell.

UITextView scrolls up after "Speak"

I have a non-editable UITextView to display some text. Users can select the text in this UITextView and choose the iOS "Speak Selection" functionality (speak button) to read it for them. However, when 'Speak' is done reading the last word, it scrolls up the UITextView. In fact, even if I select just the last word in the text, and choose 'Speak', it scrolls up the UITextView.
I have scrollEnabled set to NO, editable set to NO, and the text is a NSAttributedString.
How can I stop the UITextView to scroll up in this case?
I can't comment with my reputation, unfortunately I don't have a real answer but a workaround; so far mine is to intercept [UITextView setContentOffset:animated] via method swizzling (http://nshipster.com/method-swizzling/) and avoid calling the original method when needed (this method is called by QuickSpeak function). I guess subclassing would be cleaner if you can instantiate the view yourself (this is not my case).

Is it possible to 'hyperlink' text within a UILabel/TextView but activate a segue on tap of this 'hyperlink'?

I say 'hyperlink' because I don't know what else to call it, and that is how i'd like it to appear.
Obviously this is possible using a combination of labels and buttons, but my labels and buttons are programmatically generated and I imagine i'd have to also programmatically arrange them, which would likely be tedious and inflexible in terms of changing font sizes etc.
Any ideas/approaches would be much appreciated!
As an example, look at Instagram's following and news feed:
You should set userInteractionEnabled and then add a UITapGestureRecognizer to the label.
Have a look at Nimbus Attributed Label it can provide the functionality you are looking for.

Resources