Ottieni l'attuale primo risponditore senza utilizzare un'API privata


340

Ho inviato la mia app poco più di una settimana fa e ho ricevuto l'e-mail di rifiuto temuta oggi. Mi dice che la mia app non può essere accettata perché sto usando un'API non pubblica; in particolare, dice,

L'API non pubblica inclusa nell'applicazione è firstResponder.

Ora, la chiamata API offensiva è in realtà una soluzione che ho trovato qui su SO:

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

Come posso ottenere l'attuale primo risponditore sullo schermo? Sto cercando un modo per non rifiutare la mia app.


5
Invece di ripetere le visualizzazioni, puoi usare questa magia davvero geniale : stackoverflow.com/a/14135456/746890
Chris Nolet,

Risposte:


338

In una delle mie applicazioni spesso desidero che il primo risponditore si dimetta se l'utente tocca lo sfondo. A tale scopo ho scritto una categoria su UIView, che chiamo su UIWindow.

Di seguito è basato su questo e dovrebbe restituire il primo risponditore.

@implementation UIView (FindFirstResponder)
- (id)findFirstResponder
{
    if (self.isFirstResponder) {
        return self;        
    }
    for (UIView *subView in self.subviews) {
        id responder = [subView findFirstResponder];
        if (responder) return responder;
    }
    return nil;
}
@end

iOS 7+

- (id)findFirstResponder
{
    if (self.isFirstResponder) {
        return self;
    }
    for (UIView *subView in self.view.subviews) {
        if ([subView isFirstResponder]) {
            return subView;
        }
    }
    return nil;
}

Swift:

extension UIView {
    var firstResponder: UIView? {
        guard !isFirstResponder else { return self }

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

        return nil
    }
}

Esempio di utilizzo in Swift:

if let firstResponder = view.window?.firstResponder {
    // do something with `firstResponder`
}

278
Perché questa è una soluzione migliore della semplice chiamata [self.view endEditing:YES]?
Tim Sullivan,

69
@Tim non ho potuto farlo. È ovviamente un modo molto più semplice di dimettersi dal primo soccorritore. Non aiuta con le domande originali, tuttavia, che era identificare il primo soccorritore.
Thomas Müller,

17
Inoltre, questa è una risposta migliore perché potresti voler fare qualcos'altro con il primo soccorritore piuttosto che dimetterlo ...
Erik B

3
Va notato che questo trova solo UIViews, non altri UIResponders che potrebbero anche essere il primo risponditore (ad esempio UIViewController o UIApplication).
Patrick Pijnappel,

10
Perché la differenza per iOS 7? non vorresti anche ricorrere in quel caso?
Peter DeWeese,

531

Se il tuo obiettivo finale è solo quello di dimettersi dal primo soccorritore, questo dovrebbe funzionare: [self.view endEditing:YES]


122
Per tutti voi che affermate che questa è la risposta alla "domanda", potete dare un'occhiata alla domanda reale? La domanda chiede come ottenere l'attuale primo risponditore. Non come dimettersi dal primo soccorritore.
Justin Kredible il

2
e dove dovremmo chiamare questo self.view endEditing?
user4951

8
Abbastanza vero che questo non risponde esattamente alla domanda, ma chiaramente è una risposta molto utile. Grazie!
NovaJoe,

6
+1 anche se non è una risposta alla domanda. Perché? Perché molti vengono qui con una domanda in mente che questa risposta risponde :) Almeno 267 (al momento) di loro ...
Rok Jarc

1
Questo non risponde alla domanda.
Sasho,

186

Un modo comune di manipolare il primo soccorritore è utilizzare zero azioni mirate. Questo è un modo per inviare un messaggio arbitrario alla catena del risponditore (a partire dal primo risponditore) e continuare lungo la catena fino a quando qualcuno non risponde al messaggio (ha implementato un metodo corrispondente al selettore).

Nel caso del licenziamento della tastiera, questo è il modo più efficace che funzionerà indipendentemente dalla finestra o dalla vista che risponde per primo:

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

Questo dovrebbe essere più efficace del solito [self.view.window endEditing:YES].

(Grazie a BigZaphod per avermi ricordato il concetto)


4
Questo è veramente forte. Forse il trucco di Cocoa Touch più pulito che abbia mai visto su SO. Mi ha aiutato senza fine!
mluisbrown,

