Как определить, какое текстовое поле имеет фокус в быстрой [дубликат]

Используйте margin-left: x%;, где x - половина ширины элемента.

322
задан Mark Amery 13 January 2014 в 00:26
поделиться

24 ответа

С помощью Swift и с конкретным объектом UIView это может помочь:

func findFirstResponder(inView view: UIView) -> UIView? {
    for subView in view.subviews as! [UIView] {
        if subView.isFirstResponder() {
            return subView
        }

        if let recursiveSubView = self.findFirstResponder(inView: subView) {
            return recursiveSubView
        }
    }

    return nil
}

Просто поместите его в свой UIViewController и используйте его следующим образом:

let firstResponder = self.findFirstResponder(inView: self.view)

Обратите внимание, что результат является необязательным значением, поэтому он будет равен нулю, если в рассматриваемых представлениях не было найдено firstResponser.

312
ответ дан Dschee 22 August 2018 в 16:21
поделиться
  • 1
    Почему это лучшее решение, чем просто вызов [self.view endEditing:YES]? – Tim Sullivan 20 August 2010 в 23:25
  • 2
    @ Тит, я не мог этого сделать. Очевидно, это гораздо более простой способ смириться с первым ответчиком. Однако это не помогает с оригинальными вопросами, которые должны были идентифицировать первого ответчика. – Thomas Müller 23 August 2010 в 00:01
  • 3
    Кроме того, это лучший ответ, потому что вы, возможно, захотите сделать что-то еще с первым ответчиком, чем оставить его ... – Erik B 5 March 2014 в 15:48
  • 4
    – Peter DeWeese 31 July 2014 в 21:04
  • 5
    Этот код является технически правильным, но его нельзя использовать повторно и аганом. Правильный способ отставки первого ответчика - stackoverflow.com/a/11768282/2648673 – Marek H 23 August 2014 в 14:49
315
ответ дан Dschee 5 November 2018 в 13:56
поделиться

Если вам просто нужно убить клавиатуру, когда пользователь вступает в фоновый режим, почему бы не добавить распознаватель жестов и использовать его для отправки сообщения [[self view] endEditing:YES]?

вы можете добавить распознаватель жеста Tap в файле xib или раскадровки и подключить его к действию,

выглядит примерно так, а затем закончен

- (IBAction)displayGestureForTapRecognizer:(UITapGestureRecognizer *)recognizer{
     [[self view] endEditing:YES];
}
4
ответ дан Alladinian 22 August 2018 в 16:21
поделиться
  • 1
    Это отлично поработало для меня. Я выбрал одно соединение с представлением на Xib, и я могу добавить дополнительные представления для этого, чтобы ссылаться на ссылки в сборниках Outlet – James Perih 2 September 2013 в 08:57

Это то, что у меня есть в моей категории UIViewController. Полезно для многих вещей, включая получение первого ответчика. Блоки великолепны!

- (UIView*) enumerateAllSubviewsOf: (UIView*) aView UsingBlock: (BOOL (^)( UIView* aView )) aBlock {

 for ( UIView* aSubView in aView.subviews ) {
  if( aBlock( aSubView )) {
   return aSubView;
  } else if( ! [ aSubView isKindOfClass: [ UIControl class ]] ){
   UIView* result = [ self enumerateAllSubviewsOf: aSubView UsingBlock: aBlock ];

   if( result != nil ) {
    return result;
   }
  }
 }    

 return nil;
}

- (UIView*) enumerateAllSubviewsUsingBlock: (BOOL (^)( UIView* aView )) aBlock {
 return [ self enumerateAllSubviewsOf: self.view UsingBlock: aBlock ];
}

- (UIView*) findFirstResponder {
 return [ self enumerateAllSubviewsUsingBlock:^BOOL(UIView *aView) {
  if( [ aView isFirstResponder ] ) {
   return YES;
  }

  return NO;
 }];
}
2
ответ дан Andrei Tchijov 22 August 2018 в 16:21
поделиться

