एक निजी एपीआई का उपयोग किए बिना वर्तमान पहला उत्तरदाता प्राप्त करें


340

मैंने एक सप्ताह पहले अपना ऐप जमा किया और आज घबराई अस्वीकृति ईमेल प्राप्त की। यह बताता है कि मेरा ऐप स्वीकार नहीं किया जा सकता क्योंकि मैं एक गैर-सार्वजनिक एपीआई का उपयोग कर रहा हूं; विशेष रूप से, यह कहता है,

आपके एप्लिकेशन में शामिल किया गया गैर-सार्वजनिक API फर्स्ट रेंजर है।

अब, अपमानजनक एपीआई कॉल वास्तव में एक समाधान है जो मुझे एसओ पर यहां मिला है:

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

मुझे स्क्रीन पर वर्तमान पहला उत्तरदाता कैसे मिलेगा? मैं एक ऐसे रास्ते की तलाश कर रहा हूं जो मेरे ऐप को अस्वीकार नहीं करेगा।


5
विचारों पर ध्यान देने के बजाय, आप वास्तव में जादू के इस शानदार बिट का उपयोग कर सकते हैं : stackoverflow.com/a/14135456/746890
क्रिस नॉलेट

जवाबों:


338

यदि मेरे उपयोगकर्ता बैकग्राउंड पर टैप करते हैं तो मैं अपने किसी एप्लिकेशन में पहला रिस्पॉन्डर चाहता हूं। इस उद्देश्य के लिए मैंने UIView पर एक श्रेणी लिखी, जिसे मैं UIWindow पर कॉल करता हूं।

निम्नलिखित उस पर आधारित है और पहले उत्तरदाता को वापस करना चाहिए।

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

स्विफ्ट:

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

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

        return nil
    }
}

स्विफ्ट में उपयोग उदाहरण:

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

278
यह केवल कॉल करने से बेहतर समाधान क्यों है [self.view endEditing:YES]?
टिम सुलिवन

69
@ मुझे नहीं लगता कि आप ऐसा कर सकते हैं। यह स्पष्ट रूप से पहले उत्तरदाता के इस्तीफे का एक बहुत सरल तरीका है। यह मूल प्रश्नों के साथ मदद नहीं करता है, हालांकि, जो पहले उत्तरदाता की पहचान करना था।
थॉमस मुलर

17
इसके अलावा, यह एक बेहतर उत्तर है क्योंकि आप इसे इस्तीफा देने की तुलना में पहले उत्तरदाता के साथ कुछ और करना चाहते हैं ...
एरिक बी

3
यह ध्यान दिया जाना चाहिए कि यह केवल UIViews को ढूंढता है, अन्य UIResponders को नहीं जो पहला उत्तरदाता हो सकता है (जैसे UIViewController या UIApplication)।
पैट्रिक पिजनपेल

10
IOS 7 के लिए अंतर क्यों? क्या आप उस मामले में भी पुनर्विचार नहीं करना चाहेंगे?
पीटर डिविज़ जूल

531

यदि आपका अंतिम उद्देश्य पहले उत्तरदाता को इस्तीफा देना है, तो यह काम करना चाहिए: [self.view endEditing:YES]


122
आप सभी के लिए यह कहना कि यह "प्रश्न" का उत्तर है, क्या आप वास्तविक प्रश्न पर एक नज़र डाल सकते हैं? प्रश्न पूछता है कि वर्तमान पहले उत्तरदाता को कैसे प्राप्त किया जाए। पहले उत्तरदाता को इस्तीफा देने का तरीका नहीं।
जस्टिन क्रेडिबल

2
और हमें इस स्व.व्यू एंडेडिटिंग को कहां पर कॉल करना चाहिए?
user4951

8
यह सच है कि यह वास्तव में सवाल का जवाब नहीं दे रहा है, लेकिन स्पष्ट रूप से यह एक बहुत ही उपयोगी जवाब है। धन्यवाद!
नोवाजे नोव

