How do I bind an NSString to a UITextView in Reactive Cocoa? - ios

I'm building an iOS social client, and in the "compose" view, I've got a UITextView where the user enters text. I'd like to use ReactiveCocoa to bind the text of the UITextView to the NSString of the data model, to follow MVVM.
However, I'm running into several issues, all related to a single thing: the RACObserve block doesn't get called when the UITextView's text is changed programmatically.
(An example: I change the text into an attributed string to highlight #hashtags, #usernames, etc, but this attributed string doesn't get created when the view is programmatically changed.)
In my previous question on this topic, I got some helpful advice that I should bind the textview to the model - and vice versa - but it's not clear to me how I should do it with the current version of Reactive Cocoa. The sample code that I've managed to find calls APIs that are now deprecated.
What's the appropriate way to bind the rac_textSignal of a UITextView to an NSString (and vice versa) so that I can reliably call a block of code when the contents of the UITextView are changed (whether programmatically or by the user)?

The answer depends on whether the binding between the view model's text and the UITextViews text needs to be bidirectional. Generally we try to stay away from bidirectional bindings because they become harder to reason about. Ideally only one direction is driving the data.
So in that case, you'd write something like:
RAC(self.viewModel, text) = [RACSignal merge:#[
[self.textView rac_textSignal],
RACObserve(self.textView, text),
]];
That way you're picking up on changes to both the UITextViews text property directly, and text changes that come from the user typing.

Related

UITextInput not announced correctly by VoiceOver

