I've added a UIView
containing a UITapGestureRecognizer
as my key window's subview. It shows properly, however when I tap my view, the target method is not fired. I've even tried to replace the gesture recognizer with a UIButton
, still to no avail.
Here is my code.
NotificationView.h
#import <UIKit/UIKit.h>
typedef NS_ENUM(int, NotificationKind) {
NotificationKindActivity,
NotificationKindReply,
};
@interface NotificationView : UIView {
NotificationKind currentNotificationKind;
}
-(id)initWithMessage:(NSString*)message andColor:(UIColor*)color andKind:(NotificationKind)kind;
-(void)show;
@end
NotificationView.m
#import "NotificationView.h"
@implementation NotificationView
- (id)initWithMessage:(NSString*)message andColor:(UIColor*)color andKind:(NotificationKind)kind
{
self = [super initWithFrame:CGRectMake(0, 0, CGRectGetWidth([UIScreen mainScreen].bounds), 60)];
if (self) {
// Initialization code
[self setAlpha:0];
[self setBackgroundColor:color];
[self setUserInteractionEnabled:YES];
currentNotificationKind = kind;
UILabel *label = [[UILabel alloc] initWithFrame:self.bounds];
[label setNumberOfLines:0];
[label setFont:[UIFont fontWithName:@"Roboto-Italic" size:20]];
[label setTextColor:[UIColor whiteColor]];
[label setTextAlignment:NSTextAlignmentCenter];
[label setPreferredMaxLayoutWidth:290];
[label setText:message];
[label setUserInteractionEnabled:YES];
[self addSubview:label];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(notificationTapped)];
[tap setNumberOfTapsRequired:1];
[self addGestureRecognizer:tap];
[[[UIApplication sharedApplication] keyWindow] addSubview:self];
}
return self;
}
-(void)show{
[UIView animateWithDuration:0.3 animations:^{
[self setAlpha:1];
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3 delay:3 options:UIViewAnimationOptionCurveLinear animations:^{
[self setAlpha:0];
} completion:^(BOOL finished) {
[self removeFromSuperview];
}];
}];
}
-(void)notificationTapped{
DDLogDebug(@"Notification tapped!");
}
@end
When this happens to me it's usually because I screwed up my UIView
frame. I see all the content as expected because my UIView
isn't clipping to bounds, but I can't interact with anything because my taps are outside the bounds of the UIView
.
My simple test is to change the background color of the UIView
and see if it covers the area I expect or if I screwed up size/placement somehow.
I used to pound my head against the wall with this issue, struggling for hours, but I've done it so many times now it's 5min fix of "Oh that again".
Edit:
Then I'd look at your show
code. Your calling code isn't here, but if I'm to assume your are just using your show
code and your view is only on screen for 3 seconds, then that's you problem.
As Justin mentioned (comment above) and Apple's docs
During an animation, user interactions are temporarily disabled for
all views involved in the animation, regardless of the value in this
property. You can disable this behavior by specifying the
UIViewAnimationOptionAllowUserInteraction option when configuring the
animation.
Since the entire time your view is on the screen it's part of an animation block, all interaction will be disable for the entire time it's visible. I've never quite tested the delay
bit and whether animation was disabled during that piece, but it would not surprise me animation is disabled during the delay. The second animation is still inside the primary animation block, so I'd assume animations will be blocked until both are complete.
Updated NotificationView.m
file, as suggested by @DBD...
#import "NotificationView.h"
@implementation NotificationView
- (id)initWithMessage:(NSString*)message andColor:(UIColor*)color andKind:(NotificationKind)kind
{
self = [super initWithFrame:CGRectMake(0, 0, CGRectGetWidth([UIScreen mainScreen].bounds), 60)];
if (self) {
// Initialization code
[self setAlpha:0];
[self setBackgroundColor:color];
[self setClipsToBounds:YES];
currentNotificationKind = kind;
UILabel *label = [[UILabel alloc] initWithFrame:self.bounds];
[label setNumberOfLines:0];
[label setFont:[UIFont fontWithName:@"Roboto-Italic" size:20]];
[label setTextColor:[UIColor whiteColor]];
[label setTextAlignment:NSTextAlignmentCenter];
[label setPreferredMaxLayoutWidth:290];
[label setText:message];
[self addSubview:label];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(notificationTapped)];
[tap setNumberOfTapsRequired:1];
[self addGestureRecognizer:tap];
[[[UIApplication sharedApplication] keyWindow] addSubview:self];
}
return self;
}
-(void)show{
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
[self setAlpha:1];
} completion:nil];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
[self setAlpha:0];
} completion:^(BOOL finished) {
[self removeFromSuperview];
}];
});
}
-(void)notificationTapped{
DDLogDebug(@"Notification tapped!");
}
@end
I ended up being able to create a full-screen subview which accepted gestures by adding it to the app's keyWindow, assuming makeKeyAndVisible has been called first:
myView.hidden = YES;
[[[UIApplication sharedApplication] keyWindow] addSubview:myView];
Then to show it above the status bar:
[[UIApplication sharedApplication] keyWindow].windowLevel = UIWindowLevelStatusBar + 1;
myView.hidden = NO;
And to change it back:
[[UIApplication sharedApplication] keyWindow].windowLevel = UIWindowLevelNormal;
myView.hidden = YES;
This just changes the position of your app's main window (including all of its subviews) within the window hierarchy. Hope that helps!