I'm having trouble figuring out how to delete attributed text when a user clicks on a textView, much like it does when you do on a textField. I know how to assign the attributed text, but I want it to disappear when a user clicks on the textView, rather than having to delete the attributed text themselves.
Here is the code I'm using to populate:
if([self.detailItem.comments.rootBeerComment isEqual: #""] || self.detailItem.comments.rootBeerComment == nil){
NSAttributedString *string = [[NSAttributedString alloc]initWithString:#"Notes..."];
self.rootBeerNotes.attributedText = string;
}else{
self.rootBeerNotes.text = self.detailItem.comments.rootBeerComment;
}
In order to do something when the textView is tapped, you need to know when this event happens. The easiest way is to set the delegate of the textView to your view controller, and then implement the UITextViewDelegate delegate method which tells you this:
- (BOOL)textViewShouldBeginEditing:(UITextField *)textView
{
// You may need to check that it is the right textView if you have more than one.
textView.attributedText = nil;
return YES;
}
As an aside, you can replace this line of code:
if([self.detailItem.comments.rootBeerComment isEqual: #""] || self.detailItem.comments.rootBeerComment == nil){
with this one, which is shorter and does the same thing since the length of an empty string and the length of a nil object will both return 0:
if([self.detailItem.comments.rootBeerComment length] == 0){
You can always use this. Make an NSRange that is from the first to the last character and use an empty string "".
myAttributedString.replaceCharacters(in range: NSRange, with str: String)
Related
This seems impossible, but maybe someone else has had the same problem.
Is it possible for me to accept an autocomplete programmatically, or in some way get the suggested word that pops up? My problem is that I'm capturing the return/backspace keystroke and then move focus to another textview. When enter/backspace is hit, the textview will ignore the auto-suggested word. It seems that it is only possible to accept an autocompletion by hit space/dot (and return for new row). With this code:
- (BOOL) textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range
replacementText:(NSString *)text {
NSRange textViewRange = [textView selectedRange];
// Handle newrow and backspace.
if(([text length] == 0) && (textViewRange.location== 0) && textViewRange.length==0){
// BACKSPACE KEYSTROKE
[delegate doSomethingWhenBackspace];
return NO;
}else if ([text isEqualToString:#"\n"]){
// RETURN KEYSTROKE
[delegate doSomethingWhenReturn];
return NO;
}
return YES;
}
I tried to programmatically add "space" when the return key is hit but that also ignores the auto-completed word.
else if ([text isEqualToString:#"\n"]){
// Tryin to accept autocomplete with no result.
textview.text = [textview.text stringByAppendingString:#" "];
// RETURN KEYSTROKE
[delegate doSomethingWhenReturn];
return NO;
}
Any suggestions?
Call -resignFirstResponder (i.e. [textView resignFirstResponder]) on the text view or text field which needs to accept autocomplete results: UIKit will change the .text property to include the autocorrected text.
If you want to keep the keyboard up after your first view resigns first responder, pass the firstResponder responsibility onto your next text input view with [anotherTextView becomeFirstResponder].
For backspace and space u can use this condition
if ([[text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length]==0)
{
[delegate doSomethingWhenBackspace];
return NO;
}
I've had a very similar problem, I was making an app that had to read every letter in a text view and I has issues when Autocomplete inserted words because it was saving it as if it was one letter.
you could add each character to an array and then check to see if any are over 1 string in length. Or you could add each character that is put in into an array and then run something like
NSString *string = text;
NSMutableArray *array = [NSMutableArray new];
for (int i=0; i<string.length; i++) {
[array addObject:[string substringWithRange:NSMakeRange(i, 1)]];
}
to add each character individually, by comparing the two arrays you could determine if autocorrect has been used and with what word/s.
Hope this will help.
I am trying to make a part of my app where if the person doesn't change the blank text in my UITextField, then he/she can't go on to the next step. Basically, I want to test the UITextField for nil text. I have used the if (text == #"") method, but if the person clicks on the UITextField but doesn't type, then the if statement doesn't work. For some reason it doesn't think the text == nil or "". Am I implementing the code wrong. Any other options. Please help!!!
You should be checking the length of the text property:
if([[textField text] length] == 0) {
//do something...
}
Here's the category I use...
#implementation NSString (NSString+Extensions)
- (BOOL)isNotBlank {
return [[self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] > 0;
}
#end
This way a nil string would evaluate to false, which is correct. Creating an isBlank would return false for nil, which isn't correct.
I have write code to check the string is empty or not. This code also check for the string only space that is also empty for store name and address etc. this will help you.
NSString *stringTemp = textField.text;
stringTemp = [stringTemp stringByReplacingOccurrencesOfString:#" " withString:#""];
if ([stringTemp isEqualToString:#""]) {
NSLog(#"Empty string");
}
else{
NSLog(#"string has some content ");
}
Thanks
If I were you I would disable and enable the button while the user is typing. Imho it's better that the button looks disabled when there is no text than having the user click the button to tell him that he is not allowed to move to the next view. Most of apples own apps do it like this.
You achieve this behavior by using the UITextFieldDelegate method like this
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
// "Length of existing text" - "Length of replaced text" + "Length of replacement text"
NSInteger textLength = [aTextView.text length] - range.length + [text length];
if (textLength > 0) {
doneButton.enabled = YES;
}
else {
doneButton.enabled = NO;
}
return YES;
}
If you provide a prefilled textfield you have to enable the button in viewDidLoad (or where ever you want) and if you provide an empty field you have to disable it initally.
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;
}
}
How do I force characters input into a textbox on the iPhone to upper case?
Set autocapitalizationType to UITextAutocapitalizationTypeAllCharacters on the UITextField.
See UITextInputTraits protocol (adopted by UITextField) for more details.
This solution is not fully satisfying.
Even with the autocapitalizationType set to UITextAutocapitalizationTypeAllCharacters, the user can still press caps to release the caps lock. And the textField.text = [textField.text stringByReplacingCharactersInRange:range withString:[string uppercaseString]]; return NO; solution is not that great: we loose the editing point if the user edits the middle of the text (after a textField.text =, the editing cursor goes to the end of the string).
I've done a mix of the two solution and here is what I propose: set UITextAutocapitalizationTypeAllCharacters, and add the following code to the delegate of the UITextField.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string {
// Check if the added string contains lowercase characters.
// If so, those characters are replaced by uppercase characters.
// But this has the effect of losing the editing point
// (only when trying to edit with lowercase characters),
// because the text of the UITextField is modified.
// That is why we only replace the text when this is really needed.
NSRange lowercaseCharRange;
lowercaseCharRange = [string rangeOfCharacterFromSet:[NSCharacterSet lowercaseLetterCharacterSet]];
if (lowercaseCharRange.location != NSNotFound) {
textField.text = [textField.text stringByReplacingCharactersInRange:range
withString:[string uppercaseString]];
return NO;
}
return YES;
}
While all the other answers do actually work (they make the input uppercase), they all have the problem that the cursor position is not retained (try inserting a character in the middle of the existing text). This apparently happens in the setter of UITextField's text property, and I have not found a way to restore it programmatically (for example, restoring the original selectedTextRange does not work).
However, the good news is, that there is a direct way to replace parts of a UITextField's (or UITextView's) text, which does not suffer from this issue:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
// not so easy to get an UITextRange from an NSRange...
// thanks to Nicolas Bachschmidt (see http://stackoverflow.com/questions/9126709/create-uitextrange-from-nsrange)
UITextPosition *beginning = textField.beginningOfDocument;
UITextPosition *start = [textField positionFromPosition:beginning offset:range.location];
UITextPosition *end = [textField positionFromPosition:start offset:range.length];
UITextRange *textRange = [textField textRangeFromPosition:start toPosition:end];
// replace the text in the range with the upper case version of the replacement string
[textField replaceRange:textRange withText:[string uppercaseString]];
// don't change the characters automatically
return NO;
}
For further information on these methods, see the documentation of UITextInput.
Rather than test for whether the character is upper- or lower-case, just assume that it's lower case. Much simpler.
In the
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string
method:
NSString *newString = [[textField.text stringByAppendingString:string] uppercaseString];
textField.text = newString;
return NO;
The simplest way would be to , implement the editing changed method of the text field and set the textfield's text value to upper case representation of the entered text
- (IBAction)TextFieldEditingChanged:(id)sender
{
_yourTextField.text = [_yourTextField.text uppercaseString];
}
I've been inspired by previous answers (thanks), but all of them had some flaws for me:
using textField.autocapitalizationType does not help when you also want lowercase or even more control.
textField.text = "my String" sets the cursor to the end of the textField which is really annoying when editing.
So this is my Swift code that gives you full control over the typed characters (uppercase, lowercase, ignore...) AND lets the cursor where you expect it. The code is tested to work with more than one textField.
Edit: tested with iOS 8.3
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
// modify string as and if the user wants:
// at this place you can also return false to ignore the replacement completely.
var str = string
if !Defaults.isUpperOrLower() {
if Defaults.isAllLetterUpper() {
str = string.uppercaseString
} else {
str = string.lowercaseString
}
}
// updating textField.text sets the curser position to the end
// so we store the cursor position before updating.
let selRange = textField.selectedTextRange
// Update textField as requested and modified.
var txt = textField.text as NSString
textField.text = txt.stringByReplacingCharactersInRange(range, withString: str)
// recalculate and set the cursor position in the textField:
// tested with
// 1. typing a character
// 2. backspace
// 3. selecting a range + backspace
// 4. selecting a range + typing a character
if let selRange = selRange {
var newPosition: UITextPosition?
if range.length == 0 {
// normal character
newPosition = textField.positionFromPosition(selRange.end, inDirection: UITextLayoutDirection.Right, offset: count(string))
} else {
// backspace
newPosition = textField.positionFromPosition(selRange.end, inDirection: UITextLayoutDirection.Left, offset: range.length - count(string))
}
textField.selectedTextRange = textField.textRangeFromPosition(newPosition, toPosition: newPosition)
}
// return false because we did everything manually
return false
}
This is my code, when i have 6 textFields
Hope you will get the point
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (textField==self.textField6) {
self.textField6.text = [textField.text stringByReplacingCharactersInRange:range withString:[string uppercaseString]];
return NO;
}
return YES;
}
return NO will have issues with back button;
beter use this
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
if ([[string uppercaseString] isEqualToString:string]) {
return YES;
}
NSString *newString = [[textField.text stringByAppendingString:string] uppercaseString];
textField.text = newString;
return NO;
}
Maybe I haven't found all the kinks to this method, but so far it works well for me. It seems to avoid the issues with jumping selection to the end if you go down the route of returning NO from -textField:shouldChangeCharactersInRange:replacementString:. Since UITextField conforms to UIKeyInput, simply override the method -insertText: to change the input string to uppercase before calling super with the uppercase string:
- (void)insertText:(NSString *)text {
NSString *uppercaseText = [text uppercaseString]
[super insertText:uppercaseText]
}
Or if you've moved along to Swift:
override func insertText(text: String) {
let uppercaseText = text.uppercaseString
super.insertText(uppercaseText)
}
To Automatically input uppercase in textfield:
Case 1: Add UITextDelegate protocol and implement the following method
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
textField.text = [textField.text stringByReplacingCharactersInRange:range withString:[string capitalizedString]];
return NO;
}
Case 2:Set the following parameter in the ViewDidLoad() method in your view controller. Although in this case the user can turnoff the caps button on the keyboard.
urTextFieldName.autocapitalizationType = UITextAutocapitalizationTypeAllCharacters;
One issue I have with some of the above answers is if you try and set textfield.text, you will lose the cursor position. So if a user tries to edit the middle of the text, the cursor will jump to the end.
Here is my simple Swift solution, still using UITextFieldDelegate:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if textField == textFieldToUppercase {
if string == "" {
// User presses backspace
textField.deleteBackward()
} else {
// User presses a key or pastes
textField.insertText(string.uppercaseString)
}
// Do not let specified text range to be changed
return false
}
return true
}
You still have to handle if a user presses the Return, Done, etc key on the keyboard. Just add:
if string == "\n" {
// Do something when 'Done' is pressed, like dismiss the keyboard
textField.resignFirstResponder()
return false
}
...inside of func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool.
I want to limit the character users can type in iPhone's keyboard, so I created an array of my own. e.g. The array including 0~9 and a dot to enable users to type a price. Then I can return NO for -(BOOL)textField:shouldChangeCharactersInRange:replacementString: if the replace string is not in the array.
The problem is that the backspace button is also disabled when I use this array to filter text. Any ideas about how to enable backspace button?
Another problem is that I want to let users type their names and therefore I don't want to let them switch to numbers and punctuaction (backspace button is also locked if I use an array to filter). How to disable the switch button on the keyboard (Now I just limit them to type a~z, blank and "." , but I think disable the switch button might be a better way)?
I find a way (maybe not good enough, but it can do the work for backspace function):
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if(textField == txtChargeAmt)
{
if(string.length == 0) //backspace button is pressed
{
textField.text = [textField.text substringToIndex:(textField.text.length - 1)];
return NO;
}
for(NSString *s in arrNumberAndDot)
{
if([string isEqualToString:s])
{
return YES;
}
}
return NO;
}
else
return YES;
}
Other ideas about the backspace issue are welcomed. And how to disable the switch button then?
I guess I am not sure why you would want to use the same TextField for these two different types of input.
I would have two fields, an alphanumeric field for name entry and a numeric field for number entry.
Or am I not getting your question?
This will do what you want a little more succinctly (and efficiently):
- (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*)string
{
NSCharacterSet *validCharacterSet = [NSCharacterSet characterSetWithCharactersInString:#".0123456789"];
BOOL shouldChange =
[string length] == 0 || // deletion
textField != txtChargeAmt || // not the field we care about
[string rangeOfCharacterFromSet:validCharacterSet].location != NSNotFound;
if (!shouldChange)
{
// Tell the user they did something wrong. There's no NSBeep()
// on the iPhone :(
}
return shouldChange;
}
I'd construct that character set somewhere else so you you'd only have to do it once, but you get the idea. Anyone have any thoughts on what to do to alert the user they used an invalid character? I'm trying to solve a similar problem.