Получить текущий первый респондент без использования частного API - PullRequest
328 голосов
/ 01 декабря 2009

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

Непубличный API, включенный в ваше приложение, это firstResponder.

Теперь вызывающий вызов API - это фактически решение, которое я нашел здесь в SO:

UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
UIView   *firstResponder = [keyWindow performSelector:@selector(firstResponder)];

Как вывести текущего первого респондента на экран? Я ищу способ, которым мое приложение не будет отклонено.

Ответы [ 27 ]

5 голосов
/ 01 декабря 2009

Переберите представления, которые могут быть первым респондентом, и используйте - (BOOL)isFirstResponder, чтобы определить, являются ли они в настоящий момент.

4 голосов
/ 07 мая 2013

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

4 голосов
/ 28 мая 2016

В данном случае 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
    }

}
4 голосов
/ 27 июня 2013

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

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

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

- (IBAction)displayGestureForTapRecognizer:(UITapGestureRecognizer *)recognizer{
     [[self view] endEditing:YES];
}
3 голосов
/ 10 мая 2010

Это то, что я сделал, чтобы узнать, какой UITextField является первым ответчиком, когда пользователь нажимает кнопку Сохранить / Отмена в ModalViewController:

    NSArray *subviews = [self.tableView subviews];

for (id cell in subviews ) 
{
    if ([cell isKindOfClass:[UITableViewCell class]]) 
    {
        UITableViewCell *aCell = cell;
        NSArray *cellContentViews = [[aCell contentView] subviews];
        for (id textField in cellContentViews) 
        {
            if ([textField isKindOfClass:[UITextField class]]) 
            {
                UITextField *theTextField = textField;
                if ([theTextField isFirstResponder]) {
                    [theTextField resignFirstResponder];
                }

            }
        }

    }

}
2 голосов
/ 24 января 2012

Это то, что у меня есть в моей категории 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 голосов
/ 29 апреля 2012

С категорией UIResponder можно по закону попросить объект UIApplication сообщить вам, кто является первым респондентом.

Смотрите это:

Есть ли способ спросить у iOS, кто из ее детей имеет статус первого респондента?

1 голос
/ 17 мая 2019

Вы можете выбрать следующее UIView расширение, чтобы получить его (кредит от Daniel) :

extension UIView {
    var firstResponder: UIView? {
        guard !isFirstResponder else { return self }
        return subviews.first(where: {$0.firstResponder != nil })
    }
}
1 голос
/ 15 октября 2018

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

import UIKit

private var _foundFirstResponder: UIResponder? = nil

extension UIResponder{

    static var first:UIResponder?{

        // Sending an action to 'nil' implicitly sends it to the
        // first responder, where we simply capture it for return
        UIApplication.shared.sendAction(#selector(UIResponder.storeFirstResponder(_:)), to: nil, from: nil, for: nil)

        // The following 'defer' statement executes after the return
        // While I could use a weak ref and eliminate this, I prefer a
        // hard ref which I explicitly clear as it's better for race conditions
        defer {
            _foundFirstResponder = nil
        }

        return _foundFirstResponder
    }

    @objc func storeFirstResponder(_ sender: AnyObject) {
        _foundFirstResponder = self
    }
}

Затем я могу подать в отставку первого респондента, если таковой имеется, выполнив это ...

return UIResponder.first?.resignFirstResponder()

Но теперь я тоже могу это сделать ...

if let firstResponderTextField = UIResponder?.first as? UITextField {
    // bla
}
1 голос
/ 04 апреля 2016

Swift версия ответа @ thomas-müller

extension UIView {

    func firstResponder() -> UIView? {
        if self.isFirstResponder() {
            return self
        }

        for subview in self.subviews {
            if let firstResponder = subview.firstResponder() {
                return firstResponder
            }
        }

        return nil
    }

}
...