questa è di gran lunga la migliore soluzione, grazie mille! questo è molto meglio della soluzione sopra questa, perché funziona anche del campo di testo attivo fa parte della vista accessoria della tastiera (in quel caso non fa parte della nostra gerarchia di viste)
Pizzaiola Gorgonzola

2
Questa soluzione è la soluzione ottimale. Il sistema sa già chi è il primo soccorritore, non è necessario trovarlo.
Leftspin

2
Voglio dare a questa risposta più di un voto. Questo è un genio spaventoso.
Reid Main,

1
devios1: La risposta di Jakob è esattamente ciò che la domanda richiede, ma è un hack in cima al sistema di risposta, piuttosto che usare il sistema di risposta nel modo in cui è stato progettato. Questo è più pulito e realizza ciò per cui la maggior parte delle persone si rivolge a questa domanda, e anche in una sola riga. (Ovviamente puoi inviare qualsiasi messaggio di stile azione-bersaglio, e non solo dimettersiFirstResponder).
Nevyn,

98

Ecco una categoria che ti consente di trovare rapidamente il primo risponditore chiamando [UIResponder currentFirstResponder]. Aggiungi i seguenti due file al tuo progetto:

UIResponder + FirstResponder.h

#import <Cocoa/Cocoa.h>
@interface UIResponder (FirstResponder)
    +(id)currentFirstResponder;
@end

UIResponder + FirstResponder.m

#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

Il trucco qui è che l'invio di un'azione a zero lo invia al primo risponditore.

(Originariamente ho pubblicato questa risposta qui: https://stackoverflow.com/a/14135456/322427 )


1
+1 Questa è la soluzione più elegante al problema (e alla vera domanda posta) che ho ancora visto. Riutilizzabile ed efficiente! Vota questa risposta!
devios1

3
Bravo. Questo potrebbe essere solo per l'hacking più bello e pulito che abbia mai visto per Objc ...
Aviel Gross

Molto bella. Dovrebbe funzionare davvero bene per poter quindi chiedere al primo risponditore chi (se qualcuno) nella catena gestirà un particolare metodo di azione se dovesse essere inviato. Puoi usarlo per abilitare o disabilitare i controlli in base alla possibilità di gestirli. Risponderò alla mia domanda su questo e farò riferimento alla tua risposta.
Benjohn,


Avviso: questo non funziona se si avvia l'aggiunta di più finestre. Purtroppo non posso ancora indicare una causa oltre a ciò.
Heath Borders,

41

Ecco un'estensione implementata in Swift basata sulla risposta più eccellente di Jakob Egger:

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
    }
}

1
Risposta modificata a per swift1.2 dove sono supportate var statiche.
nacho4d,

Ciao, quando stampo il risultato di questo su una nuovissima applicazione a vista singola in viewDidAppear () ottengo zero. La vista non dovrebbe essere la prima risposta?
farhadf,

@LinusGeffarth Non ha più senso chiamare il metodo statico currentinvece di first?
Codice comandante

Immagino di averlo modificato. Mi sembra di aver abbandonato la parte importante quando ho abbreviato il nome della variabile.
LinusGeffarth,

29

Non è carino, ma il modo in cui mi dimetto dal primo risponditore quando non so cosa sia il risponditore:

Crea un UITextField, in IB o programmaticamente. Rendilo nascosto. Collegalo al tuo codice se lo hai creato in IB.

Quindi, quando si desidera chiudere la tastiera, si passa al risponditore nel campo di testo invisibile e si dimette immediatamente:

    [self.invisibleField becomeFirstResponder];
    [self.invisibleField resignFirstResponder];

61
Questo è allo stesso tempo orribile e geniale. Bello.
Alex Wayne,

4
Trovo che sia un po 'pericoloso - Apple potrebbe decidere un giorno che rendere un controllo nascosto diventare il primo risponditore non è un comportamento previsto e "correggere" iOS per non farlo più, e quindi il tuo trucco potrebbe smettere di funzionare.
Thomas Tempelmann,

2
Oppure puoi assegnare il primoRisponditore a un UITextField che è attualmente visibile e quindi chiamare resignFirstResponder su quel particolare campo di testo. Ciò elimina la necessità di creare una vista nascosta. Tuttavia, +1.
Swifty McSwifterton,

3
Penso piuttosto che sia solo orribile ... potrebbe cambiare il layout della tastiera (o la vista di input) prima di nasconderlo
Daniel,

