简体   繁体   中英

iPhone: Disable the “double-tap spacebar for .” shortcut?

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.

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;
}

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!

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]]);

}

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.

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;
}

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.

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
    }

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.

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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM