Adding an uneditable text suffix to a UITextField - iphone

I have a UITextField that I'd like to add a "?" suffix to all text entered.
The user should not be able to remove this "?" or add text to the right hand side of it.
What's the best way to go about this?

Use the UITextFieldDelegate protocol to alter the string whenever the field is being edited. Here's a quick stab at it; this will need work, but it should get you started.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString * currentText = [textField text];
if( [currentText characterAtIndex:[currentText length] - 1] != '?' ){
NSMutableString * newText = [NSMutableString stringWithString:currentText];
[newText replaceCharactersInRange:range withString:string];
[newText appendString:#"?"];
[textField setText:newText];
// We've already made the replacement
return NO;
}
// Allow the text field to handle the replacement
return YES;
}

You'll probably need to subclass UITextField and override its drawText: method to draw an additional "?" character to the right of the actual text. (Rather than actually add a "?" to the text of the view.

I had this issue and I wrote a subclass to add this functionality: https://github.com/sbaumgarten/UIPlaceholderSuffixField.
Hopefully you have found a solution by now but if you haven't, this should work.

I realize this answer is late, but I found most of these did not work for my scenario. I have a UITextField that I simply want to force to have a suffix that the user cannot edit. However, I don't want to subclass UITextView, modify how it handles drawing, etc. I just want to prevent the user from modifying the suffix.
First, I ensure the suffix is set in the textfield when editing takes place. This could be done any number of ways depending upon your scenario. For mine, I wanted it there from the start, so I simply set the textfield's text property equal to the suffix when the view loads and store off the length of the suffix for later. For example:
- (void)viewDidLoad {
[super viewDidLoad];
myTextField.text = "suffix";
_suffixLength = myTextField.text.length;
}
Then I used the UITextFieldDelegate protocol as Josh suggested above, but use the length of the string and the range to ensure nothing edits the suffix:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
// Determine starting location of the suffix in the current string
int suffixLocation = textField.text.length - _suffixLength;
// Do not allow replacing anything in/past the suffix
if (range.location + range.length > suffixLocation)
{
return NO;
}
// Continue with delegate code...
}
This should work for any suffix value you assign to the textfield.

For a single-line UITextField you should be able to measure the size of the NSString (it has a measurement function in there, somewhere) and move a UILabel to the right position.

I would add a method that is called when edit finishes:
`- (void)editDidFinish {
NSString* str=[[NSString alloc] init];
str=myEdit.text;
[str stringByAppendingString:#"?"];
myEdit.text=str;
}`

OK, im definitly too late, but maybe i can help someone out either way:
The intended way to accomplish this is by using a custom NSFormatter. Heres the docs:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSFormatter_Class/Reference/Reference.html
The basic idea is this: you create a subclass of NSFormatter, and the override at least the two worker methods:
-stringObjectForValue:
this will produce the dipsplay-String from the value stored in the object (i.e. add your questionmark here)
-objectValue:ForString:errorDescription
here, you need to transform the display-string into an object you want to store, i.e. remove the questionmark
The formatter can then be used to convert the data from the stored objects into strings that are suitable for presentation to the user.
The big advantage is that you can use formatters wherever your string will appear in the UI. It is not limited to certain UI-Elements like the solution where you override -drawText in UITextField. Its just hella convenient.