6
+1 भी अगर यह सवाल का जवाब नहीं है। क्यों? क्योंकि कई लोग एक सवाल को ध्यान में रखते हैं कि यह उत्तर उत्तर देता है :) उनमें से कम से कम 267 (फिलहाल) ...
रोक जर्क

1
यह सवाल का जवाब नहीं दे रहा है।
साशो

186

पहले प्रत्युत्तर में हेरफेर करने का एक सामान्य तरीका शून्य लक्षित कार्यों का उपयोग करना है। यह उत्तरदाता श्रृंखला (पहले प्रत्युत्तर के साथ शुरू) के लिए एक मनमाना संदेश भेजने का एक तरीका है, और श्रृंखला को जारी रखने तक जब तक कोई संदेश का जवाब नहीं देता (चयनकर्ता से मेल खाता एक तरीका लागू किया है)।

कीबोर्ड को खारिज करने के मामले में, यह सबसे प्रभावी तरीका है जो काम करेगा चाहे वह कोई भी खिड़की या दृश्य पहले उत्तरदाता हो:

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

यह इससे भी अधिक प्रभावी होना चाहिए [self.view.window endEditing:YES]

( अवधारणा को याद दिलाने के लिए BigZaphod का धन्यवाद )


4
यह बहुत अच्छा है। संभवतः सबसे स्वच्छ कोको टच चाल मैंने अभी तक एसओ पर देखी है। कोई अंत नहीं मेरी मदद की!
मुलिसब्रोन

यह सबसे अच्छा उपाय है, एक लाख धन्यवाद! यह इस एक से ऊपर के समाधान से बेहतर है, क्योंकि यह सक्रिय टेक्स्टफ़ील्ड के भी काम करता है, कीबोर्ड के एक्सेसरी व्यू का हिस्सा है (उस स्थिति में यह हमारे दृश्य पदानुक्रम का हिस्सा नहीं है)
पिज़ायोला गोर्गोन्ज़ोला

2
यह समाधान सबसे अच्छा अभ्यास समाधान है। सिस्टम पहले से ही जानता है कि पहला उत्तरदाता कौन है, इसे खोजने की कोई आवश्यकता नहीं है।
leftspin

2
मैं इस उत्तर को एक से अधिक उत्थान देना चाहता हूं। यह प्रतिभाशाली प्रतिभा है।
मुख्य

