Обнаружение backspace в UITextField в iOS8

Для обнаружения Backspace я переопределил метод DeleteBackward (должен работать с iOS5 +)

var input = new BackspaceTextField(RectangleF.Empty);
etc
input.BecomeFirstResponder();

Здесь код

public sealed class BackspaceTextField : UITextField
{
    public BackspaceTextField(RectangleF frame) : base(frame)
    {
    }

    public override void DeleteBackward ()
    {
        Console.WriteLine ("DeleteBackward");
    }
}

Когда я нажимаю кнопку "Backspace", ничего не происходит. Я ожидаю, что сообщение "DeleteBackward" должно появиться

Окружающая среда: iOS8, xamarin

Изменить: 0

Аналогичный вопрос в objective-c: Обнаружение backspace в UITextField

Я проверил дополнительную проверку. DeleteBackward - это метод из UIKeyInput, поэтому я проверил метод insertText, этот метод работает исправно.

public override void InsertText (string text)
{
   base.InsertText(text);
}

Я проверил DeleteBackward на objective-c, и он отлично работает.

Есть ли у вас какие-либо идеи по обнаружению backspace в UITextField в iOS8?

Не могли бы вы объяснить, почему метод DeleteBackward не вызывался?

Изменить: 1

Я отправил тот же вопрос в Xamarin forum. Похож на ошибку в iOS8 + xamarin, потому что в iOS 7.1 работает безотказно.

Это ошибка. Здесь подробности

Ответ 1

Многие люди говорили, что это ошибка, но поскольку эта проблема все еще существует в GM, я начинаю думать, что это может быть изменение в логике. С учетом сказанного я написал этот бит кода для своего приложения и протестировал его на iOS 7-8.

Этот код немного перед красной линией частных API, однако у вас не должно возникнуть проблемы с его использованием. Мое приложение с этим кодом находится в магазине приложений.

Добавьте следующий подкласс к вашему подклассу UITextField.

- (BOOL)keyboardInputShouldDelete:(UITextField *)textField {
    BOOL shouldDelete = YES;

    if ([UITextField instancesRespondToSelector:_cmd]) {
        BOOL (*keyboardInputShouldDelete)(id, SEL, UITextField *) = (BOOL (*)(id, SEL, UITextField *))[UITextField instanceMethodForSelector:_cmd];

        if (keyboardInputShouldDelete) {
            shouldDelete = keyboardInputShouldDelete(self, _cmd, textField);
        }
    }

    BOOL isIos8 = ([[[UIDevice currentDevice] systemVersion] intValue] == 8);
    BOOL isLessThanIos8_3 = ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.3f);

    if (![textField.text length] && isIos8 && isLessThanIos8_3) {
        [self deleteBackward];
    }

    return shouldDelete;
}

Чтобы объяснить немного, вызывали супер-реализацию этого метода, чтобы избежать потери унаследованного кода. После вызова -deleteBackward, если текст отсутствует, а версия iOS находится между 8-8.2.

РЕДАКТИРОВАТЬ: 1/28/15

Также может оказаться полезным подкласс метода -deleteBackward вашего подкласса UITextField. Это исправляет несколько условных ошибок. Один из них, если вы используете пользовательскую клавиатуру. Вот пример метода.

- (void)deleteBackward {
    BOOL shouldDismiss = [self.text length] == 0;

    [super deleteBackward];

    if (shouldDismiss) {
        if ([self.delegate respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)]) {
            [self.delegate textField:self shouldChangeCharactersInRange:NSMakeRange(0, 0) replacementString:@""];
        }
    }
}

РЕДАКТИРОВАТЬ: ответ переводится на Xamarin, поскольку исходный вопрос задал это для Xamarin.

    [Preserve]
    [Export("keyboardInputShouldDelete:")]
    private bool KeyboardInputShouldDelete(UITextField textField)
    {
        var shouldDelete = true;

        if(RespondsToSelector(new Selector("_cmd")))
        {
            //Call base class
            shouldDelete = Messaging.bool_objc_msgSend_IntPtr(Handle, Selector.GetHandle("_cmd"), textField.Handle);
        }

        //ios8 "bug": always call DeleteBackward even if the field is empty
        if(Utils.IsIos8)
        {
            DeleteBackward();
            return false;
        }

        return shouldDelete;
    }

Проверено на ios 7.1 и 8.1

РЕДАКТИРОВАТЬ: 4/14/15

В iOS 8.3 эта проблема исправлена. Код Objective-C обновлен, чтобы отразить изменения.

РЕДАКТИРОВАТЬ: 7/24/15

Как прокомментировал @nischalhada, существует состояние, в котором текстовые поля -textField:shouldChangeCharactersInRange:replacementString: вызывается дважды. Проблема связана с iOS >= 8.3 при использовании пользовательской клавиатуры. Мое решение не идеально, но оно выполняет эту работу, и я не уверен, есть ли другой способ. Поскольку оба вызова этого метода выполняются в одном и том же цикле запуска, мы будем использовать bool, чтобы отслеживать, когда выполнить код и отправить async на reset bool.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    BOOL toReturn = NO;

    if (!self.shouldTextFieldPreventChange) {
        self.shouldTextFieldPreventChange = YES;

        dispatch_async(dispatch_get_main_queue(), ^{
            // iOS8.3 custom keyboards might call this method along with internal iOS
            // code. Allowing changes on the next run loop helps avoid this issue.
            self.shouldTextFieldPreventChange = NO;
        });

        // do work...
    }

    return toReturn;
}

Ответ 2

Чтобы быть точным, это ошибка Apple iOS8. Это было сообщалось несколько раз на форумах разработчиков Apple. К сожалению, эти сообщения об ошибках не являются общедоступными, поэтому мы знаем только, что это все еще происходит с бета-версией.

Примечание: в целом (99%) большинство привязок Xamarin.iOS не являются специфическими для версии iOS (т.е. нет проверки версии), поэтому различное поведение между версиями iOS часто связано с дизайном (документированным) или с ошибками Apple (например, этот).