14

By default, if you tap the spacebar twice on the iPhone or iPad, instead of getting "  " (two spaces), you get ". " (a period followed by a space). Is there any way to disable this shortcut in code?

Update: Disabling autocorrection via UITextInputTraits doesn't work.

Update 2: Got it! See my post below.

igul222
  • 8,557
  • 14
  • 52
  • 60
  • 3
    This is not an application's concern. The *user* can turn it off if they desire via the Settings utility. – nobody Apr 05 '10 at 02:58
  • 1
    I'm writing an app in which users will input many spaces in a row for indentation. – igul222 Apr 06 '10 at 00:47
  • 2
    To Matchu & Andrew Medico who are asking "Why?"… One use-case I found: In a UISearchBar where you know the user is typing a series of words to be used as criteria. No need for a period. Worse, the period distorts the search results. – Basil Bourque Mar 05 '13 at 07:29
  • 1
    In my music app, users enter multiple spaces to align chord symbols with lyrics, and the automatic periods are not wanted. However, most users still want that functionality enabled in other apps, and even in other fields in this app, so turning it off for particular fields has a definite benefit. – arlomedia Aug 19 '14 at 19:09

9 Answers9

14

I have an answer based on the the one given by Chaise above.

Chaise's method does not allow you to type two spaces in sequence - this is not desirable in some situations. Here's a way to completely turn off the auto-period insert:

Swift

In the delegate method:

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    //Ensure we're not at the start of the text field and we are inserting text
    if range.location > 0 && text.count > 0
    {
        let whitespace = CharacterSet.whitespaces
        
        let start = text.unicodeScalars.startIndex
        let location = textView.text.unicodeScalars.index(textView.text.unicodeScalars.startIndex, offsetBy: range.location - 1)            
        
        //Check if a space follows a space
        if whitespace.contains(text.unicodeScalars[start]) && whitespace.contains(textView.text.unicodeScalars[location])
        {
            //Manually replace the space with your own space, programmatically
            textView.text = (textView.text as NSString).replacingCharacters(in: range, with: " ")
            
            //Make sure you update the text caret to reflect the programmatic change to the text view
            textView.selectedRange = NSMakeRange(range.location + 1, 0)
            
            //Tell UIKit not to insert its space, because you've just inserted your own
            return false
        }
    }
    
    return true
}

Now you can tap the spacebar as much and as fast as you like, inserting only spaces.

Objective-C

In the delegate method:

- (BOOL) textView:(UITextView*)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString*)text

Add the following code:

//Check if a space follows a space
if ( (range.location > 0 && [text length] > 0 &&
      [[NSCharacterSet whitespaceCharacterSet] characterIsMember:[text characterAtIndex:0]] &&
      [[NSCharacterSet whitespaceCharacterSet] characterIsMember:[[textView text] characterAtIndex:range.location - 1]]) )
{
    //Manually replace the space with your own space, programmatically
    textView.text = [textView.text stringByReplacingCharactersInRange:range withString:@" "];
    
    //Make sure you update the text caret to reflect the programmatic change to the text view
    textView.selectedRange = NSMakeRange(range.location+1, 0);  
    
    //Tell Cocoa not to insert its space, because you've just inserted your own
    return NO;
}
Community
  • 1
  • 1
simeon
  • 4,466
  • 2
  • 38
  • 42
  • its worked for me to avoid double space tapping and thanks to Mr.Simeon :) – Anilkumar iOS - ReactNative Oct 15 '14 at 16:28
  • 1
    This no longer works correctly with attributed text. The line `textView.text = ...` should now read: `NSMutableAttributedString *attributedString = [textView.attributedText mutableCopy]; [attributedString replaceCharactersInRange:range withString:@" "]; textView.attributedText = attributedString;` – colincameron Oct 30 '14 at 13:17
  • @RohitKP Swift version added – simeon Jun 23 '16 at 04:37
  • 1
    Having a hard time adapting this to Swift 4 and use in a textField instead of a textView. Same use case as discussed above -- a search field where looking for consecutive spaces is far more likely than wanting a period at the end of a sentence. What changes are needed to use this in a textField? – ConfusionTowers Nov 23 '17 at 06:20
  • Swift code crashes when enter emoji followed by double space) – Valeriy Van Nov 13 '21 at 09:50
6

This is the simplest solution I could get working for this problem in Swift 4. It's more complete than some other answers as it allows multiple spaces in a row to be entered.

