I am trying to achieve the same effect as Facebook's new status field: place something next to the last character which cannot be modified or selected by the user (for example, a tag: "with Joh Doe").
What's the best way of achieving it?
Thanks
Nick
An easier solution would be to simply make the last n characters in the TextView not editable. Then let the TextView handle moving and wrapping the text as needed. I would guess that is what Facebook is doing, just with some attributed text at the end.
In your ViewControllers.h, make sure the viewController conforms to the UITextViewDelegate as follows.
#interface ViewController : UIViewController <UITextViewDelegate>
Then in the viewDidLoad: method, set the delegate for the UITextView to the ViewController. You can also add your fixed text.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.myTextView.delegate = self;
self.myTextView.text = " posted by Mike";
}
Then, we will use the shouldChangeCharactersInRange: method to prevent the user from editing the text at the end you want to preserve.
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
BOOL deleteKeyPressed = ([text length] == 0 && range.length > 0);
if( range.location > (textView.text.length - #" - posted by Mike".length) || (deleteKeyPressed && range.location > (textView.text.length - #" - posted by Mike".length)) )
{
return NO;
}
return YES;
}
I think this should get you started and make you life easier than finding the end of the last text, then putting another view at the end that may need to wrap if there is not enough room to fit it.
So to prevent the user from selecting the "reserved" text, add the following delegate method to your ViewController's .m:
-(void) textViewDidChangeSelection:(UITextView *)textView
{
if( textView.selectedRange.location > (textView.text.length - #" - posted by Mike".length) )
{
[textView setSelectedRange:NSMakeRange((textView.text.length - #" - posted by Mike".length), 0 )];
}
}
You'll still need to handle if the user selects all the text, and chooses to "cut" or "paste", but that should just be another special case in shouldChangeTextInRange. I'll let you figure that one out - shouldn't be hard. Certainly a lot easier than trying to dynamically place, size, and wrap a TextView within another TextView.
Related
I added a feedback box inside my IOS application, and I want it to take only text to submit the response from the user, but when I tried to enter a white spaces inside the box it took it as a text and accept the submitting! How can I prevent that?
Specify the UIViewController as the delegate to your text view (you can do this either programmatically or specify the delegate in Interface Builder); and
Your UITextViewDelegate method shouldChangeTextInRange needs to check to see if the string to be inserted contains a space:
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if ([text rangeOfCharacterFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].location != NSNotFound) {
return NO;
}
return YES;
}
When using a UITextView to gather user input in Objective-C, how can I limit the user from trying to do more than one line break at a time?
So, this would be fine:
This is my text.
Here is some more text.
But this would not be fine:
This is my text.
Here is some more text way down here.
In your ViewController.h add UITextViewDelegate:
YourViewController : UIViewController <UITextViewDelegate>
and in the ViewController.m implement the method textView:shouldChangeTextInRange:replacementText: this way:
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if ([text isEqualToString:#"\n"]) {
NSMutableString *futureString = [textView.text mutableCopy];
[futureString insertString:text atIndex:range.location];
NSRange rangeOflineBreaks = [futureString rangeOfString:#"\n\n\n"];
if (rangeOflineBreaks.location != NSNotFound) {
return NO;
}
}
return YES;
}
This will be executed every time before the user wants to add some text to the textView and wont let him add another line break if it notices that he wants to add a line break and it finds, after trying adding it (that's why the futureString name) a triple line break mode. In that case he wont let the user add another line break.
Try it out, it should work :)
PS: Don't forget to set your textView delegate the viewController in the viewDidLoad (yourTextView.delegate = self)
This is my First question on ios
I am using two UITextView objects (textView1 and textView2) in a View, Each of them has some character limit with some following Scenario:
Initially user can only enter into textView1.
When the entered character limit of textView1 is over, the cursor will automatically shift to textView2.
After building the project, If user tap the textView2 and try to write into it, Cursor must shifted to textView1 (because it is empty).
I wrote the code and everything works fine except the third scenario, User can only enter into textView1 but focus is still on textView2
Steps to reproduce:
Build the project
user tap the textView2 first and try to write something.
According to written code, Focus remain in textView2 but user are writing into textView1 (see the attachment)
Here is the snapshot:
Here is the written code:
- (void)viewDidLoad
{
[super viewDidLoad];
[self.textView1 becomeFirstResponder];
}
- (void)textViewDidChange:(UITextView *)textView{
NSInteger restrictedLengthForTextView1 = 110;
NSInteger restrictedLengthForTextView2 = 130;
NSString *temp=textView.text;
if(textView == self.textView1){
if([[textView text] length] > restrictedLengthForTextView1){
textView.text=[temp substringToIndex:[temp length]-1];
[textView resignFirstResponder];
[self.textView2 becomeFirstResponder];
}
}else{
if([[textView text] length] > restrictedLengthForTextView2){
textView.text=[temp substringToIndex:[temp length]-1];
[self.textView2 resignFirstResponder];
}
}}
- void()textViewDidBeginEditing:(UITextView *)textView{
NSInteger restrictedLengthForTextView1 = 110;
NSLog(#"dalknwdlakwd");
if([[self.textView1 text] length] < restrictedLengthForTextView1){
if(textView == self.textView2){
[self.textView2 resignFirstResponder];
[self.textView1 becomeFirstResponder];
}
}}
Please help me here..
please do as per following:
in .h file
#interface ViewController : UIViewController<UITextViewDelegate>
{
IBOutlet UITextView *txtView1;
IBOutlet UITextView *txtView2;
}
#end
in .m file
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[txtView1 becomeFirstResponder];
txtView2.editable=NO;
}
implement the textView delegate method like below:
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if(textView.tag==1)
{
if([textView.text length]>25)
{
txtView2.editable=YES;
[txtView2 becomeFirstResponder];
}
}
return YES;
}
i have taken text length limit in first textview as 24 characters as an example.
i hope this will help you.
It is a known bug, with resigning and becoming first responder within the same runloop. Try the following
[textView2 resignFirstResponder];
[textView1 performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:0.0];
An update to Annadurai's answer: it's still a bug in 2016! After trying others' suggestions to deselect the surrounding tableview cell, and/or change the tint color only this answer worked for me:
[textView1 performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:0.0];
FYI My case was that I was resigning a textView, reloading the table, and returning to the same textView. Before this fix - no cursor on the reloaded textView (Yes I could type and see characters.) After the performSelector... afterDelay - the cursor was visible again.
I have two UITextViews! The problem i have is that I want an action to be performed when one of the textViews is entered and not the other! I placed the action code in
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
But the action is performed when either text view is entered. Is there a method that informs when a certain text view is being used?
You can do it in two ways
first
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
if ( textView == YourTextView )
{
// ----- do here
}
return YES;
}
Second is
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
if ( yourTextView.tag == yourTag)
{
// ----- do here
}
return YES;
}
You can set the tag of your UITextView to differentiate them.
// Somewhere in your code
UITextView *firstTextView = // set up your text view
firstTextView.tag = 0;
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView {
// ...
if (textView.tag == 0) {
// firstTextView
}
// ...
}
Its for this purpose itself textView is passed as an argument to the delegate function
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
You can maintain properties for the textview instances and within the delegate call just check which of the textview received the touch.
short version: How can I make a UITextField box remove all content on the users first keypress? I don't want the info removed until the user starts typing something. ie, clearing it on begin edit is not good enough.
long version: I have three UITextField that loop around (using the return key and catching the press in the "shouldReturn" method. There is text already in the UITextField, and if the user doesn't type anything and just goes to the next UITextField, the value should stay (default behaviour).
But I want it that if the user starts typing, it automatically clears the text first. Something like having the whole field highlighted, and then typing anything deletes the fiels and then adds the user keypress.
"Clear when editing begins" is no good, because the text is immediately cleared on the cursor appearing in the field. That's not desired. I thought I could use the placeholder here, but that doesn't work as a default, and I can't find a default value property. The Highlighted and Selected properties don't do anything in this regard either.
There is a delegate method called
textFieldDidBeginEditing:(UITextField*) tf{
tf.startedEdinting = YES;
}
textFeildDidEndEditing: (UITextField*) tf {
tf.startedEditing = NO;
}
Add startEditing in a category to UITextField.
Then if value changes clear the field:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (textField.startEditing){
textField.text = string;
} else {
textField.text = [textField.text stringByReplacingCharactersInRange:range withString:string];
}
}
You can add the property to the UITextField category in the following way:
.h
#property (nonatomic, assign) BOOL startEditing;
.m
#dynamic startEditing;
- (void) setStartEditing:(BOOL)startEditing_in{
NSNumber* num = [NSNumber numberWithBool:startEditing_in];
objc_setAssociatedObject(self, myConstant, num, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL) startEditing{
NSNumber* num = objc_getAssociatedObject(self, myConstant);
return [num boolValue];
}
Declare a BOOL variable in your .h file like.
BOOL clearField;
And implement the delegate methods like:
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
clearField = YES;
}
-(void)textFieldDidEndEditing:(UITextField *)textField
{
clearField = NO;
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
clearField = NO;
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if(clearField)
{
textField.text = #""
clearField = NO;
}
}
I want to thank people for their answers, I implemented both of the main methods described here and both worked flawlessly. But I have since come across a much simpler, nicer answer and involves only one line of code :)
In the textField's didBeginEditing method, place [self.textField selectAll:self]; or [self.textField selectAll:nil];
The original answer I found had selectAll:self but this shows the cut/copy/paste menu. If you send nil instead of self the menu doesn't appear.
Adding this one line of code highlights the text on entering the textField (so gives the user a visual cue), and only removes everything once a key is pressed.
Another solution that fulfils the same purpose is by simply using a text field placeholder which is defined as:
The string that is displayed when there is no other text in the text field.
So as soon as the user starts typing, the placeholder text disappears.
That's something you can set from the storyboard, or programmatically. (Yes it took me two hours trying to figure it the harder way.. when the solution was literally one line change of code).
If you want to clear the text one the user interacts with it, there is an option in interface builder to where you can set the text field to "Clear when editing begins."
Try to use the following method.
- (BOOL) textField: (UITextField *)theTextField shouldChangeCharactersInRange: (NSRange)range replacementString: (NSString *)string {
if(isFirsttime==YES)
{
textfield.text==#"";
isFirsttime=NO;
}
return YES;
}
Declare and initialize a NSString variable for your textField's initial text
NSString *initialText=#"initial text";
Then implement methods:
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
if(textField.text isEqualToString:initialText)
{
textField.text=#"";
}
}
-(void)textFieldDidEndEditing:(UITextField *)textField
{
if(textField.text isEqualToString:#"")
{
textField.text=initialText;
}
}