Как отклонить клавиатуру для UITextView с клавишей возврата?

В библиотеке IB введение говорит нам, что при нажатии клавиши возврата клавиатура для UITextView исчезнет. Но на самом деле ключ возврата может действовать только как \n.

Я могу добавить кнопку и использовать [txtView resignFirstResponder] спрятать клавиатуру

Но есть ли способ добавить действие для клавиши возврата на клавиатуре, чтобы мне не нужно было добавлять UIButton?

33 ответа

Решение

UITextView не имеет методов, которые будут вызываться, когда пользователь нажимает клавишу возврата. Если вы хотите, чтобы пользователь мог добавить только одну строку текста, используйте UITextField, Нажав кнопку возврата и скрывая клавиатуру UITextView не соответствует правилам интерфейса.

Даже тогда, если вы хотите сделать это, реализуйте textView:shouldChangeTextInRange:replacementText: метод UITextViewDelegate и в этой проверке, если текст замены \nскрыть клавиатуру

Могут быть и другие пути, но я не знаю ни одного.

Я подумал, что вместо этого выложу фрагмент:

Убедитесь, что вы заявляете о поддержке UITextViewDelegate протокол.

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

    if([text isEqualToString:@"\n"]) {
        [textView resignFirstResponder];
        return NO;
    }

    return YES;
}

Обновление Swift 4.0:

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    if text == "\n" {
        textView.resignFirstResponder()
        return false
    }
    return true
}

Я знаю, что на этот вопрос уже дан ответ, но мне не очень нравится использовать строковый литерал для новой строки, так что вот что я сделал.

- (BOOL)textView:(UITextView *)txtView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    if( [text rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location == NSNotFound ) {
        return YES;
    }

    [txtView resignFirstResponder];
    return NO;
}

Обновление Swift 4.0:

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if (text as NSString).rangeOfCharacter(from: CharacterSet.newlines).location == NSNotFound {
    return true
}
txtView.resignFirstResponder()
return false
}

Я знаю, что на это отвечали много раз, но вот мои два цента на проблему.

Я нашел ответы samvermette и ribeto очень полезными, а также комментарий maxpower в ответе ribeto. Но есть проблема с этими подходами. Проблема, о которой упоминает Мэтт в ответе samvermette, заключается в том, что если пользователь хочет вставить что-то с разрывом строки внутри, клавиатура будет скрыта без вставки чего-либо.

Таким образом, мой подход представляет собой смесь трех вышеупомянутых решений и проверяет, является ли введенная строка новой строкой, когда длина строки равна 1, поэтому мы проверяем, что пользователь печатает вместо вставки.

Вот что я сделал:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    NSRange resultRange = [text rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet] options:NSBackwardsSearch];
    if ([text length] == 1 && resultRange.location != NSNotFound) {
        [textView resignFirstResponder];
        return NO;
    }

    return YES;
}

Более элегантный способ - убрать клавиатуру, когда пользователь нажимает где-то за рамкой клавиатуры.

Во-первых, установите представление ViewController для класса "UIControl" в инспекторе идентичности в UIBuilder. Удерживая клавишу "Control", перетащите представление в файл заголовка ViewController и свяжите его как действие с событием "Touch Up Inside", например:

ViewController.h

-(IBAction)dismissKeyboardOnTap:(id)sender;

В основном файле ViewController ViewController.m:

-(IBAction)dismissKeyboardOnTap:(id)sender
    {
         [[self view] endEditing:YES];
    }

Вы можете требовать двойного касания или длительного касания, используя похожие приемы. Возможно, вам понадобится установить ваш ViewController как UITextViewDelegate и подключить TextView к ViewController. Этот метод работает как для UITextView, так и для UITextField.

Источник: Ранчо Большого Ботаника