This class method I have written in Objective-C, helps you to add a suffix text to a UITextField.
I order to make it work, you need to initialize the UILabel to the prefix or suffix in your UITextFieldLabel as follow:
myTextField.rightView = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 0.0f, myTextField.frame.size.height)];
myTextField.rightViewMode = UITextFieldViewModeAlways;
[MyClass UpdateUITextFieldSuffix:myTextField withString:#"My Suffix!"];
Once we have the UILabel attached to the UITextField, you can use this class method to update the text, and this text will be automatically resized to fit in the field.
+ (BOOL)UpdateUITextFieldSuffix:(UITextField*)textField withString:(NSString*)string
{
BOOL returnUpdateSuffix = NO;
if (string != nil && [string respondsToSelector:#selector(length)] && [string length] > 0)
{
NSObject *labelSuffix = textField.rightView;
if (labelSuffix != nil && [labelSuffix respondsToSelector:#selector(setText:)])
{
[(UILabel*)labelSuffix setTextAlignment:NSTextAlignmentRight];
[(UILabel*)labelSuffix setText:string];
[(UILabel*)labelSuffix setBackgroundColor:[UIColor redColor]];
{
NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
((UILabel*)labelSuffix).font, NSFontAttributeName,nil];
CGRect frame = [((UILabel*)labelSuffix).text boundingRectWithSize:CGSizeMake(0.0f, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attributesDictionary
context:nil];
CGSize size = frame.size;
CGRect newFrame = [(UILabel*)labelSuffix frame];
newFrame.size.width = size.width;
[(UILabel*)labelSuffix setFrame:newFrame];
[(UILabel*)labelSuffix setNeedsLayout];
[(UILabel*)labelSuffix layoutIfNeeded];
}
returnUpdateSuffix = YES;
}
}
return returnUpdateSuffix;
}

I have written the following method to achieve the above task of placing non-editable suffix to UITextField:
- (void)setSuffixText:(NSString *)suffix
{
UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
[label setBackgroundColor:[UIColor clearColor]];
[label setFont:[UIFont fontWithName:self.tfdDistance.font.fontName size:self.tfdDistance.font.pointSize]];
[label setTextColor:self.tfdDistance.textColor];
[label setAlpha:.5];
[label setText:suffix];
CGSize suffixSize = [suffix sizeWithFont:label.font];
label.frame = CGRectMake(0, 0, suffixSize.width, self.tfdDistance.frame.size.height);
[self.tfdDistance setRightView:label];
[self.tfdDistance setRightViewMode:UITextFieldViewModeAlways];
}​

Related

How can I read UITextField value in an IBAction. I'm creating UITextField programmatically

How can I read UITextField value in an IBAction? I'm creating UITextField programmatically. So I can't set #property and #synthesize using Xcode. The code to generate UITextField is as follows:
for(i=0; i<[fieldName count]; i++)
{
UITextField *name = [fieldName objectAtIndex:i];
frame = CGRectMake(fromLeft, fromTop, totalWidth, totalHeight);
name = [[[UITextField alloc] initWithFrame:frame] autorelease];
name.borderStyle = UITextBorderStyleRoundedRect;
name.returnKeyType = UIReturnKeyDone;
//name.placeholder = [fieldName objectAtIndex:i];
name.autocapitalizationType = UITextAutocapitalizationTypeWords;
name.adjustsFontSizeToFitWidth = TRUE;
name.keyboardType = UIKeyboardTypeDefault;
[name addTarget:self action:#selector(doneEditing:) forControlEvents:UIControlEventEditingDidEndOnExit];
[scroller addSubview:name];
fromTop = fromTop + 40;
}
Now I want to read values of each textbox in a button click (IBAction). Can anyone help me please?
You could use something like this to loop through all UITextFields that are subviews of self.view and add their text to a NSMutableArray:
for (UITextField *field in self.view.subviews) {
if ([field isKindOfClass:[UITextField class]]) {
if ([[field text] length] > 0) {
[someMutableArray addObject:field.text];
}
}
}
if your doneEditing: looks like this doneEditing:(id)sender then you can say:
UITextField *field = (UITextField *)sender;
NSString *myText = field.text;
EDIT:
To access a UITextField without setting as an instance variable you need to tag it when you create it:
[textField setTag:1];
then whenever you want to access it you can get it from its parent view by the tag:
UITextField *myTextField = [scroller viewWithTag:1];
NSString *myString = myTextField.text;
in your case, set the tag to i+1 for example to have all the textfield with unique tags.
implement the IBAction function like the following:
-(IBAction) doneEditing:(UITextField*)sender
{
NSString * val = sender.text;
}
try using the UITextFieldDelegate , i think its better for your case.
add each UITextField a Tag and by that you will recognise the UITextField.
NSString *value = sender.text;
If inside an IBAction, of course.
Which ignores that there is a set of text fields, and a single button action.
The simplest solution to the overall problem would be to store a set (or array) of text fields in an instance variable, and iterate over that set in the button action. But that is a rather coarse approach; it is probably better to use the text field delegate method and store text values directly in an array, using the button to trigger the save.
In addition, Apple HIG would tell you that you should update your data model as the text fields are edited, rather than use a "Save" button - which is poor UX design - unless, of course, the values of individual fields can interact.

UILabels not overlaying correctly in UITextView

I'm trying to add syntax highlighting to my program.I'm using this code to add highlights:
-(void)highlightWord:(NSString *)word: (UIColor *)color {
int amount = textDisplay.text.length;
NSString *newString = textDisplay.text;
NSUInteger count = 0, length = amount;
NSRange range = NSMakeRange(0, length);
while(range.location != NSNotFound)
{
range = [textDisplay.text rangeOfString: word options:NSLiteralSearch range:range];
if(range.location != NSNotFound)
{
range = NSMakeRange(range.location + range.length, length - (range.location + range.length));
count++;
}
}
while (count != 0) {
count--;
NSRange highlight = [newString rangeOfString:word];
newString = [textDisplay.text stringByReplacingCharactersInRange:highlight withString:#" "];
UILabel *view1 = [[UILabel alloc] initWithFrame:[self frameOfTextRange:highlight inTextView:textDisplay]];
view1.text = word;
view1.textColor = color;
view1.font = [UIFont fontWithName:#"System" size: 14.0];
view1.backgroundColor = [UIColor clearColor];
view1.textAlignment = UITextAlignmentLeft;
[textDisplay addSubview:view1];
}
}
- (IBAction)highlighString:(id)sender {
for (UIView *subview in [textDisplay subviews]){
if ([subview isKindOfClass:[UILabel class]]){
[subview removeFromSuperview];
}
}
[self highlightWord:#"test" :[UIColor blueColor]];
[self highlightWord:#"this" :[UIColor redColor]];
[self highlightWord:#"is" :[UIColor grayColor]];
[self highlightWord:#"a" :[UIColor greenColor]];
}
#end
But this seems to cause a weird results:
The desired result would be that it overlays to colored label seamlessly.
Mixing UILabel and UITextView is unlikely to give you easy copy and paste. It's an interesting idea, but I'd be surprised if it worked seamlessly. The usual Apple solution for this problem in all non-beta versions of iOS is to use a UIWebView, horrible as that is. Cocoanetics has one called DTRichTextEditor which I haven't reviewed, but based on the quality of their work on DTCoreText (which I'm very impressed with), I have high hopes for it for iOS 5. Betas are under NDA, but you may be interested in watching Session 220 from WWDC 2012 for more future-looking information.
Your specific bug is that you're adding the labels as subviews to the text view. You should put them on top of the text view as peers. Putting them inside the textview makes you subject the text view's drawing system, which is biting you. Of course when you put them on top of the text view, that's going to make it hard to handle copy and paste. You'll need to pass the touches through somehow, and then draw the selection correctly. See the previous paragraph.
If a fully editable rich-text view were easy to hack up with some labels, then you'd find several implementations on GitHub. Unfortunately it's actually pretty tough to do well. I bow to Cocoanetics for taking it on; I gave up pretty quickly. If you can simplify your problem to a non-general text view, then it may be possible this way, but I'd recommend investigating DTRichTextEditor or a UIWebView. See Session 511 "Rich Text Editing in Safari on iOS" from WWDC 2011.

Weird UITextView behavior

I have a UITextView which i create in the code:
myView = [[UITextView alloc] initWithFrame:CGRectMake(10,5,220,50)];
myView.editable = YES;
myView.font = [UIFont fontWithName:#"Helvetica" size:16];
myView.bounces = NO;
myView.delegate = self;
I set UIView *myView in the header as well..
and also set delegate - UIViewController UITextViewDelegate>
I want to hide a label every time the user writes a text
for that i check if its equal to 0, else its hide.
the problem is in this method -
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if(textView.text.length == 0)
descLabel.hidden = NO;
else
descLabel.hidden = YES;
return YES;
}
the program runs but when i enter the first letter the label still there,
and when i enter the second letter it goes as it should. then i delete those two and its still gone! (reminder: i want the label to be hidden when the length is 0) but when i press on the delete again i can see the label. Pretty weird..
I want to have the behavior of a placeholder in UITextView but this problem annoying
Thanks for your help!
You are checking the textView.text value before the change; you presumably want to check the length after the update would be applied.
e.g.:
NSString *newString = [textView.text stringByReplacingCharactersInRange:range withString:text];

UITextView insert text in the textview text

I want to have to occasionally insert text into the UITextView text object. For example, if the user presses the "New Paragraph" button I would like to insert a double newline instead of just the standard single newline.
How can I go about such? Do i have to read the string from UITextView, mutate it, and write it back? Then how would I know where the pointer was?
Thanks
Since the text property of UITextView is immutable, you have to create a new string and set the text property to it. NSString has an instance method (-stringByAppendingString:) for creating a new string by appending the argument to the receiver:
textView.text = [textView.text stringByAppendingString:#"\n\n"];
UITextview has an insertText method and it respects cursor position.
- (void)insertText:(NSString *)text
for example:
[myTextView insertText:#"\n\n"];
Here's how I implemented it and it seems to work nicely.
- (void) insertString: (NSString *) insertingString intoTextView: (UITextView *) textView
{
NSRange range = textView.selectedRange;
NSString * firstHalfString = [textView.text substringToIndex:range.location];
NSString * secondHalfString = [textView.text substringFromIndex: range.location];
textView.scrollEnabled = NO; // turn off scrolling or you'll get dizzy ... I promise
textView.text = [NSString stringWithFormat: #"%#%#%#",
firstHalfString,
insertingString,
secondHalfString];
range.location += [insertingString length];
textView.selectedRange = range;
textView.scrollEnabled = YES; // turn scrolling back on.
}
For Swift 3.0
You can append text by calling method insertText of UITextView Instance.
Example:
textView.insertText("yourText")
This is a correct answer!
The cursor position is also right.
A scroll position is also right.
- (void) insertString: (NSString *) insertingString intoTextView: (UITextView *) textView
{
[textView replaceRange:textView.selectedTextRange withText:insertingString];
}
- (void)insertStringAtCaret:(NSString*)string {
UITextView *textView = self.contentCell.textView;
NSRange selectedRange = textView.selectedRange;
UITextRange *textRange = [textView textRangeFromPosition:textView.selectedTextRange.start toPosition:textView.selectedTextRange.start];
[textView replaceRange:textRange withText:string];
[textView setSelectedRange:NSMakeRange(selectedRange.location + 1, 0)];
self.changesDetected = YES; // i analyze the undo manager in here to enabled/disable my undo/redo buttons
}

UIKeyboard turn Caps Lock on

I need my user to input some data like DF-DJSL so I put this in the code:
theTextField.autocapitalizationType = UITextAutocapitalizationTypeAllCharacters;
But unfortunately what happens is the first to letter type in CAPS but then letter immediately after typing the hyphen will be in lower case and then the rest return to CAPS therefore producing output like this (unless the user manually taps the shift button after typing a hyphen): DF-dJSL
How can I fix this?
Many Thanks
You don't mention which SDK you're using, but against 3.0 and above I see your desired behaviour.
That said, you could always change the text to upper case when they finish editing using the textFieldDidEndEditing method from the delegate:
- (void)textFieldDidEndEditing:(UITextField *)textField {
NSString *textToUpper = [textField.text uppercaseString];
[theTextField setText:textToUpper];
}
Or, by setting up a notification on the textfield when it changes, you could change the text as it is being typed:
// setup the UITextField
{
theTextField.delegate = self;
theTextField.autocapitalizationType = UITextAutocapitalizationTypeAllCharacters;
[theTextField addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
}
You have to do it this way since, unlike UISearchBar, UITextField doesn't implement textDidChange. Something like this, perhaps?
- (void)textFieldDidChange:(UITextField *)textField {
NSRange range = [textField.text rangeOfString : #"-"];
if (range.location != NSNotFound) {
theTextField.autocapitalizationType = UITextAutocapitalizationTypeAllCharacters;
}
}