개인 API를 사용하지 않고 현재 첫 번째 응답자를 가져옵니다.
일주일 전에 앱을 제출했는데 오늘 무시무시한 거절 메일이 왔어요.공개되지 않은 API를 사용하고 있기 때문에 앱이 받아들여질 수 없다는 것을 알 수 있습니다.구체적으로는 다음과 같습니다.
어플리케이션에 포함되어 있는 비공개 API는 firstResponder입니다.
문제가 되는 API 호출은 실제로 여기 SO에서 찾은 솔루션입니다.
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
UIView *firstResponder = [keyWindow performSelector:@selector(firstResponder)];
현재 응급 구조자를 화면에 표시하려면 어떻게 해야 합니까?앱이 거부당하지 않는 방법을 찾고 있습니다.
가 첫 응답자를것이 일 것입니다.[self.view endEditing:YES]
내 어플리케이션 중 하나에서는 사용자가 백그라운드를 탭할 경우 첫 번째 응답자가 기권하기를 원하는 경우가 많습니다.이를 위해 UIWindow에서 호출하는 UIView에 카테고리를 작성했습니다.
다음은 이를 기반으로 하며 첫 번째 응답자를 반환해야 합니다.
@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`
}
첫 번째 응답자를 조작하는 일반적인 방법은 제로 타깃액션을 사용하는 것입니다.이것은 임의의 메시지를 응답측 체인에 송신해(첫 번째 응답측부터), 메시지에 응답할 때까지(실렉터와 일치하는 메서드가 실장되어 있다) 체인을 계속하는 방법입니다.
키보드를 꺼내는 경우, 이것은 어떤 창이나 뷰가 최초 응답자인지에 관계없이 작동하는 가장 효과적인 방법입니다.
[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];
더 예요.[self.view.window endEditing:YES]
.
(BigZaphod가 컨셉을 상기시켜줘서 고마워요)
여기 카테고리가 있습니다.이 카테고리로 전화를 하면 최초 응답자를 빠르게 찾을 수 있습니다.[UIResponder currentFirstResponder]
프로젝트에 다음 2개의 파일을 추가합니다.
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
여기서 중요한 것은 액션을 0으로 전송하면 첫 번째 응답자에게 전송된다는 것입니다.
(당초 이 답변은 이쪽에서 공개했습니다.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
}
}
예쁘지는 않지만 응답자가 무엇인지 모를 때 첫 번째 응답자를 사임시키는 방법:
IB 또는 프로그래밍 방식으로 UITextField를 만듭니다.숨김으로 하다.IB에서 만든 코드와 연결하세요.
그런 다음 키보드를 해제하려면 응답측을 보이지 않는 텍스트 필드로 전환하고 즉시 재기동합니다.
[self.invisibleField becomeFirstResponder];
[self.invisibleField resignFirstResponder];
Swift 3 & 4 버전의 네빈산염 답변:
UIApplication.shared.sendAction(#selector(UIView.resignFirstResponder), to: nil, from: nil, for: nil)
올바른 첫 대응자를 많은 UIViewController
예를 들어 첫 번째 응답자로)는 뷰 계층에 루프를 걸 필요가 없으며 개인 API를 사용하지 않습니다.
이미 첫 번째 응답자에 액세스하는 방법을 알고 있는 Apple의 sendAction:to:from:forEvent: 메서드를 활용합니다.
두 가지 방법으로 조정하면 됩니다.
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
Swift 및 특정 개체와 함께 사용하면 다음과 같은 이점이 있습니다.
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
}
당신의 그 your your 에 넣어주세요.UIViewController
뭇매를 맞다
let firstResponder = self.findFirstResponder(inView: self.view)
결과는 Optional 값이므로 지정된 뷰 서브뷰 계층에서 firstResponder를 찾을 수 없는 경우 0이 됩니다.
첫 번째 응답측에는 클래스 UIResponder의 임의의 인스턴스를 지정할 수 있습니다.따라서 UIView에도 불구하고 첫 번째 응답측일 수 있는 클래스가 있습니다.를 들어, 「」입니다.UIViewController
첫 번째 응답자일 수도 있습니다.
이 GIST에서는 어플리케이션 창의 rootViewController에서 시작하는 컨트롤러 계층을 루프함으로써 첫 번째 응답자를 얻는 재귀적인 방법을 찾을 수 있습니다.
다음을 수행하여 첫 번째 응답자를 검색할 수 있습니다.
- (void)foo
{
// Get the first responder
id firstResponder = [UIResponder firstResponder];
// Do whatever you want
[firstResponder resignFirstResponder];
}
단, 첫 번째 응답측이 UIView 또는 UIViewController의 서브클래스가 아닌 경우 이 접근법은 실패합니다.
하려면 , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,UIResponder
이 클래스의 모든 살아있는 인스턴스를 배열할 수 있도록 마법 스위즐링을 수행합니다.첫 에게 '아니다'라고 .-isFirstResponder
.
이 접근방식은 다른 요점에서 구현되어 있습니다.
도움이 됐으면 좋겠다.
될 수 하여 " " 를 사용합니다.- (BOOL)isFirstResponder
현재 있는지 확인할 수 있습니다.
의 컬렉션을 이 아니라 이 뷰가 있는 의 컬렉션을 찾는 입니다.isFirstResponder
도 설정하기 위해 , 설정하다.nil
하지만 메시지 수신자는 보관하고 있기 때문에 돌려보내고 싶은 것은 무엇이든 할 수 있습니다.
또, 검출된 응답측을 보관 유지하는 옵션도 제로 아웃 합니다.defer
콜 자체에서 스테이트먼트를 클릭합니다.이것에 의해, 콜의 마지막에 레퍼런스가 남아 있지 않게 됩니다.심지어 약한 레퍼런스도 남아 있지 않습니다.
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 and place it in the _foundFirstResponder variable.
// As such, the variable will contain the current first responder (if any) immediately after this line executes
UIApplication.shared.sendAction(#selector(UIResponder.storeFirstResponder(_:)), to: nil, from: nil, for: nil)
// The following 'defer' statement runs *after* this getter returns,
// thus releasing any strong reference held by the variable immediately thereafter
defer {
_foundFirstResponder = nil
}
// Return the found first-responder (if any) back to the caller
return _foundFirstResponder
}
// Make sure to mark this with '@objc' since it has to be reachable as a selector for `sendAction`
@objc func storeFirstResponder(_ sender: AnyObject) {
// Capture the recipient of this message (self), which is the first responder
_foundFirstResponder = self
}
}
이상, 이렇게 하는 것만으로 긴급 대응자를 사임시킬 수 있습니다.
UIResponder.first?.resignFirstResponder()
그러나 내 API는 실제로 최초 응답자가 무엇이든 반환하기 때문에 나는 그것으로 내가 원하는 것을 할 수 있다.
다음으로 현재 최초 응답자가 다음 중 어느 쪽인지 확인하는 예를 나타냅니다.UITextField
와 함께helpMessage
속성 집합, 속성 집합의 경우 컨트롤 바로 옆에 도움말 버블에 표시됩니다.화면의 '퀵 도움말' 버튼으로 호출합니다.
func showQuickHelp(){
if let textField = UIResponder?.first as? UITextField,
let helpMessage = textField.helpMessage {
textField.showHelpBubble(with:helpMessage)
}
}
상기의 서포트는, 다음의 확장으로 정의되어 있습니다.UITextField
이렇게...
extension UITextField {
var helpMessage:String? { ... }
func showHelpBubble(with message:String) { ... }
}
이제 이 기능을 지원하려면 도움말 메시지가 있는 텍스트 필드를 결정하고 나머지는 UI가 대신 처리합니다.
피터 스타인버거가 방금 트위터에 사적인 통지에 대해UIWindowFirstResponderDidChangeNotification
이는 firstResponder의 변경을 감시하는 경우에 관찰할 수 있습니다.
사용자가 백그라운드 영역을 탭할 때 키보드를 종료해야 하는 경우 제스처 인식기를 추가하고 이를 사용하여[[self view] endEditing:YES]
★★★★★★★★★★★★★★★★★?
탭 제스처 인식기를 xib 또는 스토리보드 파일에 추가하여 액션에 연결할 수 있습니다.
이렇게 생겼다가 끝나다
- (IBAction)displayGestureForTapRecognizer:(UITapGestureRecognizer *)recognizer{
[[self view] endEditing:YES];
}
Swift 버전의 멋진 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
}
}
사용자가 ModalViewController에서 [Save/Cancel]를 클릭했을 때 UITextField가 firstResponder인지 확인하기 위해 다음과 같이 했습니다.
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];
}
}
}
}
}
이것은, 「UIView Controller Category(UIView 컨트롤러 카테고리)」에 있는 것입니다.응급 구조자를 찾는 것 등 여러 가지 용도로 유용합니다.블록은 훌륭해!
- (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;
}];
}
가 ★★★★★★★★★★★★★★★UIResponder
할 수 UIApplication
첫 번째 응답자가 누구인지 알려드립니다.
다음을 참조하십시오.
iOS 뷰에 어떤 자녀가 최초 대응자 상태인지 문의할 수 있는 방법이 있습니까?
할 수 .UIView
(Daniel의 크레딧):
extension UIView {
var firstResponder: UIView? {
guard !isFirstResponder else { return self }
return subviews.first(where: {$0.firstResponder != nil })
}
}
다음과 같이 시도할 수도 있습니다.
- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event {
for (id textField in self.view.subviews) {
if ([textField isKindOfClass:[UITextField class]] && [textField isFirstResponder]) {
[textField resignFirstResponder];
}
}
}
해보지는 않았지만 좋은 해결책인 것 같다
이건 재귀의 좋은 후보야!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;
}
다음과 같이 privite api를 호출할 수 있습니다.apple ignore:
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow]; SEL sel = NSSelectorFromString(@"firstResponder"); UIView *firstResponder = [keyWindow performSelector:sel];
@thomas-müler 응답의 신속한 버전
extension UIView {
func firstResponder() -> UIView? {
if self.isFirstResponder() {
return self
}
for subview in self.subviews {
if let firstResponder = subview.firstResponder() {
return firstResponder
}
}
return nil
}
}
UIView 어디에서든 응급구조자를 찾을 수 있는 구현 방법을 알려드리겠습니다.내 영어에 도움이 되고 미안했으면 좋겠어.감사해요.
+ (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;
}
로미오 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]);
}
}
}
}
}
}
}
업데이트: 제가 틀렸습니다. 쓸 수 요.UIApplication.shared.sendAction(_:to:from:for:)
http://stackoverflow.com/a/14135456/746890 에 표시되어 있는 최초의 응답측을 호출합니다.
대부분의 답변은 현재 최초 응답자가 보기 계층에 없는 경우 실제로 찾을 수 없습니다.를 들어, 「」라고 하는 것은,AppDelegate
★★★★★★★★★★★★★★★★★」UIViewController
서브클래스
응답자가 경우에도 수 .UIView
.
요?next
의 of의 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
작업 아래의 코드.
- (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;
}
첫 번째 응답자를 찾는 가장 간단한 방법:
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
}
}
: Get ★★★★★★★★★★★★★★★★★★★★★★★★UIResponder.actualFirst
츠키노
언급URL : https://stackoverflow.com/questions/1823317/get-the-current-first-responder-without-using-a-private-api
'programing' 카테고리의 다른 글
단일 인스턴스 WPF 응용 프로그램을 만드는 올바른 방법은 무엇입니까? (0) | 2023.04.16 |
---|---|
Python에서 범위를 벗어난 인덱스 기본값 가져오기 (0) | 2023.04.16 |
Git 커밋의 출처를 특정하다 (0) | 2023.04.16 |
Windows용 Objective-C (0) | 2023.04.11 |
목록에서 고유한 값 목록을 가져옵니다. (0) | 2023.04.11 |