РЕДАКТИРОВАТЬ: Я также хотел бы добавить, что если вы используете UIScrollView, вышеупомянутый метод может не так легко работать через Interface Builder. В этом случае вы можете использовать UIGestureRecognizer и вызвать вместо него метод [[self view] endEditing:YES] внутри него. Примером может быть:

-(void)ViewDidLoad{
    ....
    UITapGestureRecognizer *tapRec = [[UITapGestureRecognizer alloc] 
        initWithTarget:self action:@selector(tap:)];
    [self.view addGestureRecognizer: tapRec];
    ....
}

-(void)tap:(UITapGestureRecognizer *)tapRec{
    [[self view] endEditing: YES];
}

Когда пользователь нажимает за пределами клавиатуры и не нажимает пробел, клавиатура отклоняется.

Добавьте этот метод в свой контроллер представления.

Свифт:

func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
    if text == "\n" {
        textView.resignFirstResponder()
        return false
    }
    return true
}

Этот метод также может быть полезен для вас:

/**
Dismiss keyboard when tapped outside the keyboard or textView

:param: touches the touches
:param: event   the related event
*/
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    if let touch = touches.anyObject() as? UITouch {
        if touch.phase == UITouchPhase.Began {
            textField?.resignFirstResponder()
        }
    }
}
-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    if([text isEqualToString:@"\n"])
        [textView resignFirstResponder];
    return YES;
}

yourtextView.delegate=self;

Также добавьте UITextViewDelegate

Не забудьте подтвердить протокол

ЕСЛИ ты не добавил if([text isEqualToString:@"\n"]) Вы не можете редактировать

Я нашел ответ Джозебамы самым полным и чистым ответом, доступным в этой теме.

Ниже приведен синтаксис Swift 4 для него:

func textView(_ textView: UITextView, shouldChangeTextIn _: NSRange, replacementText text: String) -> Bool {
    let resultRange = text.rangeOfCharacter(from: CharacterSet.newlines, options: .backwards)
    if text.count == 1 && resultRange != nil {
        textView.resignFirstResponder()
        // Do any additional stuff here
        return false
    }
    return true
}

Аналогично другим ответам с помощью UITextViewDelegate но новый быстрый интерфейс isNewline было бы:

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    if let character = text.first, character.isNewline {
        textView.resignFirstResponder()
        return false
    }
    return true
}

Существует другое решение при использовании с uitextview. Вы можете добавить панель инструментов как InputAccessoryView в "textViewShouldBeginEditing", и с помощью кнопки "Готово" на этой панели инструментов вы можете отклонить клавиатуру, код для этого следующий:

В viewDidLoad

toolBar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, 320, 44)]; //toolbar is uitoolbar object
toolBar.barStyle = UIBarStyleBlackOpaque;
UIBarButtonItem *btnDone = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(btnClickedDone:)];
[toolBar setItems:[NSArray arrayWithObject:btnDone]];

В методе textviewdelegate

- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
     [textView setInputAccessoryView:toolBar];
     return YES;
}

В действии кнопки "Готово", которая находится на панели инструментов, следующее:

-(IBAction)btnClickedDone:(id)sender
{
    [self.view endEditing:YES];
}

Быстрый

func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
    if text == "\n" {
        textView.resignFirstResponder()
    }
    return true
}

и настроить

Использование контроллера навигации для размещения панели для отклонения клавиатуры:

в.h файле:

UIBarButtonItem* dismissKeyboardButton;

в.m файле:

- (void)viewDidLoad {
    dismissKeyboardButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(dismissKeyboard)];
}

-(void)textViewDidBeginEditing:(UITextView *)textView {
    self.navigationItem.rightBarButtonItem = dismissKeyboardButton;
}

-(void)textFieldDidBeginEditing:(UITextField *)textField {
    self.navigationItem.rightBarButtonItem = dismissKeyboardButton;
}

-(void)dismissKeyboard {
    [self.textField resignFirstResponder];
    [self.textView resignFirstResponder];
    //or replace this with your regular right button
    self.navigationItem.rightBarButtonItem = nil;
}