Это некрасиво, но способ, которым я увольняю первый ответчик, когда я не знаю, что такое ответчик:

Создайте UITextField, либо в IB, либо программно. Сделайте это Скрытым. Ссылка на ваш код, если вы сделали это в IB.

Затем, когда вы хотите убрать клавиатуру, вы переключите ответчик в невидимое текстовое поле и немедленно смирите его:

    [self.invisibleField becomeFirstResponder];
    [self.invisibleField resignFirstResponder];
29
ответ дан cannyboy 22 August 2018 в 16:21
поделиться
  • 1
    Это одновременно и ужасно, и гениально. Ницца. – Alex Wayne 2 June 2010 в 18:54
  • 2
    Я считаю, что это немного опасно - Apple может решить, что однажды создание скрытого элемента управления станет первым ответчиком, а не предполагаемым поведением и «исправить». iOS не делать этого больше, и тогда ваш трюк может перестать работать. – Thomas Tempelmann 24 October 2012 в 14:16
  • 3
    Или вы можете назначить firstResponder UITextField, который в настоящее время виден, а затем вызвать resignFirstResponder на этом конкретном текстовом поле. Это устраняет необходимость создания скрытого представления. Все равно +1. – Swifty McSwifterton 17 June 2013 в 00:51
  • 4
    Я скорее думаю, что это только ужасно ... это может изменить раскладку клавиатуры (или входное представление), прежде чем скрыть ее – Daniel 23 October 2014 в 09:25

Если ваша конечная цель - просто уйти в отставку первого ответчика, это должно работать: [self.view endEditing:YES]

522
ответ дан cdyson37 22 August 2018 в 16:21
поделиться
  • 1
    Ибо вы все говорите, что это ответ на «вопрос», можете ли вы взглянуть на реальный вопрос? Вопрос спрашивает, как получить текущего первого ответчика. Не как уволить первого ответчика. – Justin Kredible 4 January 2012 в 22:02
  • 2
    и где мы должны называть это self.view endEditing? – J. Chang 13 November 2012 в 12:47
  • 3
    Правда, это точно не отвечает на этот вопрос, но, безусловно, это очень полезный ответ. Благодаря! – NovaJoe 29 November 2012 в 17:52
  • 4
    – Rok Jarc 8 May 2013 в 18:02
  • 5
    Это не отвечает на вопрос. – Sasho 7 March 2014 в 11:12
88
ответ дан Community 22 August 2018 в 16:21
поделиться

Это хороший кандидат на рекурсию! Нет необходимости добавлять категорию в UIView.

Использование (из вашего контроллера вида):

UIView *firstResponder = [self findFirstResponder:[self view]];

Код:

// This is a recursive function
- (UIView *)findFirstResponder:(UIView *)view {

    if ([view isFirstResponder]) return view; // Base case

    for (UIView *subView in [view subviews]) {
        if ([self findFirstResponder:subView]) return subView; // Recursion
    }
    return nil;
}
1
ответ дан Daniel 22 August 2018 в 16:21
поделиться

Вы также можете попробовать следующее:

- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event { 

    for (id textField in self.view.subviews) {

        if ([textField isKindOfClass:[UITextField class]] && [textField isFirstResponder]) {
            [textField resignFirstResponder];
        }
    }
} 

Я не пробовал, но кажется хорошим решением

1
ответ дан Frade 22 August 2018 в 16:21
поделиться

Peter Steinberger просто написал твиттер о частном уведомлении UIWindowFirstResponderDidChangeNotification, который вы можете наблюдать, если хотите посмотреть изменение первого ретранслятора.

4
ответ дан Heath Borders 22 August 2018 в 16:21
поделиться
  • 1
    Он отклоняется, потому что использование частного api, возможно, использование частного уведомления также может быть плохой идеей ... – Laszlo 20 November 2013 в 03:13

