Midori
Midori

Reputation: 121

How to simulate the delete key on UITextField?

I'm creating an application on the iPad. I create a custom keyboard using UITextField's inputView property. To insert text at the cursor position, I use the copy & paste method (http://dev.ragfield.com/2009/09/insert-text-at-current-cursor-location.html), which works fine. Now, I want to create the delete key. The code that I'm using is:

if (textField.text.length > 0) {
    textField.text = [textField.text substringToIndex:textField.text.length-1];
}

However, as you may know, it only deletes the last character no matter where the cursor is. Does anyone know a better solution?

Thank you very much!

Upvotes: 12

Views: 10637

Answers (10)

GoldenJoe
GoldenJoe

Reputation: 8002

Just call [textField deleteBackward]. It takes care of cursor position and highlight for you.

Swift:

textField.deleteBackward()

Upvotes: 18

froggomad
froggomad

Reputation: 1915

To delete the position behind the cursor, you can use textField.deleteBackward()

This is what I use in Swift 5 when I want to get the result without deleting text from the UITextField

extension UITextField {
    /// simulates removing a character from 1 position behind the cursor and returns the result
    func backSpace() -> String {
        guard var text = text, // local text property, matching textField's text
              let selectedRange = selectedTextRange,
              !text.isEmpty
        else { return "" }
        
        let offset = offset(from: beginningOfDocument, to: selectedRange.start)
        
        let index = text.index(text.startIndex, offsetBy: offset-1)
        // this doesn't remove text from the textField, just the local text property
        text.remove(at: index)
        return text
    }
}

Upvotes: 0

oscar castellon
oscar castellon

Reputation: 3138

you can try this:

if (txtTotal.text.length > 0) {
        txtTotal.text = [txtTotal.text substringToIndex:txtTotal.text.length-1];
    }

Upvotes: -1

user577537
user577537

Reputation:

Late to the party (again ;-)... With iOS 5, UITextField conforms to the UITextInput protocol, which extends UITextField with some helpful methods and properties, including the selectedTextRange property, which is the equivalent of selectedRange in UITextView.

Upvotes: 5

KubajzT
KubajzT

Reputation: 34

What about this if (UITextField.text.length > 0) { [UITextField deleteBackward]; } :)

Upvotes: -1

rob mayoff
rob mayoff

Reputation: 385610

Holy necro, Batman! Here's a much simpler answer for iOS 3.2 and later. Do this in your view controller's viewDidLoad or some other method that has access to the delete button:

[self.myDeleteButton addTarget:nil action:@selector(deleteBackward) forControlEvents:UIControlEventTouchDown];

This is the same way the system keyboard's delete key works: it sends the deleteBackward message along the responder chain.

Upvotes: 0

nacho4d
nacho4d

Reputation: 45108

This is an alternative solution using a undocumented API: selectionRange. I don't know if this will pass Apple's review but as written it won't make the app crash if the method is not available and does not give warnings too :)

This is a category on UITextField:

- (void)deleteBackward{
    @try{
        //check current selected range
        NSRange selectedRange = [[self valueForKey:@"selectionRange"] rangeValue];
        if (selectedRange.location == NSNotFound) selectedRange = NSMakeRange([[self text] length], 0);
        if (selectedRange.location < 1) return;

        //delete one char
        NSRange deleteRange = (selectedRange.length>0)?selectedRange:NSMakeRange(selectedRange.location-1,1);
        self.text = [self.text stringByReplacingCharactersInRange:deleteRange withString:@""];

        //adjust the selected range to reflect the changes
        selectedRange.location = deleteRange.location;
        selectedRange.range.length = 0;
        [self setValue:[NSValue valueWithRange:range] forKey:@"selectionRange"];
     }@catch (NSException *exception) {
          NSLog(@"failed but catched. %@", exception);
     }@finally {}
}


UPDATE: 2011/11/17

in iOS5 UITextField and UITextView conform to UITextInput protocol (which conforms to UIKeyInput) so you can do [textField deleteBackward]; which will do exactly the same as Del key in the keyboard, [textField insertText:@"text"]; which will insert text and update the caret position correctly, etc.

So, for compatibility purposes, probably what you want to do similar to this:

- (void) myDeleteBackward
{
    if ([self conformsToProtocol:@protocol(UITextInput)]) {
        // iOS5 and later
        [textField deleteBackward];
        // Or do below line if you are not deploy-targeting 5.0 or above and want to avoid warnings
        //[textField performSelector:@selector(deleteBackward)];
    } else {
        // iOS4 and older versions
        // Do the trick :)
        @try{
        ...
        }@catch (NSException *exception) {
        ...
        }
    }
}

Upvotes: 4

zelk
zelk

Reputation: 607

I've got it! This is my delegate method implementation for the shouldChangeCharactersInRange method.


- (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange) range replacementString:(NSString *) string {
    if ([string isEqualToString:@"⌫"]) {
        // NSLog(@"Backspace Pressed");
        NSMutableString *text = [textField.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];
        }
        // NSLog(@"%@", text);
        textField.text = text;
        return NO;
    } else {return YES;}
}

It works hand in hand with the UIPasteboard method for getting text into a UITextField, which means that you have to link up your delete key to a method that pastes an arbitrary unicode character into your textfield (such as ), and when the delegate method is called, you recognise that the replacement string is that specific character.

From there, you either delete the characters in range from your mutable string of the textField's text if there is an active selection, or you grab the previous character from the range if there is no selection, just a caret.

Upvotes: 5

Tom S
Tom S

Reputation: 624

Consider switching to a UITextView, which has a selectedRange property for getting the currently selected range of characters or the cursor location if nothing is selected. Unfortunately, UITextField does not have this method, and I have not found another way to find the cursor location.

The documentation for the selectedRange property can be found here here on Apple's website. It consists of an NSRange in which selectedRange.location is the cursor location and selectedRange.length is the number of selected characters (or zero if nothing is selected.) Your code will have to look something like this:

textView.text = [textView.text stringByReplacingCharactersInRange:textView.selectedRange withString:@""];

Upvotes: 2

drawnonward
drawnonward

Reputation: 53669

A UITextView has a selectedRange property. Given that you could build a range to use with the following code:

textView.text = [textView.text stringByReplacingCharactersInRange:?? withString:@""];

A UITextField has no such range property. Obviously it has a _selectionRange member but that is private.

I think your choices are either to switch to a UITextView or prevent having the cursor anywhere but at the end of the text and using your current code.

You can always submit a bug report to Apple requesting that they document selectedRange for UITextField, or ask for special permission to access the field directly.

Upvotes: 0

Related Questions