Добавить наблюдателя в viewDidLoad

[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(textViewKeyPressed:) name: UITextViewTextDidChangeNotification object: nil];

а затем используйте селектор, чтобы проверить наличие \ n

-(void) textViewKeyPressed: (NSNotification*) notification {

  if ([[[notification object] text] hasSuffix:@"\n"])
  {
    [[notification object] resignFirstResponder];
  }
}

Он использует "\n" и не проверяет ключ возврата, но я думаю, что это нормально.

ОБНОВИТЬ

Смотрите ответ Рибто ниже, который использует [NSCharacterSet newlineCharacterSet] на месте \n

Свифт Код

Реализуйте UITextViewDelegate в своем классе / Смотрите так:

class MyClass: UITextViewDelegate  { ...

установите делегата textView на себя

myTextView.delegate = self

А затем реализовать следующее:

  func textViewDidChange(_ textView: UITextView) {
    if textView.text.characters.count >= 1 {

        if let lastChar = textView.text.characters.last {

            if(lastChar == "\n"){

              textView.text = textView.text.substring(to: textView.text.index(before: textView.text.endIndex))
              textView.resignFirstResponder()
            }
        }
    }
}

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

Просто решил эту проблему по-другому.

  • Создайте кнопку, которая будет помещена в фоновом режиме
  • Из Инспектора атрибутов измените тип кнопки на пользовательский, и кнопка станет прозрачной.
  • Разверните кнопку, чтобы охватить весь вид, и убедитесь, что кнопка находится позади всех других объектов. Простой способ сделать это - перетащить кнопку в верхнюю часть списка в представлении.
  • Управление перетащите кнопку на viewController.h файл и создать действие (отправленное событие: Touch Up Inside), как:

    (IBAction)ExitKeyboard:(id)sender;
    
  • В ViewController.m должен выглядеть так:

    (IBAction)ExitKeyboard:(id)sender {
        [self.view endEditing:TRUE];
    }
    
  • Запустите приложение, и когда вы уйдете из TextView, клавиатура исчезнет

Как и матовый комментарий к samvermette, мне также не нравится идея обнаружения "\n". Клавиша "return" существует по причине в UITextView, то есть, конечно, для перехода к следующей строке.

Лучшее решение, на мой взгляд, состоит в том, чтобы имитировать приложение для сообщений iPhone - добавить панель инструментов (и кнопку) на клавиатуре.

Я получил код из следующего сообщения в блоге:

http://www.iosdevnotes.com/2011/02/iphone-keyboard-toolbar/

шаги:

-Добавить панель инструментов в ваш файл XIB - установите высоту 460

-Добавить элемент кнопки панели инструментов (если еще не добавлен). Если вам нужно выровнять его по правому краю, также добавьте элемент кнопки гибкой панели в XIB и переместите элемент кнопки панели инструментов

-Создайте действие, которое связывает ваш элемент кнопки с resignFirstResponder следующим образом:

- (IBAction)hideKeyboard:(id)sender {
    [yourUITextView resignFirstResponder];
}

-Затем:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];

    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

- (void)keyboardWillShow:(NSNotification *)notification {
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.3];

    CGRect frame = self.keyboardToolbar.frame;
    frame.origin.y = self.view.frame.size.height - 260.0;
    self.keyboardToolbar.frame = frame;

    [UIView commitAnimations];
}

- (void)keyboardWillHide:(NSNotification *)notification {
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.3];

    CGRect frame = self.keyboardToolbar.frame;
    frame.origin.y = self.view.frame.size.height;
    self.keyboardToolbar.frame = frame;

    [UIView commitAnimations];
}

Попробуй это:

 - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
    if ([text isEqualToString:@"\n"]) {
        [self.view endEditing:YES];
    }

    return YES;

}

// Вы можете использовать это...