19

Per una versione Swift 3 e 4 della risposta di nevyn :

UIApplication.shared.sendAction(#selector(UIView.resignFirstResponder), to: nil, from: nil, for: nil)

10

Ecco una soluzione che riporta il primo risponditore corretto (molte altre soluzioni non segnalano un UIViewControllercome primo risponditore, ad esempio), non richiede il ciclo nella gerarchia di viste e non utilizza API private.

Sfrutta il metodo Apple sendAction: a: da: forEvent: che già sa come accedere al primo risponditore.

Dobbiamo solo modificarlo in 2 modi:

  • Estendi in UIRespondermodo che possa eseguire il nostro codice sul primo risponditore.
  • Sottoclasse UIEventper restituire il primo risponditore.

Ecco il codice:

@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

7

Il primo risponditore può essere qualsiasi istanza della classe UIResponder, quindi ci sono altre classi che potrebbero essere il primo risponditore nonostante le UIVview. Ad esempio, UIViewControllerpotrebbe anche essere il primo risponditore.

In questa sintesi troverai un modo ricorsivo per ottenere il primo risponditore eseguendo il ciclo attraverso la gerarchia dei controller a partire dal rootViewController delle finestre dell'applicazione.

Puoi recuperare il primo risponditore facendo

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

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

Tuttavia, se il primo risponditore non è una sottoclasse di UIView o UIViewController, questo approccio fallirà.

Per risolvere questo problema, possiamo UIResponderadottare un approccio diverso creando una categoria ed eseguendo uno swizzeling magico per essere in grado di costruire un array di tutte le istanze viventi di questa classe. Quindi, per ottenere il primo risponditore, possiamo semplicemente iterare e chiedere ad ogni oggetto se -isFirstResponder.

Questo approccio può essere trovato implementato in quest'altro aspetto .

Spero che sia d'aiuto.


7

Usando Swift e con un oggetto specificoUIView questo potrebbe aiutare:

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
}

Inseriscilo nel tuo UIViewControllere usalo in questo modo:

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

Prendi nota che il risultato è un valore opzionale, quindi sarà nullo nel caso in cui non sia stato trovato firstResponser nelle viste secondarie delle viste fornite.


5

Scorrere le viste che potrebbero essere il primo risponditore e utilizzare - (BOOL)isFirstResponderper determinare se lo sono attualmente.


4

Peter Steinberger ha appena twittato la notifica privata UIWindowFirstResponderDidChangeNotification, che puoi osservare se vuoi guardare il primo cambio di risposta.


Viene rifiutato perché usare un'API privata, forse usare anche una notifica privata potrebbe essere una cattiva idea ...
Laszlo

4

Se devi solo uccidere la tastiera quando l'utente tocca un'area di sfondo, perché non aggiungere un riconoscimento gesti e usarlo per inviare il [[self view] endEditing:YES]messaggio?

puoi aggiungere il Riconoscimento gesti Tap nel file xib o storyboard e collegarlo a un'azione,

sembra qualcosa del genere quindi finito

- (IBAction)displayGestureForTapRecognizer:(UITapGestureRecognizer *)recognizer{
     [[self view] endEditing:YES];
}

Questo ha funzionato alla grande per me. Ho scelto una connessione per una vista sullo Xib, e posso aggiungere altre viste per invocare in Referencing Outlet Collections
James Perih

4

Solo in questo caso ecco la versione Swift del fantastico approccio di Jakob Egger:

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

Questo è quello che ho fatto per trovare quello che UITextField è il primoRisponditore quando l'utente fa clic su Salva / Annulla in un 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

Questo è ciò che ho nella mia categoria UIViewController. Utile per molte cose, incluso ottenere il primo soccorritore. I blocchi sono fantastici!

- (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

Puoi scegliere la seguente UIViewestensione per ottenerla (credito di Daniel) :

extension UIView {
    var firstResponder: UIView? {
        guard !isFirstResponder else { return self }
        return subviews.first(where: {$0.firstResponder != nil })
    }
}

1

Puoi provare anche così:

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

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

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

Non l'ho provato ma sembra una buona soluzione


1

La soluzione di romeo https://stackoverflow.com/a/2799675/661022 è interessante, ma ho notato che il codice ha bisogno di un altro loop. Stavo lavorando con tableViewController. Ho modificato lo script e poi ho controllato. Tutto ha funzionato perfettamente.

Ho raccomandato di provare questo:

- (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]);
                        }
                    }
                }
            }
        }
    }
}

