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

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

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

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

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

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

Ответы [ 27 ]

1 голос
/ 22 сентября 2015

Я хотел бы поделиться с вами моей реализацией поиска первого респондента в любом месте 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 голос
/ 01 сентября 2011

Вы также можете попробовать вот так:

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

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

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

Я не пробовал, но, похоже, хорошее решение

1 голос
/ 11 ноября 2014

Вы можете назвать Privite API, как это, яблоко игнорировать:

UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
SEL sel = NSSelectorFromString(@"firstResponder");
UIView   *firstResponder = [keyWindow performSelector:sel];
1 голос
/ 29 мая 2014

Решение от romeo https://stackoverflow.com/a/2799675/661022 круто, но я заметил, что коду нужен еще один цикл. Я работал с tableViewController. Я отредактировал скрипт и затем проверил. Все работало отлично.

Я рекомендовал попробовать это:

- (void)findFirstResponder
{
    NSArray *subviews = [self.tableView subviews];
    for (id subv in subviews )
    {
        for (id cell in [subv 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]) {
                            NSLog(@"current textField: %@", theTextField);
                            NSLog(@"current textFields's superview: %@", [theTextField superview]);
                        }
                    }
                }
            }
        }
    }
}
1 голос
/ 11 октября 2013

Это хороший кандидат на рекурсию! Нет необходимости добавлять категорию в 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;
}
0 голосов
/ 22 мая 2018

Обновление: я был не прав. Вы действительно можете использовать 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
    }
}

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

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

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

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

let firstResponder = UIResponder.first
0 голосов
/ 15 ноября 2016

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

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