1
devios1: जैकब का जवाब वास्तव में वही है जो सवाल पूछता है, लेकिन यह उत्तरदाता प्रणाली के शीर्ष पर एक हैक है, इसके बजाय प्रत्युत्तर प्रणाली का उपयोग करने के तरीके से किया गया है। यह क्लीनर है और पूरा करता है कि ज्यादातर लोग इस प्रश्न के लिए क्या आते हैं, और एक-लाइनर में भी। (आप निश्चित रूप से कोई भी लक्ष्य-एक्शन स्टाइल संदेश भेज सकते हैं, और न केवल इस्तीफा दे सकते हैं।
नेविन

98

यहां एक श्रेणी है जो आपको कॉल करके पहले उत्तरदाता को जल्दी से ढूंढने की अनुमति देता है [UIResponder currentFirstResponder]। बस अपनी परियोजना में निम्नलिखित दो फाइलें जोड़ें:

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

यहाँ चाल यह है कि शून्य करने के लिए एक क्रिया भेजना पहले प्रत्युत्तर के लिए भेजता है।

(मैंने मूल रूप से इस उत्तर को यहां प्रकाशित किया है: https://stackoverflow.com/a/14135456/322427 )


1
+1 यह समस्या का सबसे सुरुचिपूर्ण समाधान है (और पूछा गया वास्तविक प्रश्न) मैंने अभी तक देखा है। पुन: प्रयोज्य और कुशल! इस उत्तर को बढ़ाएं!
devios1

3
वाहवाही। यह सिर्फ सबसे सुंदर और साफ हैक के लिए हो सकता है जो मैंने ओब्जेक के लिए कभी देखा है ...
ग्रॉस

बहुत अच्छा। तब के लिए वास्तव में अच्छी तरह से काम करना चाहिए पहले चेन जो (अगर किसी को भी) एक विशेष कार्रवाई विधि को संभालने के लिए यह भेजने के लिए सक्षम थे पूछने के लिए सक्षम होना चाहिए। आप इसका उपयोग नियंत्रणों को सक्षम या अक्षम करने के लिए कर सकते हैं, भले ही वे नियंत्रित करने में सक्षम हों। मैं इस पर अपने स्वयं के प्रश्न का उत्तर दूंगा और आपके उत्तर का संदर्भ दूंगा।
16


चेतावनी: यदि आप कई विंडो जोड़ना शुरू नहीं करते हैं तो यह काम नहीं करता है। मैं दुर्भाग्यवश उस कारण से आगे बढ़ने का संकेत नहीं दे सकता।
हीथ बॉर्डर्स

41

यहां जैकब एगर के सबसे उत्कृष्ट उत्तर के आधार पर स्विफ्ट में लागू एक एक्सटेंशन दिया गया है:

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

स्विफ्ट 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
स्विफ्ट 1.2 के लिए संशोधित उत्तर जहां स्थिर संस्करण समर्थित हैं।
nacho4d

नमस्ते, जब मैं viewDidAppear () में एक नया आवेदन देखने पर इसका परिणाम निकालता हूं, तो मुझे शून्य मिलता है। क्या पहली प्रतिक्रिया नहीं होनी चाहिए?
Farhadf

@LinusGeffarth currentइसके बजाय स्थैतिक विधि को कॉल करने के लिए अधिक समझ में नहीं आता है first?
कोड कमांडर

ऐसा लगता है, इसे संपादित किया। मुझे लगता है कि चर नाम को संक्षिप्त करते समय महत्वपूर्ण हिस्सा गिर गया है।
LinusGeffarth

29

यह सुंदर नहीं है, लेकिन जिस तरह से मैंने फर्स्ट रिप्रेज़ेंट को इस्तीफा दे दिया, जब मुझे नहीं पता कि रिस्पॉन्डर क्या है:

IB या प्रोग्रामेटिक रूप से, एक UITextField बनाएँ। इसे छिपाओ। यदि आपने इसे IB में बनाया है तो इसे अपने कोड से लिंक करें।

फिर, जब आप कीबोर्ड को खारिज करना चाहते हैं, तो आप रिस्पोंडर को अदृश्य टेक्स्ट फ़ील्ड पर स्विच करते हैं, और तुरंत इसे इस्तीफा दे देते हैं:

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

61
यह एक ही समय में भयानक और प्रतिभाशाली दोनों है। अच्छा लगा।
एलेक्स वेन

4
मुझे लगता है कि थोड़ा खतरनाक है - Apple एक दिन तय कर सकता है कि एक छिपे हुए नियंत्रण को बनाने वाला पहला उत्तरदाता व्यवहार नहीं है और "iOS" को किसी भी अधिक नहीं करने के लिए "ठीक करें" और फिर आपकी चाल काम करना बंद कर सकती है।
थॉमस टेम्पेलमैन

2
या आप पहले Responder को एक UITextField को असाइन कर सकते हैं जो वर्तमान में दिखाई दे रहा है और फिर उस विशेष टेक्स्टफिल्ड पर resignFirstResponder को कॉल करें। यह एक छिपे हुए दृश्य को बनाने की आवश्यकता को हटा देता है। सभी समान, +1।
स्विफ्टी मैकस्वीफ्टरटन

3
मुझे लगता है कि यह केवल भयानक है ... इसे छिपाने से पहले यह कीबोर्ड लेआउट (या इनपुट दृश्य) को बदल सकता है
डैनियल

19

नेविंस के उत्तर के स्विफ्ट 3 और 4 संस्करण के लिए :

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

10

यहां एक समाधान दिया गया है जो सही प्रथम उत्तरदाता को रिपोर्ट करता है (कई अन्य समाधान UIViewControllerपहले उत्तरदाता के रूप में रिपोर्ट नहीं करेंगे, उदाहरण के लिए), दृश्य पदानुक्रम पर लूपिंग की आवश्यकता नहीं है, और निजी एपीआई का उपयोग नहीं करता है।

यह Apple की विधि sendAction: से: से: forEvent: का लाभ उठाता है , जो पहले से ही पहले उत्तरदाता तक पहुंचने का तरीका जानता है।

हमें इसे केवल 2 तरीकों से बदलना होगा:

  • बढ़ाएँ UIResponderताकि यह पहले प्रत्युत्तर पर हमारे अपने कोड को निष्पादित कर सके।
  • UIEventपहला प्रत्युत्तर वापस करने के लिए उपवर्ग ।

यहाँ कोड है:

@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

पहला उत्तरदाता वर्ग UIResponder का कोई उदाहरण हो सकता है, इसलिए अन्य वर्ग हैं जो UIViews के बावजूद पहला उत्तरदाता हो सकते हैं। उदाहरण के लिए UIViewControllerपहला उत्तरदाता भी हो सकता है।

में इस सार आप अनुप्रयोग की खिड़कियों के rootViewController से शुरू नियंत्रकों के पदानुक्रम के माध्यम से पाशन द्वारा पहले प्रत्युत्तर प्राप्त करने के लिए एक पुनरावर्ती रास्ता मिल जाएगा।

आप कर सकते हैं फिर पहली प्रतिक्रिया कर रहा है

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

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

हालाँकि, यदि पहला प्रत्युत्तर UIView या UIViewController का उपवर्ग नहीं है, तो यह दृष्टिकोण विफल हो जाएगा।

इस समस्या को ठीक करने के लिए हम एक श्रेणी बनाकर एक अलग दृष्टिकोण कर सकते हैं UIResponderऔर इस वर्ग के सभी जीवित उदाहरणों की एक सरणी बनाने में सक्षम होने के लिए कुछ जादू स्वाज़लिंग प्रदर्शन करते हैं। फिर, पहला उत्तर प्राप्त करने के लिए हम सरल पुनरावृति कर सकते हैं और प्रत्येक वस्तु पूछ सकते हैं यदि -isFirstResponder

यह दृष्टिकोण इस अन्य जिस्ट में कार्यान्वित किया जा सकता है ।

आशा है ये मदद करेगा।


7

स्विफ्ट का उपयोग और एक विशिष्ट UIViewवस्तु के साथ यह मदद कर सकता है:

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
}

बस इसे अपने में रखें UIViewControllerऔर इसे इस तरह उपयोग करें:

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

ध्यान दें कि परिणाम एक वैकल्पिक मूल्य है, इसलिए यह शून्य होगा यदि दिए गए विचारों के साक्षात्कारों में कोई फर्स्टप्रोसेसर नहीं मिला।


5

उन विचारों पर ध्यान दें जो पहले उत्तरदाता हो सकते हैं और - (BOOL)isFirstResponderयह निर्धारित करने के लिए उपयोग कर सकते हैं कि क्या वे वर्तमान में हैं।


4

पीटर स्टीनबर्गर ने सिर्फ निजी अधिसूचना के बारे में ट्वीट किया थाUIWindowFirstResponderDidChangeNotification , जिसे आप देख सकते हैं कि क्या आप पहले रिपरडर बदलाव को देखना चाहते हैं।


वह अस्वीकार कर दिया जाता है क्योंकि एक निजी एपीआई का उपयोग करना, शायद एक निजी अधिसूचना का उपयोग करना भी एक बुरा विचार हो सकता है ...
लास

4

यदि आपको बस कीबोर्ड को मारने की आवश्यकता है जब उपयोगकर्ता एक पृष्ठभूमि क्षेत्र पर टैप करता है तो एक जेस्चर पहचानकर्ता क्यों नहीं जोड़ा जाता है और [[self view] endEditing:YES]संदेश भेजने के लिए इसका उपयोग करता है ?

आप xib या स्टोरीबोर्ड फ़ाइल में टैप जेस्चर पहचानकर्ता को जोड़ सकते हैं और इसे एक क्रिया से जोड़ सकते हैं,

कुछ इस तरह दिखता है तो समाप्त हो गया

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

यह मेरे लिए बहुत अच्छा काम किया। मैंने Xib पर एक दृश्य के लिए एक कनेक्शन को चुना, और मैं इसके लिए अतिरिक्त विचार जोड़ सकता हूं रेफरेंसिंग आउटलेट कलेक्शंस में आमंत्रित करने के लिए
जेम्स पेरिह

4

बस यहां मामला है जबरदस्त जैकब एगर के दृष्टिकोण का स्विफ्ट संस्करण:

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

यह वही है जो मैंने पाया था कि 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

यह वही है जो मेरे 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

आप UIViewइसे पाने के लिए निम्नलिखित एक्सटेंशन चुन सकते हैं (डैनियल द्वारा क्रेडिट) :

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

1

आप इस तरह भी कोशिश कर सकते हैं:

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

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

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

मैंने कोशिश नहीं की, लेकिन यह एक अच्छा समाधान है


1

रोमियो 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]);
                        }
                    }
                }
            }
        }
    }
}