func disableAutoPeriodOnDoubleTapSpace() {
    textField.addTarget(self, action: #selector(replaceAutoPeriod), for: .editingChanged)
}

@objc private func replaceAutoPeriod() {
    textField.text = textField.text.replacingOccurrences(of: ". ", with: "  ")
}

If your text field is formatted with .attributedText, you will need to store the old .selectedTextRange before and reset it after you set the .text value. Otherwise your cursor will move to the end of the text when editing in the middle of a string.

Hope this helps someone who tried all the other answers with no luck!

Lars
  • 425
  • 1
  • 5
  • 10
4

Put this in your delegate class:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{

 //Check for double space
 return !(range.location > 0 && 
          [string length] > 0 &&
          [[NSCharacterSet whitespaceCharacterSet] characterIsMember:[string characterAtIndex:0]] &&
          [[NSCharacterSet whitespaceCharacterSet] characterIsMember:[[textField text] characterAtIndex:range.location - 1]]);

}
Chaise
  • 41
  • 3
  • One problem with this solution comes up when editing and attempting to insert between two words. The first character will likely be a space and is filtered. simeon's answer below handles this or use a variation of http://stackoverflow.com/questions/1528049/iphone-sdk-disable-auto-creation-of-dot-in-text-field-or-textview/1529488#1529488 that replaces ". " with two spaces. – John Lemberger Aug 22 '11 at 16:01
  • The bad news about `shouldChangeCharactersInRange:` is that it does not distinguish between text insertion and deletion. If you delete the last character, NSRange looks like if you duplicate the remaining text. If you think it's no problem, try to select and delete some character(s) in the middle of the word: I do not even know how to explain NSRange in this case. Probably it's a bug, and maybe even the emulator's bug... – 18446744073709551615 Nov 16 '11 at 14:18
  • Is there a reason why `[NSCharacterSet whitespaceCharacterSet]` is being used rather than just checking for the `@" "` space character? Since this character set contains both spaces and tabs. – micap Nov 24 '14 at 09:46
2

This is an implementation in Swift 4.0 for text fields, copying Simeon's answer:

func textField(_ textField: UITextField, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    //Ensure we're not at the start of the text field and we are inserting text
    if range.location > 0 && text.count > 0{
        let whitespace = CharacterSet.whitespaces
        //get list of whitespace characters

        let start = text.unicodeScalars.startIndex
        if let textFieldText = textField.text{
            let location = textFieldText.unicodeScalars.index(textFieldText.unicodeScalars.startIndex, offsetBy: range.location - 1)

            //Check if a space follows a space
            if whitespace.contains(text.unicodeScalars[start]) && whitespace.contains(textFieldText.unicodeScalars[location]){

                //Manually replace the space with your own space, programmatically
                textField.text = (textFieldText as NSString?)?.replacingCharacters(in: range, with: " ")

                if let pos = textField.position(from: textField.beginningOfDocument, offset: range.location + 1)
                {
                    //Make sure you update the text caret to reflect the programmatic change to the text view
                    textField.selectedTextRange = textField.textRange(from: pos, to: pos)


                    //Tell UIKit not to insert its space, because you've just inserted your own
                    return false
                }
            }
        }
    }
    return true
}

Hope this helps!

EDIT: Added a missing return statement at bottom.

Dunk L
  • 21
  • 1
  • 3
2

Swift 5

I found this question and answer tree while trying to allow " " and "-" in a carefully managed textField input context, but not allowing "." nor "–" caused by double space or double -.

So, I simplified and modified the delegate func as follows:

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        if textField.text?.last == " " && string == " " {
            textField.text = (textField.text as NSString?)?.replacingCharacters(in: range, with: " ")
            if let pos = textField.position(from: textField.beginningOfDocument, offset: range.location + 1) {
            // updates the text caret to reflect the programmatic change to the textField
                textField.selectedTextRange = textField.textRange(from: pos, to: pos)
                return false
            }
        }
        if textField.text?.last == "-" && string == "-" {
            textField.text = (textField.text as NSString?)?.replacingCharacters(in: range, with: "-")
            if let pos = textField.position(from: textField.beginningOfDocument, offset: range.location + 1) {
            // updates the text caret to reflect the programmatic change to the textField
                textField.selectedTextRange = textField.textRange(from: pos, to: pos)
                return false
            }
        }
        return true
    }
Peter Brockmann
  • 3,594
  • 3
  • 28
  • 28
1

Another version of the one simeon posted (which was a version of Chaise's). This one works with text fields (UITextField). You'll have to set up the UITextFieldDelegate for this to do anything. I commented out the line for updating the caret, but it still seems to work.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)text
{
    //Check if a space follows a space
    if ( (range.location > 0 && [text length] > 0 &&
          [[NSCharacterSet whitespaceCharacterSet] characterIsMember:[text characterAtIndex:0]] &&
          [[NSCharacterSet whitespaceCharacterSet] characterIsMember:[[textField text] characterAtIndex:range.location - 1]]) )
    {
        //Manually replace the space with your own space, programmatically
        textField.text = [textField.text stringByReplacingCharactersInRange:range withString:@" "];

        //Make sure you update the text caret to reflect the programmatic change to the text view
//      textField.selectedTextRange = NSMakeRange(range.location+1, 0);  

        //Tell Cocoa not to insert its space, because you've just inserted your own
        return NO;
    }
    return YES;
}
nevan king
  • 112,709
  • 45
  • 203
  • 241
1

A simpler solution that I have found to work for a UITextView is as follows:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    if ([text isEqualToString:@". "] && range.length == 1) {
        NSMutableAttributedString *attributedString = [textView.attributedText mutableCopy];
        [attributedString replaceCharactersInRange:range withString:@" "];
        textView.attributedText = attributedString;
        textView.selectedRange = NSMakeRange(range.location + 1, 0);

        return NO;
    }
    return YES;
}

This allows multiple spaces to be entered repeatedly and also handles attributed text. The only case I have found it to fail with is when pasting a period and space with a single character already selected.

colincameron
  • 2,696
  • 4
  • 23
  • 46
-2

I don't think there's a way to turn off exactly this, but check out UITextInputTraits. This lets you declare properties of your field, for example you can say if it's for entering a URL. That influences the behavior of the keyboard. If the reason you don't want double space to yield a period and space is that the text should be literally what the user entered for some reason, perhaps you want to turn off autocorrection. It's possible turning off autocorrection turns off double space for period, I don't know.

Ken
  • 12,933
  • 4
  • 29
  • 32
  • 2
    Should be a comment rather than an answer. Turning off autocorrection doesn't turn off double space for a period, fyi. – indiantroy Aug 26 '13 at 21:01
-4

OK, I figured it out. In your UITextView delegate, add this:

-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    if([text isEqualToString:@". "])
        return NO;
}
igul222
  • 8,557
  • 14
  • 52
  • 60