[objective-c] 개인 API를 사용하지 않고 현재 첫 번째 응답자 확보

나는 일주일 전에 내 앱을 제출했고 오늘 두려운 거부 이메일을 받았습니다. 비공개 API를 사용하고 있기 때문에 내 앱을 수락 할 수 없다고 알려줍니다. 구체적으로 말하면

응용 프로그램에 포함 된 비공개 API는 firstResponder입니다.

이제 문제가되는 API 호출은 실제로 여기에서 찾은 솔루션입니다.

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

화면에서 현재 첫 번째 응답자를 얻으려면 어떻게해야합니까? 내 앱이 거부되지 않는 방법을 찾고 있습니다.



답변

내 응용 프로그램 중 하나에서 사용자가 배경을 탭하면 첫 번째 응답자가 사직하기를 원합니다. 이를 위해 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
    }
}

Swift의 사용 예 :

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


답변

최종 목표가 첫 번째 응답자를 사임하는 것이라면 다음과 같이 작동합니다. [self.view endEditing:YES]


답변

첫 번째 응답자를 조작하는 일반적인 방법은 목표가없는 작업을 사용하는 것입니다. 이것은 임의의 메시지를 응답자 체인에 보내고 (첫 번째 응답자부터 시작) 누군가 메시지에 응답 할 때까지 체인을 계속 진행하는 방법입니다 (선택자와 일치하는 방법을 구현했습니다).

키보드를 닫을 경우 가장 먼저 응답하는 창이나보기에 관계없이 가장 효과적인 방법입니다.

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

이것은 심지어보다 효과적이어야합니다 [self.view.window endEditing:YES].

( 개념을 상기시켜 준 BigZaphod 에게 감사한다 )


답변

다음을 호출하여 첫 ​​번째 응답자를 빠르게 찾을 수있는 카테고리가 있습니다 [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

여기서 비결은 작업을 nil로 보내면 첫 번째 응답자에게 보냅니다.

(나는 원래이 답변을 https://stackoverflow.com/a/14135456/322427 )에 게시했습니다.


답변

다음은 Jakob Egger의 가장 훌륭한 답변을 기반으로 Swift에서 구현 된 확장입니다.

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


답변

예쁘지는 않지만 응답자가 무엇인지 모르는 경우 firstResponder를 사임하는 방법은 다음과 같습니다.

IB 또는 프로그래밍 방식으로 UITextField를 만듭니다. 그것을 숨기십시오. IB에서 작성한 경우 코드와 연결하십시오.

그런 다음 키보드를 닫으려면 응답자를 보이지 않는 텍스트 필드로 전환하고 즉시 서명을 취소하십시오.

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


답변

nevyn의 답변 스위프트 3 & 4 버전 :

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