धन्यवाद! आप सही हैं, यहां प्रस्तुत अधिकांश उत्तर यूट्यूव्यू के साथ काम करते समय हल नहीं हैं। आप अपने कोड को बहुत सरल कर सकते हैं, यहां तक ​​कि अगर if ([textField isKindOfClass:[UITextField class]]), यूटैक्सव्यू के लिए उपयोग की अनुमति देता है, और पहले ट्रेडर को वापस कर सकता है।
Frade

1

यह पुनरावृत्ति के लिए अच्छा उम्मीदवार है! 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;
}

1

आप इस तरह से निजी एपि को कॉल कर सकते हैं, सेब को अनदेखा करें:

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

1
लेकिन ... कृपया जाँचने के लिए 'respondsToSelector' का प्रयोग करें
ibcker

मैं अब 'respondsToSelector' की जांच करता हूं, लेकिन मुझे अभी भी एक चेतावनी मिली है, यह असुरक्षित हो सकता है। क्या मैं किसी तरह चेतावनी से छुटकारा पा सकता हूं?
एक्ट

1

@ थोमस-मुलर की प्रतिक्रिया का स्विफ्ट संस्करण

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

मेरा यहाँ से थोड़ा अलग दृष्टिकोण है। अपने द्वारा 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

मैं यूआईवाईईवाई के किसी भी स्थान पर पहली प्रतिक्रिया खोजने के लिए आपके कार्यान्वयन के साथ साझा करना चाहूंगा। मुझे उम्मीद है कि यह मेरी अंग्रेजी के लिए मदद और खेद है। धन्यवाद

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

काम के नीचे कोड।

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

अपडेट: मैं गलत था। आप वास्तव UIApplication.shared.sendAction(_:to:from:for:)में इस लिंक में प्रदर्शित पहले प्रत्युत्तर को कॉल करने के लिए उपयोग कर सकते हैं : http://stackoverflow.com/a/14135456/7464690


यदि उत्तर पदानुक्रम में नहीं है, तो यहां अधिकांश उत्तर वास्तव में वर्तमान पहला उत्तर नहीं मिल सकता है। उदाहरण के लिए, 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

पहला उत्तर खोजने का सबसे सरल तरीका:

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

डिफ़ॉल्ट कार्यान्वयन दिए गए लक्ष्य ऑब्जेक्ट के लिए एक्शन विधि को भेजता है या, यदि कोई लक्ष्य निर्दिष्ट नहीं है, तो पहले उत्तरदाता को।

अगला कदम:

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

उपयोग: बस UIResponder.actualFirstअपने उद्देश्यों के लिए मिलता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.