Шаг 1. Первый шаг - убедиться, что вы заявили о поддержке UITextViewDelegate протокол. Это делается в вашем заголовочном файле, в качестве примера можно привести заголовок

EditorController.h:

@interface EditorController : UIViewController  {
  UITextView *messageTextView;
}

@property (nonatomic, retain) UITextView *messageTextView;

@end

Шаг 2. Далее вам нужно зарегистрировать контроллер как делегат UITextView. Продолжая из приведенного выше примера, вот как я инициализировал UITextView с EditorController как делегат...

- (id) init {
    if (self = [super init]) {
        // define the area and location for the UITextView
        CGRect tfFrame = CGRectMake(10, 10, 300, 100);
        messageTextView = [[UITextView alloc] initWithFrame:tfFrame];
        // make sure that it is editable
        messageTextView.editable = YES;

        // add the controller as the delegate
        messageTextView.delegate = self;
    }

Шаг 3. И теперь последняя часть головоломки - принять меры в ответ на shouldCahngeTextInRange сообщение следующее:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range 
  replacementText:(NSString *)text
{
    // Any new character added is passed in as the "text" parameter
    if ([text isEqualToString:@"\n"]) {
        // Be sure to test for equality using the "isEqualToString" message
        [textView resignFirstResponder];

        // Return FALSE so that the final '\n' character doesn't get added
        return FALSE;
    }
    // For any other character return TRUE so that the text gets added to the view
    return TRUE;
}

Я использовал этот код для смены респондента.

 - (BOOL)textView:(UITextView*) textView shouldChangeTextInRange: (NSRange) range replacementText: (NSString*) text
    {
        if ([text isEqualToString:@"\n"]) {
            //[textView resignFirstResponder];
            //return YES;
            NSInteger nextTag = textView.tag + 1;
            // Try to find next responder
            UIResponder* nextResponder = [self.view viewWithTag:nextTag];
            if (nextResponder) {
                // Found next responder, so set it.
                [nextResponder becomeFirstResponder];
            } else {
                // Not found, so remove keyboard.
                [textView resignFirstResponder];
            }
            return NO; 


            return NO;
        }
        return YES;

    }

Вы также можете скрыть клавиатуру при касании на экране просмотра:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
     UITouch * touch = [touches anyObject];
     if(touch.phase == UITouchPhaseBegan) {
        [txtDetail resignFirstResponder];
      }
 }

Хорошо. Все дали ответы с хитростями, но я думаю, что правильный путь для достижения этого

Подключение следующего действия к событию "Закончено ли при выходе" в Interface Builder, (щелкните правой кнопкой мыши TextField и cntrl-drag от 'Did end on exit' до следующего метода.

-(IBAction)hideTheKeyboard:(id)sender
{
    [self.view endEditing:TRUE];
}

Быстрый ответ:

override func viewDidLoad() {
    super.viewDidLoad()
    let tapGestureReconizer = UITapGestureRecognizer(target: self, action: "tap:")
    view.addGestureRecognizer(tapGestureReconizer)
}

func tap(sender: UITapGestureRecognizer) {
    view.endEditing(true)
}

Вопрос состоит в том, как сделать это с помощью клавиши возврата, но я думаю, что это может помочь кому-то с намерением просто заставить клавиатуру исчезнуть при использовании UITextView:

@IBOutlet weak var textView: UITextView!

private func addToolBarForTextView() {
  let textViewToolbar: UIToolbar = UIToolbar()
  textViewToolbar = UIBarStyle.Default
  textViewToolbar = [
    UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.Done, target: self, action: #selector(self.cancelInput)),
    UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: self, action: nil),
    UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.Done, target: self, action: #selector(self.doneInput))
  ]
  textViewToolbar()
  self.textView.inputAccessoryView = textViewToolbar //do it for every relevant textView if there are more than one
}

func doneInput() {
  self.textView.resignFirstResponder()
}

func cancelInput() {
  self.textView.text = ""
  self.textView.resignFirstResponder()
}

