Özel bir API kullanmadan geçerli ilk yanıtlayıcıyı edinin


340

Uygulamamı bir haftadan biraz önce gönderdim ve bugün korkunç e-posta e-postasını aldım. Herkese açık olmayan bir API kullandığım için uygulamamın kabul edilemeyeceğini söylüyor; özellikle diyor ki,

Başvurunuzda bulunan herkese açık olmayan API, ilk Yanıtlayıcıdır.

Şimdi, rahatsız edici API çağrısı aslında burada SO buldum bir çözüm:

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

Geçerli ilk yanıtlayıcıyı ekrana nasıl getirebilirim? Uygulamamı reddetmeyecek bir yol arıyorum.


5
Görüntülemeleri yinelemek yerine, bu gerçekten mükemmel büyüyü kullanabilirsiniz: stackoverflow.com/a/14135456/746890
Chris Nolet

Yanıtlar:


338

Uygulamalarımdan birinde, kullanıcı arka plana dokunursa ilk yanıtlayıcının istifa etmesini isterim. Bu amaçla UIView üzerine UIWindow'da çağırdığım bir kategori yazdım.

Aşağıdakiler buna dayanmaktadır ve ilk yanıtlayıcıyı döndürmelidir.

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

Swift'teki kullanım örneği:

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

278
Bu neden sadece aramaktan daha iyi bir çözüm [self.view endEditing:YES]?
Tim Sullivan

69
@Zaman ben bunu yapamazdın. İlk yanıtlayıcıyı istifa etmenin çok daha basit bir yolu olduğu açıktır. Yine de, ilk yanıtlayıcıyı tanımlamak için orijinal sorulara yardımcı olmaz.
Thomas Müller

17
Ayrıca, bu daha iyi bir cevaptır, çünkü ilk yanıtlayanla istifa etmekten başka bir şey yapmak isteyebilirsiniz ...
Erik B

3
Bunun sadece UIViews bulduğunu, aynı zamanda ilk cevaplayıcı olabilecek diğer UIResponder'ları (örneğin UIViewController veya UIApplication) bulduğuna dikkat edilmelidir.
Patrick Pijnappel

10
İOS 7 için neden fark var? bu durumda da tekrarlamak istemez miydin?
Peter DeWeese

531

Nihai amacınız sadece ilk cevaplayıcıyı istifa etmekse, bu işe yaramalıdır: [self.view endEditing:YES]


122
Hepiniz bunun "sorunun" cevabı olduğunu söylerken, asıl soruya bir göz atabilir misiniz? Soru, geçerli ilk yanıtlayıcıyı nasıl alacağınızı sorar. İlk yanıtlayıcının nasıl istifa edeceği değil.
Justin Kredible

2
ve bu self.view endEditing adını nereye vermeliyiz?
user4951

8
Bunun soruyu tam olarak cevaplamadığı, ancak açıkça çok yararlı bir cevap olduğu doğru. Teşekkürler!
NovaJoe

6
Sorunun cevabı olmasa bile +1. Neden? Çünkü birçoğu bu cevabın cevap verdiğini akılda tutarak bir soruya geliyor :) En azından 267 tanesi (şu anda) ...
Rok Jarc

1
Bu soruya cevap vermiyor.
Sasho

186

İlk yanıtlayıcıyı değiştirmenin yaygın bir yolu, sıfır hedefli eylemler kullanmaktır. Bu, yanıtlayan zincirine rastgele bir mesaj göndermenin (ilk yanıtlayıcıdan başlayarak) ve birisi iletiye yanıt verene kadar (seçiciyle eşleşen bir yöntem uygulayana kadar) zinciri devam ettirmenin bir yoludur.

Klavyenin kapatılması durumunda, hangi pencere veya görünüm ilk yanıtlayıcı olursa olsun çalışacak en etkili yol budur:

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

Bu daha da etkili olmalı [self.view.window endEditing:YES].

