how to perform backspace and delete? - ios

I'm developing a code in where I need to perform a delete and backspace from UITextView.
I tried:
-(IBAction)delete:(id)sender{
uitextview.text =[uitextview.text substringToIndex:[uitextview.text length]-1];
}
this perform back space and remove last character but how to delete a character from specific location in uitextview on a particular cursor location.
Please help me out.

Use the selectedRange property of the UITextView. This indicates the cursor position and/or selected text (the length will be zero if no text is selected).
So, if the range.length is > 0, you can use stringByReplacingCharactersInRange:withString: to delete the selected text.
Otherwise, create a new range with location one less than your selected range and length of 1, and then remove this character.
You may need to re-set the selectedRange property to make sure the cursor ends up in the appropriate position. I imagine all this is done automatically when using the standard keyboard.

Thanks for ur suggest.I want to suggest one way to perform this work.
NSRange range = uitextview.selectedRange;
NSRange selectedRange = uitextview.selectedRange;
NSLog(#"%d",selectedRange.length);
NSLog(#"%d",selectedRange.location);
int temploc=selectedRange.location;
NSMutableString *text = [uitextview.text mutableCopy];
// NSLog(#"Length: %d Location: %d", range.length, range.location);
if (range.length > 0) {
[text deleteCharactersInRange:range];
}
if (range.length == 0 && range.location != 0) {
NSRange backward = NSMakeRange(range.location - 1, 1);
// NSLog(#"Length: %d Location: %d", backward.length, backward.location);
[text deleteCharactersInRange:backward];
}
uitextview.text = text;

Related

UITextView Delegates Behaving Differently with Custom iOS Keyboards (SwiftKey)

Using the shouldChangeTextInRange delegate to detect certain phrases when typed in a UITextViewgives different range and text values when alternating between the stock keyboard and custom keyboards like SwiftKey (in identical scenarios).
-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {...}
For example:
The moment after I type: "ABCDEFG HIJ " without quotes and including the ending space using the stock keyboard, I find the range is equal to (11, 0). However, with SwiftKey, I'm getting a range of (10, 1). Additionally, the text will be equal to just the ending space using the stock keyboard, but will be equal to 'J' plus the space using SwiftKey. Any ideas why this is happening and how I can standardize my function's behavior given these differences if I want to properly detect certain phrases? (my goal is detecting the two words immediately before every space press)
EDIT: this is how I detect the two words in front of a space. My issue is that range is giving me different values when I use a non-stock keyboard
NSUInteger charsBackUntilSecondSpace = 0;
NSUInteger numSpacesFound = 0;
for (int i = (int) range.location - 1; i>= 0; i--) {
if([textView.text characterAtIndex:i] == ' ') {
numSpacesFound++;
}
if (numSpacesFound == 2) {
break;
}
else {
charsBackUntilSecondSpace++;
}
}
UITextRange *selectedTextRange = textView.selectedTextRange;
NSUInteger location = [textView offsetFromPosition:textView.beginningOfDocument
toPosition:selectedTextRange.start];
NSRange inFrontOfCursorRange = NSMakeRange (location - charsBackUntilSecondSpace, charsBackUntilSecondSpace);
my goal is detecting the two words immediately before every space
press
You can achieve it like that below:-
NSInteger lenPos=(range.length > 2) ? 2 : range.length;
NSString *txt=[textView.text substringWithRange:NSMakeRange(range.length-lenPos-1, lenPos)] ;

iOS - appending string before and after the word

I want to add a string in the highlighted area in the textview, I mean by the highlighted area, where the blue line is located.
So once the user click on the button it adds a string where the "blue line" is located
I used stringByAppendingString but it adds the string after the word exists only
NSRange range = myTextView.selectedRange;
NSString * firstHalfString = [myTextView.text substringToIndex:range.location];
NSString * secondHalfString = [myTextView.text substringFromIndex: range.location];
myTextView.scrollEnabled = NO; // turn off scrolling
NSString * insertingString = [NSString stringWithFormat:#"your string value here"];
myTextView.text = [NSString stringWithFormat: #"%#%#%#",
firstHalfString,
insertingString,
secondHalfString];
range.location += [insertingString length];
myTextView.selectedRange = range;
myTextView.scrollEnabled = YES;
You need to use the selectedRange to find out where the text cursor is. Then use replaceCharactersInRange:withString: or insertString:atIndex: to insert the new text into the original text. Then update the text into the view.
Even though its not clear what you are trying to achieve, it seems that you want the user to start editing the textfield from the position where text starts. In that case , you can refer following:
Hint 1
Set your view controller (or some other appropriate object) as the text field's delegate and implement the textFieldDidBeginEditing: method like this:
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
UITextPosition *beginning = [textField beginningOfDocument];
[textField setSelectedTextRange:[textField textRangeFromPosition:beginning
toPosition:beginning]];
}
Note that setSelectedTextRange: is a protocol method of UITextInput (which UITextField implements), so you won't find it directly in the UITextField documentation.
Hint 2
self.selectedTextRange = [self textRangeFromPosition:newPos toPosition:newPos];
Hint 3
finding-the-cursor-position-in-a-uitextfield/

UITextPosition to Int

I have a UISearchBar on which I am trying to set a cursor position. I am using UITectField delegates as I couldn't find anything direct for UISearchBar. Below is the code I am using:
UITextField *textField = [searchBar valueForKey: #"_searchField"];
// Get current selected range , this example assumes is an insertion point or empty selection
UITextRange *selectedRange = [textField selectedTextRange];
// Construct a new range using the object that adopts the UITextInput, our textfield
UITextRange *newRange = [textField textRangeFromPosition:selectedRange.start toPosition:selectedRange.end];
Question is in the 'newRange' object for 'toPosition' I want to have something like selectedRange.end-1; as I want the cursor to be on second last position.
How do I set the cursor to second last position?
Swift 5
I came across this question originally because I was wondering how to convert UITextPosition to an Int (based on your title). So that is what I will answer here.
You can get an Int for the current text position like this:
if let selectedRange = textField.selectedTextRange {
// cursorPosition is an Int
let cursorPosition = textField.offset(from: textField.beginningOfDocument, to: selectedRange.start)
}
Note: The properties and functions used above are available on types that implement the UITextInput protocol so if textView was a UITextView object, you could replace the instances of textField with textView and it would work similarly.
For setting the cursor position and other related tasks, see my fuller answer.
the clue is to make a position and then a range with no length and then select it
e.g.
- (IBAction)select:(id)sender {
//get position: go from end 1 to the left
UITextPosition *pos = [_textField positionFromPosition:_textField.endOfDocument
inDirection:UITextLayoutDirectionLeft
offset:1];
//make a 0 length range at position
UITextRange *newRange = [_textField textRangeFromPosition:pos
toPosition:pos];
//select it to move cursor
_textField.selectedTextRange = newRange;
}

iOS keyboard shortcut for double space to replace with ".", overwrites my UITextfiled data

I have a table row which has some data entry by using UITextField.
Lets say I have some data in it "my data"
Next time when I come back to this activity, I want to edit it and append with . using double space keyboard shortcut to get the result as "my data."
The issue is when I use double space shortvut it overwrite it and replace the value of UITextfield with ". "
Is there any way to append this shortcut.
I hope I explain my problem correctly.
Well after lot of digging and some manipulation I have following answer, this could help if some one has the same issue.
use following delegate call
(BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
and copy the following code to fix the issue described above.
//Check for double space, hot key shortcut provided on keyboard.
if((range.location > 0 &&
[string length] > 0 &&
[[NSCharacterSet whitespaceCharacterSet] characterIsMember:[string characterAtIndex:0]] &&
[[NSCharacterSet whitespaceCharacterSet] characterIsMember:[[textField text] characterAtIndex:range.location - 1]])) {
//Manually replace the space with your own space, programmatically
NSRange backward = NSMakeRange(range.location - 1, 1);
textField.text = [rightTextField.text stringByReplacingCharactersInRange:backward withString:#". "];
return NO;
}
This will append . to the existing data in UITextField.
Still looking for better answer.

UITextView word wrapping with maximum line-length and lines

For an iOS application I am writing I am using an UITextView, where the user can insert a limited text.
To the textview there are 2 restrictions:
Lines can be no longer than 30 characters
There can be only 20 lines of text in the UITextView.
So in short, the maximum is 20 lines of 30 characters.
When a user is typing some text in the UITextView and the current sentence is 30 characters, I want it to automatically insert a new line \n (before the last word on that line) and force the last word and cursor to the line below.
When a user has 20 lines with 30 characters (or even simpler said: 20 lines, with 30 characters on the last line) I want the input to be blocked.
Now, most of this is fairly 'simple' but the code I have does not account for border cases, like inserting text in earlier lines.
I looked around the documentation from Apple, but I can not find a way to actually force this kind of Word-wrapping on a UITextView.
My try is to handle all this in the shouldChangeTextInRange delegate method (made the code a little more verbose, so it's a bit easier to read).
#define MAX_LENGTH_LINE 30
#define MAX_LENGTH_ROWS 20
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
// Check for backspaces, they should always be allowed?
if ([text length] == 0 && ![text isEqualToString:#"\n"])
return YES;
NSArray* lines = [textView.text componentsSeparatedByString:#"\n"];
// Check if there are a maximum of lines and the last line is already maxed out
NSString* lastLine = [lines objectAtIndex:[lines count] - 1];
if (([lines count] == MAX_LENGTH_ROWS) &&
(lastLine != nil) &&
([lastLine length] > MAX_LENGTH_LINE) &&
([text length] > 0))
return NO;
if ((lastLine != nil) &&
([lastLine length] > MAX_LENGTH_LINE))
{
NSRange range = [textView.text rangeOfString:#" " options:NSBackwardsSearch];
NSRange breakRange = [textView.text rangeOfString:#"\n" options:NSBackwardsSearch];
if (breakRange.location == NSNotFound)
breakRange = NSMakeRange(0, 1);
if (range.location == NSNotFound) {
range = NSMakeRange(0, 1);
}
if (range.location > breakRange.location)
{
textView.text = [textView.text stringByReplacingCharactersInRange:NSMakeRange(range.location, 1) withString:#"\n"];
}
else
{
textView.text = [textView.text stringByAppendingString:#"\n"];
}
}
if ([text isEqualToString:#"\n"])
{
if ([lines count] == MAX_LENGTH_ROWS)
return NO;
else {
return YES;
}
NSRange range = NSMakeRange(textView.text.length - 1, 1);
[textView scrollRangeToVisible:range];
}
return YES;
}
In the meanwhile I have been at this for a while and I lost it at the moment. Anyone who can give some pointers to just limit the UITextView to the 20 lines / 30 characters limitation I want?
This may be counter to your overall goals, but the simplest answer in my head is to reparse the string every time the user adds a character. Then it wouldn't matter where the character was added. Instead of doing all this in shouldChangeTextInRange: do it in textViewDidChange:. You will need to be a UITextViewDelegate and you will need a single class NSString to hold the last successful user text update in case your user is trying to add a character beyond the allowed limit.
Something like this:
-(void)textViewDidChange:(UITextView *)textView
{
//Get the textview without any new line characters
NSMutableString *temporaryString = [NSMutableString stringWithString:[textView.text stringByReplacingOccurrencesOfString:#"\n" withString:#" "]];
bool updatePossible = true;
int i = 0, numberOfLinesSoFar = 0;
while(i + 30 < [temporaryString length])
{
//Go 30 characters in and start the reverse search for a word separation
i += 30;
int j = i;
//Get the location of the final word separation in the current line
while(j >= i && [temporaryString characterAtIndex:j] != ' ')
{
j--;
}
//This means we found a word separation
if(j > i)
{
i = j;
[temporaryString replaceCharactersInRange:NSMakeRange(i,1) withString:#"\n"];
}
//We didn't find a word separation
else
{
//Here we will just have to break the line at 30.
[temporaryString insertString:#"\n" atIndex:i];
}
numberOfLinesSoFar++;
//Check if we just wrote line 20 and still have characters to go.
if(numberOfLinesSoFar > 19 && i < [temporaryString length])
{
//Revert user change to the last successful character addition
textView.text = lastSuccessfulViewString;
updatePossible = false;
break;
}
}
if(updatePossible)
{
//If we are within the limits then update the global string (for undoing character additions) and the textview
textView.text = temporaryString;
lastSuccessfulViewString = temporaryString;
}
}
Now this won't allow the user to put in their own new line characters, but that could be handled with a couple if then statements.
After a bit of fiddling around, created a control which contains a UITextView as a subview.
I let this control handle the text wrapping and forward the delegate methods to the view registered as a delegate.
It might help others, so I am posting a link to BitBucket here.
PS. It's still very verbose, but that is to show how I solved this for my case.
https://bitbucket.org/depl0y/sbframework/src/master/SBFramework/Views/SBTextView?at=master

Resources