Вызовите addToolBarForTextView() в viewDidLoad или другом методе жизненного цикла.

Кажется, это было идеальное решение для меня.

Ура,

Мурат

Вы должны добавить UIToolbar наверх UITextView сделать проще, чем использовать shouldChangeTextIn

В Swift 4

let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50))
        toolbar.barStyle = .default
        toolbar.items = [
            UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil),
            UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(doneAction))
        ]
        textView.inputAccessoryView = toolbar
@objc func doneAction(){
 self.textView.resignFirstResponder()
}
+ (void)addDoneButtonToControl:(id)txtFieldOrTextView
{
    if([txtFieldOrTextView isKindOfClass:[UITextField class]])
    {
        txtFieldOrTextView = (UITextField *)txtFieldOrTextView;
    }
    else if([txtFieldOrTextView isKindOfClass:[UITextView class]])
    {
        txtFieldOrTextView = (UITextView *)txtFieldOrTextView;
    }

    UIToolbar* numberToolbar = [[UIToolbar alloc]initWithFrame:CGRectMake(0,
                                                                          0,
                                                                          [Global returnDeviceWidth],
                                                                          50)];
    numberToolbar.barStyle = UIBarStyleDefault;


    UIBarButtonItem *btnDone = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"btn_return"]
                                                                style:UIBarButtonItemStyleBordered
                                                               target:txtFieldOrTextView
                                                               action:@selector(resignFirstResponder)];

    numberToolbar.items = [NSArray arrayWithObjects:btnDone,nil];
    [numberToolbar sizeToFit];

    if([txtFieldOrTextView isKindOfClass:[UITextField class]])
    {
         ((UITextField *)txtFieldOrTextView).inputAccessoryView = numberToolbar;
    }
    else if([txtFieldOrTextView isKindOfClass:[UITextView class]])
    {
         ((UITextView *)txtFieldOrTextView).inputAccessoryView = numberToolbar;
    }
}
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range  replacementText:(NSString *)text
{
    if (range.length==0) {
        if ([text isEqualToString:@"\n"]) {
            [txtView resignFirstResponder];
            if(textView.returnKeyType== UIReturnKeyGo){

                [self PreviewLatter];
                return NO;
            }
            return NO;
        }
    }   return YES;
}

Для Xcode 6.4., Swift 1.2.:

   override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent)
    {
        super.touchesBegan(touches, withEvent: event)
        if let touch = touches.first as? UITouch
        {
            self.meaningTextview.resignFirstResponder()
        }
    }

Попробуй это.

NSInteger lengthOfText = [[textView.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length];

Я знаю, что это не точный ответ на этот вопрос, но я нашел эту ветку после поиска в интернете ответа. Я предполагаю, что другие разделяют это чувство.

Это мое отличие UITapGestureRecognizer, которое я считаю надежным и простым в использовании - просто установите делегат TextView на ViewController.

Вместо ViewDidLoad я добавляю UITapGestureRecognizer, когда TextView становится активным для редактирования:

-(void)textViewDidBeginEditing:(UITextView *)textView{
    _tapRec = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap:)];

    [self.view addGestureRecognizer: _tapRec];
    NSLog(@"TextView Did begin");
}

Когда я нажимаю вне TextView, представление завершает режим редактирования, и UITapGestureRecognizer удаляет себя, чтобы я мог продолжить взаимодействие с другими элементами управления в представлении.

-(void)tap:(UITapGestureRecognizer *)tapRec{
    [[self view] endEditing: YES];
    [self.view removeGestureRecognizer:tapRec];
    NSLog(@"Tap recognized, tapRec getting removed");
}

Надеюсь, это поможет. Это кажется таким очевидным, но я никогда не видел такого решения в Интернете - я что-то не так делаю?

Не забудьте установить делегат для textView - иначе resignfirstresponder не будет работать.

Другие вопросы по тегам