I have a problem with how VoiceOver announces my custom UITextInput while it has the keyboard focus: When the accessibility focus is moved to the UITextInput view by swiping left/right the UITextInput is correctly announced by VoiceOver and I am hearing something like Text field, is editing, <content of text input>, character mode, insertion point at end.
However, if I move the accessibility focus to the UITextInput by tapping on it, VoiceOver says empty line, which is not correct.
I would expect VoiceOver to make the exact same announcement regardless of how the UITextInput got the accessibility focus.
Any ideas what might be the cause of this strange behavior?
To mark up a text field you need to take advantage of a few different properties. First, the "Text field" part you can only get by having your custom control extend the UITextFIeld class. That's the only way you can get that announcement at the beginning without effecting the read out of other things. You could append this to the label. Here are the properties I would recommend for your custom text editor.
Preferred Markup
ClassType: Subclass of UITextField (Probably the most important piece)
AccessibilityLabel: The thing the entered text represents (Ex: Password, Username, etc).
AccessibilityValue: The entered text.
AccessibilityHint: "Some non critical information, that shares some details about what the entered information will be used for."
AccessibilityTraits: (NOT Static Text Trait)
Note that the hint isn't crucial information. Hints are frequently ignored, and just contain useful clarifying information.
Aleternate markup (NOT RECOMMENDED)
ClassType: Subclass of ????
AccessibilityLabel: Text field, $EditingState, $EnteredText, $InsertionPoint
AccessibilityValue: nil
AccessibilityHint: nil
AccessibilityTraits: StaticText (Weird, but if you're including role information yourself, we want the trait of static text on an editable text field, to avoid VoiceOver being smart, including it, and duplicating role information in the announcement... is this starting to feel like a hack yet???)
This is honestly a terrible idea, you really should just have your TextField inherit from the system UITextField and get all of that stuff for free, but if you must, this is how you wold achieve... similar behavior without doing so. I say similar because there are a few things you CANNOT replicate in a custom control.
keyboard type ("character mode") is more painful for you to grab than the system, and I recommend omitting in the custom case. The keyboard doesn't have to respect your requests for mode changes (custom keyboards and such), you can get into trouble and confuse users attempting to share this based on your application's settings. The system is the only thing that can get this right in all scenarios.
Inflection. By doing this the custom way you will lose the style and inflection from VoiceOver. The pauses between the Label and the Entered Text. The lower voice that is read when the placeholder text is read out, etc. You can add commas to your AccessibilityLabel to somewhat replicate some of this, but ultimately, your custom control will always sound a little "off" to a VoiceOver user... unless your custom view extends UITextField...

Output specific images when user taps the keyboard

I want to output a image when a user taps on the keyboard. Let's say the user taps A on the keyboard in a UITextView. Instead of outputting the normal A, I want to output the picture of an Ape.
If a user taps "S" I want to output the image of a sun. Is this possible with out having to make a customized keyboard?
Besides creating a custom keyboard yourself, there isn't really a neat method to getting an image output.
One thing you could do is program Swift to automatically recognise the letter input and display an image over or replacing the letter as a result.
To do this, assign a variable to your editable UITextView or UITextField. If you are using XCode you can do this by control-dragging the text field into your code and it will automatically create a weak var, but you can change the strength in the pop up box that appears.
Since you now have a variable, there are two options as to how you display the image. One way is rather boring and will clutter your code, but essentially involves this, which I have simplified for the purposes of demonstration:
if UITextInput == a {
// add image
}
You would have to repeat this for every letter of the alphabet.
Or you could create a dictionary in which each letter corresponds to each image, and find a way to cycle through them, depending on the user's input. Even though this is harder to program for a newbie, it makes the code much neater and quicker.
This is the link to the official Apple documentation on dictionaries:
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html#//apple_ref/doc/uid/TP40014097-CH8-ID113
However, since I assume you are programming an app for the younger market, I would recommend creating a custom keyboard as it will be easier in the long run.
Hope this helps,
will

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.

custom input view keyboard functionality

I wanted to ask a quick question just to make sure I am not missing anything simple before I implement a more difficult method. I need to create a custom keyboard for an iPhone application. This I have already done by creating a view with the buttons, using a custom input view and it displays exactly like it should. Now most of the buttons are standard numbers which need to update a UITextField in the screen that called the keyboard. Does anyone know a simple way to do this? I assume there has to be a built in function that the keyboard uses to send the information but I haven't been able to find any reference to it. Otherwise I will have to go the more difficult route. If anyone has a simple way to do this I would appreciate it. I haven't worked with custom keyboards before.
You won't be able to do it the same way that Apple does it, as their keyboard is basically an input device, globally.
I recommend you just append the data in your button press multiplex method. Here's an example:
NSString *appendThisText = #"subtitle";
self.myTextView.text=[NSString stringWithFormat:#"%#%#", self.myTextView.text, appendThisText];
Custom keyboards are simpler than you realise.
UITextField conforms to the UITextInput protocol. That's a bit of a red-herring because this protocol provides all the really complex stuff like selecting text and so on. But UITextInput itself conforms to UIKeyInput. This is your friend.
The key UIKeyInput methods are:
- (void)insertText:(NSString *)text;
- (void)deleteBackward;
Your keyboard class should have a delegate (which points to the textfield that the keyboard is operating on) and you simply call these methods to insert and delete text.

Curious Warning Message on UITextView

Has anyone run across this warning message building for the iPhone?
More importantly do you understand how to fix it?
"unsupported configuration data detection and editable"
It's seems to be the UITextView that is complaining.
Here's a screenshot.
The problem is that you have that textview set both to editable + to detect/autolink phone numbers, events, addresses, etc. a text area can either be editable and not detect/autolink text, or it can autolink text but not be editable.
Your settings for that textview should look like:
or
but not like:
I think in your scenario, the text input is only used to input text, nothing more. Then when it get's presented back, the "presenting text view" will take care of detecting the potential information... dates, events, etc.
To be more precise : in a simple app scenario, a user types in some text (let's say an event input text view - with no detection necessary at this point). Then when it get's eventually presented back to him or another user (let's say the detail view of the event), the text will be presented back in a "non-editable" text view that in turn will be able to have detections.
I know this question is a little old, but this is how I resolved it;
In Interface Builder I have Links Detection selected, and Editable Behaviour not selected.
Then, in my ViewController, I implemented the UITextView - (BOOL)textViewShouldBeginEditing:(UITextView *)textView { } delegate method and return NO.
It removed the warning and prevents the user from being able to edit the UITextView's content.

Resources