вы можете вызвать privite api следующим образом: apple ignore:

UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
SEL sel = NSSelectorFromString(@"firstResponder");
UIView   *firstResponder = [keyWindow performSelector:sel];
1
ответ дан ibcker 22 August 2018 в 16:21
поделиться
  • 1
    но ... пожалуйста, используйте «responsesToSelector», чтобы проверить – ibcker 11 November 2014 в 11:13
  • 2
    Теперь я проверяю «отвечает наТелектор», но я все еще получаю предупреждение, это может быть небезопасно. Могу ли я как-нибудь избавиться от предупреждения? – ecth 8 April 2015 в 13:45
5
ответ дан Johan Kool 22 August 2018 в 16:21
поделиться

Код ниже работает.

- (id)ht_findFirstResponder
{
    //ignore hit test fail view
    if (self.userInteractionEnabled == NO || self.alpha <= 0.01 || self.hidden == YES) {
        return nil;
    }
    if ([self isKindOfClass:[UIControl class]] && [(UIControl *)self isEnabled] == NO) {
        return nil;
    }

    //ignore bound out screen
    if (CGRectIntersectsRect(self.frame, [UIApplication sharedApplication].keyWindow.bounds) == NO) {
        return nil;
    }

    if ([self isFirstResponder]) {
        return self;
    }

    for (UIView *subView in self.subviews) {
        id result = [subView ht_findFirstResponder];
        if (result) {
            return result;
        }
    }
    return nil;
}   
0
ответ дан John Chen 22 August 2018 в 16:21
поделиться

Вот расширение, реализованное в Swift на основе наилучшего ответа Якоба Эггера:

import UIKit

extension UIResponder {
    // Swift 1.2 finally supports static vars!. If you use 1.1 see: 
    // http://stackoverflow.com/a/24924535/385979
    private weak static var _currentFirstResponder: UIResponder? = nil

    public class func currentFirstResponder() -> UIResponder? {
        UIResponder._currentFirstResponder = nil
        UIApplication.sharedApplication().sendAction("findFirstResponder:", to: nil, from: nil, forEvent: nil)
        return UIResponder._currentFirstResponder
    }

    internal func findFirstResponder(sender: AnyObject) {
        UIResponder._currentFirstResponder = self
    }
}

Swift 4

import UIKit    

extension UIResponder {
    private weak static var _currentFirstResponder: UIResponder? = nil