Grazie! Hai ragione, la maggior parte delle risposte presentate qui non è una soluzione quando si lavora con uitableview. Potresti semplificare molto il tuo codice, persino eliminare if if ([textField isKindOfClass:[UITextField class]]), consentendo l'uso di uitextview e restituire il firstresponder.
Frade,

1

Questo è un buon candidato per la ricorsione! Non è necessario aggiungere una categoria a UIView.

Utilizzo (dal controller della vista):

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

Codice:

// 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

puoi chiamare API private in questo modo, apple ignora:

UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
SEL sel = NSSelectorFromString(@"firstResponder");
UIView   *firstResponder = [keyWindow performSelector:sel];

1
ma ... per favore usa '
respondsToSelector

Ora controllo "respondsToSelector" ma ricevo ancora un avviso, questo potrebbe non essere sicuro. Posso liberarmi dell'avvertimento in qualche modo?
ecth

1

Versione rapida della risposta di @ 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
    }

}

1

Ho un approccio leggermente diverso rispetto alla maggior parte qui. Invece di scorrere la raccolta di visualizzazioni cercando quella isFirstResponderimpostata, invio anch'io un messaggio a nil, ma memorizzo il destinatario (se presente), quindi restituisco quel valore in modo che il chiamante ottenga l'istanza effettiva (di nuovo, se presente) .

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
    }
}

Posso quindi dare le dimissioni dal primo risponditore, se presente, facendo questo ...

return UIResponder.first?.resignFirstResponder()

Ma ora posso anche fare questo ...

if let firstResponderTextField = UIResponder?.first as? UITextField {
    // bla
}

1

Vorrei condividere con te la mia implementazione per trovare il primo soccorritore in qualsiasi parte di UIView. Spero sia d'aiuto e mi dispiace per il mio inglese. Grazie

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

0

Codice sotto lavoro.

- (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

Aggiornamento: ho sbagliato. Puoi infatti utilizzare UIApplication.shared.sendAction(_:to:from:for:)per chiamare il primo risponditore dimostrato in questo link: http://stackoverflow.com/a/14135456/746890 .


La maggior parte delle risposte qui non riesce davvero a trovare l'attuale primo risponditore se non si trova nella gerarchia della vista. Ad esempio, AppDelegateo UIViewControllersottoclassi.

C'è un modo per garantirti di trovarlo anche se il primo oggetto responder non è un UIView .

Innanzitutto, consente di implementarne una versione inversa, usando la nextproprietà di UIResponder:

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

Con questa proprietà calcolata, possiamo trovare l'attuale primo risponditore dal basso verso l'alto anche se non lo è UIView. Ad esempio, da a viewaUIViewController chi lo sta gestendo, se il controller di visualizzazione è il primo risponditore.

Tuttavia, abbiamo ancora bisogno di una risoluzione dall'alto verso il basso, una sola varper ottenere l'attuale primo risponditore.

Innanzitutto con la gerarchia delle viste:

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

Questo cercherà il primo risponditore all'indietro e, se non riuscisse a trovarlo, direbbe ai suoi sottoview di fare la stessa cosa (perché il suo sottoview nextnon è necessariamente se stesso). Con questo possiamo trovarlo da qualsiasi vista, incluso UIWindow.

E infine, possiamo costruire questo:

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

Quindi, quando si desidera recuperare il primo risponditore, è possibile chiamare:

let firstResponder = UIResponder.first

0

Il modo più semplice per trovare il primo soccorritore:

func sendAction(_ action: Selector, to target: Any?, from sender: Any?, for event: UIEvent?) -> Bool

L'implementazione predefinita invia il metodo di azione all'oggetto target specificato o, se non viene specificato alcun target, al primo risponditore.

Passo successivo:

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

    @objc
    private func firstResponderWhereYouAre(sender: AnyObject)
    {
        UIResponder.first = self
    }

    static var actualFirst: UIResponder?
    {
        UIApplication.shared.sendAction(#selector(findFirstResponder(sender:)), to: nil, from: nil, for: nil)
        return UIResponder.first
    }
}

Uso: basta ottenere UIResponder.actualFirstper i propri scopi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.