( BigZaphod'a bana konsepti hatırlattığı için teşekkürler )


4
Bu çok havalı. Muhtemelen henüz SO'da gördüğüm en güzel Cocoa Touch hilesi. Bana hiç yardımcı olmadı!
mluisbrown

Bu FAR TARAFINDAN en iyi çözüm, bir milyon teşekkürler! Bu, bunun üstündeki çözümden çok daha iyi, çünkü aktif metin alanının bile klavyenin aksesuar görünümünün bir parçası olduğu (bu durumda görünüm hiyerarşimizin bir parçası değildir)
Pizzaiola Gorgonzola

2
Bu çözüm en iyi uygulama çözümüdür. Sistem zaten ilk cevaplayıcının kim olduğunu biliyor, onu bulmaya gerek yok.
leftspin

2
Bu cevabı birden fazla oy vermek istiyorum. Bu ürkütücü bir dahi.
Reid Main

1
devios1: Jakob'un cevabı tam olarak sorunun sormasını ister, ancak yanıtlama sistemini tasarlandığı şekilde kullanmak yerine yanıtlama sisteminin tepesinde bir hack'tir. Bu daha temizdir ve çoğu insanın bu soruya ne için geldiğini ve tek satırda başarır. (Elbette, sadece resignFirstResponder değil, herhangi bir hedef eylem stili mesajı da gönderebilirsiniz).
nevyn

98

İşte ilk yanıtlayıcıyı arayarak hızlı bir şekilde bulmanızı sağlayan bir kategori [UIResponder currentFirstResponder]. Projenize aşağıdaki iki dosyayı eklemeniz yeterlidir:

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

Buradaki hile, nil'e bir eylem göndermenin ilk yanıtlayıcıya göndermesidir.

(Aslında bu cevabı burada yayınladım: https://stackoverflow.com/a/14135456/322427 )


1
+1 Bu, henüz gördüğüm soruna (ve sorulan asıl soruya) en şık çözümdür. Yeniden kullanılabilir ve verimli! Bu yanıtı oyla!
devios1

3
Bravo. Bu sadece Objc için gördüğüm en güzel ve temiz kesmek olabilir ...
Gross

Çok hoş. Daha sonra ilk yanıtlayana kimin zincirde (eğer varsa) gönderilecek belirli bir işlem yöntemini işleyeceğini sorabilmek için gerçekten iyi çalışmalıdır. Denetimleri, işlenip işlenemediklerine göre etkinleştirmek veya devre dışı bırakmak için kullanabilirsiniz. Bu konuda kendi sorumu cevaplayacağım ve cevabınızı referans alacağım .
Benjohn


Uyarı: Birden çok pencere eklemeye başlarsanız bu çalışmaz. Maalesef bunun ötesinde bir nedene işaret edemiyorum.
Heath Borders

41

Swift'de Jakob Egger'in en mükemmel cevabına dayanan bir Uzantı:

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

Hızlı 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
Statik değişkenin desteklendiği swift1.2 için değiştirilmiş cevap.
nacho4d

Merhaba, bunun sonucunu viewDidAppear () içinde yepyeni bir tek görünüm uygulaması üzerine yazdırdığımda nil alıyorum. Görüş ilk cevap veren olmamalı mı?
farhadf

@LinusGeffarth currentBunun yerine statik yöntemi çağırmak daha anlamlı değil firstmi?
Kod Komutanı

Tahmin et, düzenledi. Değişken adını kısaltırken önemli kısmı düşürdüm.
LinusGeffarth

29

Güzel değil, ama cevaplayıcının ne olduğunu bilmediğimde firstResponder'ı istifa etmenin yolu:

IB veya programlı olarak bir UITextField oluşturun. Gizli yap. IB'de yaptıysanız kodunuza bağlayın.

Ardından, klavyeyi kapatmak istediğinizde, yanıtlayıcıyı görünmez metin alanına geçirir ve hemen istifa edersiniz:

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

61
Bu aynı zamanda hem korkunç hem de dahidir. Güzel.
Alex Wayne

4
Biraz tehlikeli buluyorum - Apple bir gün gizli bir kontrolün ilk yanıtlayıcı haline gelmesinin amaçlanan davranış olmadığı kararını verebilir ve iOS'u artık bunu yapmamak için "düzeltir" ve sonra hile çalışmayı durdurabilir.
Thomas Tempelmann

2
Veya firstResponder'ı görünür durumda olan bir UITextField öğesine atayabilir ve daha sonra o textField üzerinde resignFirstResponder öğesini çağırabilirsiniz. Bu, gizli bir görünüm oluşturma ihtiyacını ortadan kaldırır. Hepsi aynı, +1.
Swifty McSwifterton

3
Daha doğrusu sadece korkunç olduğunu düşünüyorum ... gizlemeden önce klavye düzenini (veya giriş görünümünü) değiştirebilir
Daniel

19

Nevyn'in cevabının Swift 3 ve 4 versiyonu için :

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

10

İşte doğru ilk yanıtlayıcıyı bildiren bir çözüm (diğer birçok çözüm a'yı ilk yanıtlayıcı UIViewControllerolarak raporlamaz ), görünüm hiyerarşisinde döngü gerektirmez ve özel API'ler kullanmaz.

İlk yanıtlayıcıya nasıl erişileceğini zaten bilen Apple'ın sendAction: to: from: forEvent: yöntemini kullanır .

Sadece 2 şekilde ayarlamamız gerekiyor:

  • UIResponderİlk yanıtlayıcıda kendi kodumuzu yürütebilmesi için genişletin .
  • UIEventİlk yanıtlayıcıyı döndürmek için alt sınıf .

İşte kod:

@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

İlk yanıtlayıcı UIResponder sınıfının herhangi bir örneği olabilir, bu nedenle UIViews'a rağmen ilk yanıtlayıcı olabilecek başka sınıflar da vardır. Örneğin UIViewController, ilk cevaplayıcı da olabilir.

Gelen bu Ana fikir uygulamanın pencerelerin rootViewController başlayarak denetleyicilerin hiyerarşisi döngü tarafından ilk yanıtlayıcısını almak için özyinelemeli yol bulacaktır.

Ardından ilk yanıtlayıcıyı

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

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

Ancak, ilk yanıtlayıcı UIView veya UIViewController alt sınıfı değilse, bu yaklaşım başarısız olur.

Bu sorunu çözmek için, üzerinde bir kategori oluşturarak farklı bir yaklaşım UIResponderyapabiliriz ve bu sınıfın tüm canlı örneklerinden oluşan bir dizi oluşturabilmek için sihirli bir büyü yapabiliriz. Daha sonra, ilk yanıtlayıcıyı almak için basit yineleme yapabilir ve her nesneye olup olmadığını sorabiliriz -isFirstResponder.

Bu yaklaşım, bu diğer özde de uygulanabilir .

Umarım yardımcı olur.


7

Swift'i kullanmak ve belirli bir UIViewnesneyle bu yardımcı olabilir:

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
}

Sadece içine yerleştirin UIViewControllerve şu şekilde kullanın:

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

Sonucun Opsiyonel bir değer olduğuna dikkat edin, bu nedenle verilen görünümler alt görüntülemelerinde ilk Yanıtlayıcı bulunmaması durumunda sıfır olacaktır.


5

İlk yanıtlayıcı olabilecek görünümleri yineleyin ve - (BOOL)isFirstResponderşu anda olup olmadığını belirlemek için kullanın .


4

Peter Steinberger , ilk Yanıtlayıcı değişikliğini izlemek isteyip istemediğinizi görebileceğiniz özel bildirimi tweetlediUIWindowFirstResponderDidChangeNotification .


Özel bir API kullanarak, belki de özel bir bildirim kullanmak da kötü bir fikir olabilir çünkü reddedildi ...
Laszlo

4

Kullanıcı bir arka plan alanına dokunduğunda klavyeyi öldürmeniz gerekiyorsa neden bir hareket tanıyıcı eklemeyin ve [[self view] endEditing:YES]mesajı göndermek için kullanmıyorsunuz ?

Dokunma hareketi tanıyıcıyı xib veya film şeridi dosyasına ekleyebilir ve bir eyleme bağlayabilirsiniz,

böyle bir şey görünüyor sonra bitmiş

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

Bu benim için harika çalıştı. Xib'deki bir görünüme tek bir bağlantı seçtim ve bunun için başvurmak için ek görünümler ekleyebilirim Referans Çıkışı Koleksiyonları
James Perih

4

Buradaki durum harika Jakob Egger'in yaklaşımının Swift versiyonudur:

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

Kullanıcı bir ModalViewController Kaydet / İptal'i tıklattığında ne UITextField firstResponder olduğunu bulmak için yaptım:

    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

Bu benim UIViewController Kategorisi ne var. İlk cevaplayıcı dahil olmak üzere birçok şey için kullanışlıdır. Bloklar harika!

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

Almak için aşağıdaki UIViewuzantıyı seçebilirsiniz (Daniel tarafından kredi) :

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

1

Bunu da deneyebilirsiniz:

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

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

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

Denemedim ama iyi bir çözüm gibi görünüyor


1

Romeo https://stackoverflow.com/a/2799675/661022 çözüm harika, ama kodun bir döngü daha gerektiğini fark ettim. TableViewController ile çalışıyordum. Senaryoyu düzenledim ve sonra kontrol ettim. Her şey mükemmel çalıştı.

Bunu denemek için recommed:

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

Teşekkürler! Doğru, burada sunulan cevapların çoğu uitableview ile çalışırken bir çözüm değildir. Kodunuzu çok basitleştirebilir, hatta if ([textField isKindOfClass:[UITextField class]])if'ı çıkarabilir, uitextview için kullanıma izin verebilir ve ilk yanıtlayıcıyı döndürebilirsiniz.
Frade

1

Bu özyineleme için iyi bir aday! UIView'e kategori eklemenize gerek yok.

Kullanımı (görünüm denetleyicinizden):

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

Kod:

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

Bunun gibi privit api diyebilirsiniz, elma yoksay:

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

1
ama ... kontrol etmek için lütfen '
responsesToSelector

Şimdi 'responsesToSelector'ı kontrol ediyorum ama hala bir uyarı alıyorum, bu güvensiz olabilir. Bir şekilde uyarıdan kurtulabilir miyim?
ecth

1

@ Thomas-müller'in yanıtının hızlı versiyonu

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

Buradakinden biraz daha farklı bir yaklaşımım var. isFirstResponderAyarlanmış olanı arayan görünümler koleksiyonunu yinelemek yerine , ben de bir mesaj gönderirim nil, ancak alıcıyı (varsa) saklarım, daha sonra arayanın gerçek örneği (tekrar varsa) alması için bu değeri döndürürüm .

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

Daha sonra bunu yaparak ilk yanıtlayıcıyı istifa edebilirim ...

return UIResponder.first?.resignFirstResponder()

Ama şimdi bunu da yapabilirim ...

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

1

UIView'in herhangi bir yerinde ilk yanıtlayıcıyı bulmak için uygulamamı sizinle paylaşmak istiyorum. Umarım İngilizcem için yardımcı olur ve özür dilerim. Teşekkürler

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

Aşağıdaki kod çalışır.

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

Güncelleme: Yanılmışım. Gerçekten UIApplication.shared.sendAction(_:to:from:for:)bu bağlantıda gösterilen ilk yanıtlayıcıyı aramak için kullanabilirsiniz : http://stackoverflow.com/a/14135456/746890 .


Buradaki yanıtların çoğu, görünüm hiyerarşisinde değilse geçerli ilk yanıtlayıcıyı gerçekten bulamaz. Örneğin AppDelegateveya UIViewControlleralt sınıflar.

İlk yanıtlayıcı nesnesi a olmasa bile, onu bulmanızı garanti etmenin bir yolu vardır UIView.

İlk olarak, şu nextözelliği kullanarak tersine çevrilmiş bir sürümünü uygulayalım UIResponder:

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

Bu hesaplanan özellik sayesinde, geçerli olmasa bile, ilk yanıtlayıcıyı aşağıdan yukarıya doğru bulabiliriz UIView. Örneğin, bir gelen viewetmek UIViewControllergörünüm denetleyicisi ilk savunan ise, onu kimin yönettiğini.

Ancak, varmevcut ilk yanıtlayıcıyı elde etmek için yine de yukarıdan aşağıya bir çözünürlüğe ihtiyacımız var .

Önce görünüm hiyerarşisinde:

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

Bu, ilk yanıtlayıcıyı geriye doğru arayacaktır ve bulamazsa, alt görünümlerine aynı şeyi yapmasını söyler (çünkü alt görünümünün nextkendisi olması gerekmez). Bununla, dahil olmak üzere herhangi bir görünümden bulabilirsiniz UIWindow.

Ve son olarak, bunu yapabiliriz:

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

İlk yanıtlayıcıyı almak istediğinizde şunu arayabilirsiniz:

let firstResponder = UIResponder.first

0

İlk yanıtlayıcıyı bulmanın en kolay yolu:

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

Varsayılan uygulama, eylem yöntemini verilen hedef nesneye veya hedef belirtilmezse ilk yanıtlayıcıya gönderir.

Sonraki adım:

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

Kullanımı: Sadece UIResponder.actualFirstkendi amaçlarınız için olsun .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.