    public static var current: UIResponder? {
        UIResponder._currentFirstResponder = nil
        UIApplication.shared.sendAction(#selector(findFirstResponder(sender:)), to: nil, from: nil, for: nil)
        return UIResponder._currentFirstResponder
    }

    @objc internal func findFirstResponder(sender: AnyObject) {
        UIResponder._currentFirstResponder = self
    }
}
34
ответ дан LinusGeffarth 22 August 2018 в 16:21
поделиться
  • 1
    Модифицированный ответ на swift1.2, где поддерживается статический var. – nacho4d 27 June 2015 в 03:17
  • 2
    Привет, когда я распечатываю результат этого в совершенно новом приложении с одним представлением в viewDidAppear (), я получаю нуль. Разве это не должно быть первым ответчиком? – farhadf 23 November 2015 в 17:39
  • 3
    @LinusGeffarth Не имеет смысла называть статический метод current вместо first? – Code Commander 7 February 2018 в 20:02
  • 4
    Угадайте, отредактировал. Похоже, что я отказался от важной части при сокращении имени переменной. – LinusGeffarth 7 February 2018 в 20:16

Общим способом манипулирования первым ответчиком является использование нулевых целевых действий. Это способ отправить произвольное сообщение в цепочку ответчиков (начиная с первого ответчика) и продолжить цепочку, пока кто-то не ответит на сообщение (реализовал метод, соответствующий селектору).

Для случай отклонения клавиатуры, это самый эффективный способ, который будет работать независимо от того, какое окно или представление является первым ответчиком:

[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];

Это должно быть более эффективным, чем даже [self.view.window endEditing:YES].

(Благодаря BigZaphod для напоминания мне о понятии)

172
ответ дан nevyn 22 August 2018 в 16:21
поделиться
  • 1
    Это так круто. Возможно, самый аккуратный трюк Cocoa Touch, который я видел на SO еще. Не помогло мне! – mluisbrown 1 June 2013 в 01:28
  • 2
    это BY FAR лучшее решение, спасибо миллион! это намного лучше, чем решение выше этого, потому что оно работает даже с активным текстовым полем, является частью вспомогательного представления клавиатуры (в этом случае оно не является частью нашей иерархии представлений) – Pizzaiola Gorgonzola 30 August 2013 в 15:50
  • 3
    Это решение является наилучшим решением. Система уже знает, кто первый ответчик, нет нужды ее находить. – leftspin 7 March 2014 в 22:35
  • 4
    Я хочу дать ответ больше, чем один. Это гениальный гений. – Reid Main 18 April 2014 в 03:35
  • 5
    devios1: Ответ Якоба - именно то, что задает вопрос, но это взломать систему ответчиков, вместо того, чтобы использовать систему ответчиков так, как она была разработана. Это чище и позволяет большинству людей прийти к этому вопросу и в однострочном режиме. (Вы можете, конечно, отправить любое сообщение стиля целевого действия, а не просто resignFirstResponder). – nevyn 26 January 2017 в 01:59

Для версии ответа Невина Swift 3:

UIApplication.shared.sendAction(#selector(UIView.resignFirstResponder), to: nil, from: nil, for: nil)
9
ответ дан Pochi 22 August 2018 в 16:21
поделиться
  • 1
    Единственное, что сработало! Благодаря! – Joshua Hart 23 August 2017 в 12:50
  • 2
    Спасибо, мне не удалось сменить селекторную часть на Swift. – Oscar A. Esquivel Oviedo 26 March 2018 в 17:51
3
ответ дан Romeo 22 August 2018 в 16:21
поделиться

Я хотел бы поделиться с вами своей реализацией, чтобы найти первого ответчика в любом месте UIView. Надеюсь, это поможет и извините за мой английский. Спасибо

+ (UIView *) findFirstResponder:(UIView *) _view {

UIView *retorno;

for (id subView in _view.subviews) {

    if ([subView isFirstResponder])
        return subView;

    if ([subView isKindOfClass:[UIView class]]) {
        UIView *v = subView;

        if ([v.subviews count] > 0) {
            retorno = [self findFirstResponder:v];
            if ([retorno isFirstResponder]) {
                return retorno;
            }
        }
    }
}

return retorno;

}

1
ответ дан Rubens Iotti 22 August 2018 в 16:21
поделиться

Вот решение, в котором сообщается о правильном первом ответчике (многие другие решения, например, не сообщают об UIViewController в качестве первого ответчика), не требует циклизации по иерархии представлений и не использует частные API .

Он использует метод Apple sendAction: to: from: forEvent: , который уже знает, как получить доступ к первому ответчику.

Нам просто нужно настроить это двумя способами:

  • Расширить UIResponder, чтобы он мог выполнить наш собственный код для первого ответчика.
  • Подкласс UIEvent, чтобы вернуть первого ответчика.

Вот код:

@interface ABCFirstResponderEvent : UIEvent
@property (nonatomic, strong) UIResponder *firstResponder;
@end

@implementation ABCFirstResponderEvent
@end

@implementation UIResponder (ABCFirstResponder)
- (void)abc_findFirstResponder:(id)sender event:(ABCFirstResponderEvent *)event {
    event.firstResponder = self;
}
@end

@implementation ViewController

+ (UIResponder *)firstResponder {
    ABCFirstResponderEvent *event = [ABCFirstResponderEvent new];
    [[UIApplication sharedApplication] sendAction:@selector(abc_findFirstResponder:event:) to:nil from:nil forEvent:event];
    return event.firstResponder;
}

@end
10
ответ дан Senseful 22 August 2018 в 16:21
поделиться

В этом случае речь идет о Swift-версии удивительного подхода Якоба Эггера:

import UIKit

private weak var currentFirstResponder: UIResponder?

extension UIResponder {

    static func firstResponder() -> UIResponder? {
        currentFirstResponder = nil
        UIApplication.sharedApplication().sendAction(#selector(self.findFirstResponder(_:)), to: nil, from: nil, forEvent: nil)
        return currentFirstResponder
    }

    func findFirstResponder(sender: AnyObject) {
        currentFirstResponder = self
    }

}
3
ответ дан Valentin Shergin 22 August 2018 в 16:21
поделиться

Первым ответчиком может быть любой экземпляр класса UIResponder, поэтому есть другие классы, которые могут быть первым ответчиком, несмотря на UIViews. Например, UIViewController также может быть первым ответчиком.

В этот gist вы найдете рекурсивный способ получить первого ответчика, перейдя через иерархию контроллеров, начиная с rootViewController окон приложения.

Вы можете получить первый ответчик, выполнив

- (void)foo
{
    // Get the first responder
    id firstResponder = [UIResponder firstResponder];

    // Do whatever you want
    [firstResponder resignFirstResponder];      
}

Однако, если первый ответчик не является подклассом UIView или UIViewController, этот подход не сработает.

Чтобы исправить это проблема, мы можем сделать другой подход, создав категорию на UIResponder и выполнить волшебное свидание, чтобы иметь возможность построить массив всех живых экземпляров этого класса. Затем, чтобы получить первого ответчика, мы можем простую итерацию и спросить каждый объект, если -isFirstResponder.

Этот подход можно найти в в этом другом gist .

Надеюсь, что это поможет.

7
ответ дан vilanovi 22 August 2018 в 16:21
поделиться

Обновление: я был неправ. Вы действительно можете использовать UIApplication.shared.sendAction(_:to:from:for:) для вызова первого ответчика, продемонстрированного в этой ссылке: http://stackoverflow.com/a/14135456/746890 .


Большая часть ответы здесь не могут действительно найти текущего первого ответчика, если он не находится в иерархии представлений. Например, AppDelegate или UIViewController подклассы.

Существует способ гарантировать, что вы найдете его, даже если первый объект-ответчик не является UIView.

Сначала позволяет реализовать обратную версию, используя свойство next в UIResponder:

extension UIResponder {
    var nextFirstResponder: UIResponder? {
        return isFirstResponder ? self : next?.nextFirstResponder
    }
}

С помощью этого вычисленного свойства мы можем найти текущего первого ответчика снизу вверх, даже если это не UIView. Например, от view до UIViewController, который управляет им, если контроллер представления является первым ответчиком.

Однако нам по-прежнему необходимо разрешение сверху вниз, одно var - получить текущий ответчик.

Сначала с иерархией представления:

extension UIView {
    var previousFirstResponder: UIResponder? {
        return nextFirstResponder ?? subviews.compactMap { $0.previousFirstResponder }.first
    }
}

Это будет искать первого ответчика назад, и если он не сможет его найти, он скажет его подпункты делать то же самое (потому что его subview next не обязательно сам). С этим мы можем найти его из любого вида, включая UIWindow.

И, наконец, мы можем построить это:

extension UIResponder {
    static var first: UIResponder? {
        return UIApplication.shared.windows.compactMap({ $0.previousFirstResponder }).first
    }
}

Итак, когда вы хотите получить первого ответчика, вы можете позвонить:

let firstResponder = UIResponder.first
0
ответ дан yesleon 22 August 2018 в 16:21
поделиться
88
ответ дан Community 5 November 2018 в 13:56
поделиться
0
ответ дан MarqueIV 5 November 2018 в 13:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: