views:

4174

answers:

6

I submitted my app a little over a week ago and got the dreaded rejection email today. It reads as follows:

Dear -----------,

Thank you for submitting --------- to the App Store. Unfortunately it cannot be added to the App Store because it is using a private API. Use of non-public APIs, which as outlined in the iPhone Developer Program License Agreement section 3.3.1 is prohibited:

"3.3.1 Applications may only use Documented APIs in the manner prescribed by Apple and must not use or call any private APIs."

The non-public API that is included in your application is firstResponder.

Regards,

iPhone Developer Program

Now, the offending API call is actually a solution I found here on SO:

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

So this is my question; How do I get the current first responder on the screen? I'm looking for a legal way that won't get my app rejected.

Thanks.

I figured this out based on the solution provided by Thomas below. Here is what the final code looks like:

@implementation UIView (FindFirstResponder)
- (UIView *)findFirstResonder
{
    if (self.isFirstResponder) {        
        return self;     
    }

    for (UIView *subView in self.subviews) {
     UIView *firstResponder = [subView findFirstResonder];

        if (firstResponder != nil) {
      return firstResponder;
     }
    }

    return nil;
}
@end
+1  A: 

Iterate over the views that could be the first responder and use - (BOOL)isFirstResponder to determine if they currently are.

Johan Kool
+26  A: 

In my application, I often want the first responder to resign if the user taps on the background. For this purpose I wrote this category on UIView, which I call on the UIWindow.

@implementation UIView (FindAndResignFirstResponder)
- (BOOL)findAndResignFirstResponder
{
    if (self.isFirstResponder) {
        [self resignFirstResponder];
        return YES;     
    }
    for (UIView *subView in self.subviews) {
        if ([subView findAndResignFirstResponder])
            return YES;
    }
    return NO;
}
@end

That's different from what you're trying to do, but should give you an idea how to approach this.

Thomas Müller
Excellent solution. I modified your category for my needs as follows:@implementation UIView (FindFirstResponder)- (UIView *)findFirstResonder{ if (self.isFirstResponder) { return self; } for (UIView *subView in self.subviews) { UIView *firstResponder = [subView findFirstResonder]; if (firstResponder != nil) { return firstResponder; } } return nil;}@end
Peter Parker
oops, formatting got screwed up.
Peter Parker
Great answer, just a small spelling mistake: "findAndResignFirstResonder" should be "findAndResignFirstRespon[d]er"
Alan Rogers
Thanks, Alan, I fixed it.
Thomas Müller
Why is this a better solution than simply calling `[self.view endEditing:YES]`?
Tim Sullivan
@Tim I didn't you could do that. It's obviously a much simpler way to resign the first responder. It doesn't help with the original questions, though, which was to identify the first responder.
Thomas Müller
@Thomas: Cool... I was mostly curious if I was missing something obvious.
Tim Sullivan
A: 

This is what I did to find what UITextField is the firstResponder when the user clicks Save/Cancel in a 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];
                }

            }
        }

    }

}
Romeo
+14  A: 

[self.view endEditing:YES] works for me

cdyson37
Great find. Very useful.
Luther Baker
This is the solution.
Eonil
Awesome find! This is good.
Floetic
+4  A: 

It's not pretty, but the way I resign the firstResponder when I don't know what that the responder is:

Create an UITextField, either in IB or programmatically. Make it Hidden. Link it up to your code if you made it in IB.

Then, when you want to dismiss the keyboard, you switch the responder to the invisible text field, and immediately resign it:

    [self.invisibleField becomeFirstResponder];
    [self.invisibleField resignFirstResponder];
cannyboy
This is both horrible and genius at the same time. Nice.
Squeegy
A: 

cdyson37 = genuis!

Post this as a comment to the relevant answer please.
Squeegy