web-dev-qa-db-fra.com

Existe-t-il un moyen de demander à une vue iOS lequel de ses enfants a le statut de premier répondant?

Sous Mac OS X, vous pouvez trouver le premier répondant comme ceci:

[[self window] firstResponder]

Existe-t-il un moyen de le faire dans iOS? Ou avez-vous besoin d'énumérer les contrôles enfants et d'envoyer un isFirstRespondermessage à chacun d'eux?

43
Tommy Herbert

Vous devez parcourir tous les contrôles enfants et tester la propriété isFirstResponder. Lorsque vous rencontrez TRUE, sortez de la boucle.

UIView *firstResponder;
for (UIView *view in self.view.subviews) //: caused error
{
    if (view.isFirstResponder)
    {
        firstResponder = view;
        break;
    }
}

MEILLEURE SOLUTION

Voir réponse de Jakob .

11
Evan Mulawski

J'aime vraiment la solution de VJK , mais comme MattDiPasquale le suggère, cela semble plus complexe que nécessaire. J'ai donc écrit cette version plus simple:

Objectif c

#import "UIResponder+FirstResponder.h"

static __weak id currentFirstResponder;

@implementation UIResponder (FirstResponder)

+(id)currentFirstResponder {
    currentFirstResponder = nil;
    [[UIApplication sharedApplication] sendAction:@selector(findFirstResponder:) to:nil from:nil forEvent:nil];
    return currentFirstResponder;
}

-(void)findFirstResponder:(id)sender {
   currentFirstResponder = self;
}

@end

Swift 4

import UIKit

extension UIResponder {

    private static weak var _currentFirstResponder: UIResponder?

    static var currentFirstResponder: UIResponder? {
        _currentFirstResponder = nil
        UIApplication.shared.sendAction(#selector(UIResponder.findFirstResponder(_:)), to: nil, from: nil, for: nil)

        return _currentFirstResponder
    }

    @objc func findFirstResponder(_ sender: Any) {
        UIResponder._currentFirstResponder = self
    }
}

J'en ai également fait une méthode de classe car cela semblait plus logique. Vous pouvez maintenant trouver le premier répondant comme ceci: [UIResponder currentFirstResponder]


237
Jakob Egger

J'ai écrit une catégorie sur UIResponder pour trouver le premier répondant

@interface UIResponder (firstResponder)
- (id) currentFirstResponder;
@end

et

#import <objc/runtime.h>
#import "UIResponder+firstResponder.h"

static char const * const aKey = "first";

@implementation UIResponder (firstResponder)

- (id) currentFirstResponder {
    [[UIApplication sharedApplication] sendAction:@selector(findFirstResponder:) to:nil from:self forEvent:nil];
    id obj = objc_getAssociatedObject (self, aKey);
    objc_setAssociatedObject (self, aKey, nil, OBJC_ASSOCIATION_ASSIGN);
    return obj;
}

- (void) setCurrentFirstResponder:(id) aResponder {
    objc_setAssociatedObject (self, aKey, aResponder, OBJC_ASSOCIATION_ASSIGN);
}

- (void) findFirstResponder:(id) sender {
    [sender setCurrentFirstResponder:self];
}

@end

Ensuite, dans toute classe qui dérive d'un UIResponder, vous pouvez obtenir le premier répondeur en appelant

UIResponder* aFirstResponder = [self currentFirstResponder];

mais n'oubliez pas d'importer d'abord le fichier d'interface de la catégorie UIResponder!

Cela utilise des API documentées, il ne devrait donc pas y avoir de problèmes de rejet dans l'App Store.

25
VJK

Si vous avez besoin d'un premier répondeur pour pouvoir lui demander de démissionner, voici une approche pour obtenir la démission de n'importe lequel. UIView a une méthode qui parcourra toutes les sous-vues UIViews et demandera à tous ceux qui sont les premiers intervenants de démissionner.

[[self view] endEditing:YES];

Voici un lien vers les documents UIView d'Apple "Cette méthode examine la vue actuelle et sa hiérarchie de sous-vues pour le champ de texte qui est actuellement le premier répondeur. S'il en trouve un, il demande à ce champ de texte de démissionner en tant que premier répondant. Si le paramètre force est défini sur OUI, le champ de texte n'est même jamais demandé; il est forcé de démissionner. "

11
Praxiteles