I have simple view controller and I add this controller like subview for window using this code:
UIWindow *window = [UIApplication sharedApplication].keyWindow;
if (!window)
window = [[UIApplication sharedApplication].windows objectAtIndex:0];
self.view.alpha = 0.0;
self.view.userInteractionEnabled = YES;
[window addSubview:self.view];
[self.view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(closeTriggered:)]];
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
self.view.alpha = 1.0;
}completion:nil];
But when I click at this view - nothing happens. Even event touchesBegan: are not called.
UPD:
Code above is in -(void)show method. I want to show one controller above all controllers.
In FirstViewController I create instance of CustonAlertViewController like that:
CustomAlertViewController *alertVC = [[CustomAlertViewController alloc]init];
[alertVC show];
In CustomAlertViewController I have show method presented at the top and viewDidLoad method with:
self.view.backgrondColor = [UIColor greenColor];
Hidden views (and equivalently, those with alpha == 0.0) do not respond to touches. If you require a fully transparent view, leave alpha as > 0.0, and say...
self.view.backgroundColor = [UIColor clearColor];
Alternatively, assign a nonzero alpha.
EDIT yes, the CustomAlertViewController instance is being deallocated immediately after show is invoked. The view controller that does the allocating needs to have a strong property to keep the alert around,
#property(nonatomic,strong) CustomAlertViewController *alertVC;
and adding...
CustomAlertViewController *alertVC = [[CustomAlertViewController alloc]init];
self.alertVC = alertVC;
[alertVC show];
This doesn't try to address some potential problems beyond the scope of this question (like rotation, or cleanly restoring when the alert is done).
// try like this , u need to give number of touches
UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(closeTriggered:)];
gesture.numberOfTapsRequired = 1;
gesture.numberOfTouchesRequired = 1;
[self.view addGestureRecognizer:gesture];
You have been setting your UITapGestureRecognizer after you addSubview to your window. like:
You have been doing this
//...
[window addSubview:self.view];
[self.view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(closeTriggered:)]];
//...
Try this
Set UITapGestureRecognizer before you addSubview. And remember to add the UIGestureRecognizerDelegate to your ViewController
self.view.alpha = 0.2;
self.view.userInteractionEnabled = YES;
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(closeTriggered:)];
tapGesture.numberOfTapsRequired = 1;
[tapGesture setDelegate:self];
[self.view addGestureRecognizer:tapGesture];
// After you add your self.view to window
[window addSubview:self.view];
I hope this can help you.
Related
I created a UIView in a separate class, and I am trying to animate it in my ViewController. It is supposed to work like the notification screen on the iPhone that appears when you swipe down, and then you can swipe it back up.
I can get my custom view to swipe down, but when I try to swipe it back up, the swipe up gesture is not being initiated.
I am a novice, so any help is greatly appreciated!
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NotificationView *notificationView = [[NotificationView alloc]init];
[self.view addSubview:notificationView];
UISwipeGestureRecognizer *swipeDownGestureRecognizer = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(swipeDown:)];
swipeDownGestureRecognizer.direction = UISwipeGestureRecognizerDirectionDown;
UISwipeGestureRecognizer *swipeUpGestureRecognizer = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(swipeUp:)];
swipeUpGestureRecognizer.direction = UISwipeGestureRecognizerDirectionUp;
[self.view addGestureRecognizer:swipeDownGestureRecognizer];
[notificationView addGestureRecognizer:swipeUpGestureRecognizer];
}
-(void) swipeUp: (UISwipeGestureRecognizer *) recognizer{
UIView *notificationView = [[NotificationView alloc]init];
notificationView = recognizer.view;
[UIView animateWithDuration:2.0 animations:^{
notificationView.frame = CGRectMake(0, 0, 414, 723);
}];
}
-(void) swipeDown: (UISwipeGestureRecognizer *) recognizer{
UIView *notificationView = [[NotificationView alloc]init];
notificationView = recognizer.view;
[UIView animateWithDuration:2.0 animations:^{
notificationView.frame = CGRectMake(0, 723, 414, 723);
}];
}
notificationView in ViewDidLoad should be assigned to the viewControllers property, and you should not alloc init views in the gesture recognizer actions.
You should create a property with your notificationView and keep a reference to it, and not create a new one over and over.
#property (strong, nonatomic) NotificationView *notificationView;
And in your viewDidLoad:
_notificationView = [[NotificationView alloc] init];
// Important line to solve your problems on gestures not fired off on your notification view
_notificationView.userInteractionEnabled = YES;
[self.view addSubview:_notificationView];
and simply change:
-(void)swipeDown:(UISwipeGestureRecognizer *)recognizer {
[UIView animateWithDuration:2.0 animations:^{
_notificationView.frame = CGRectMake(0, 723, 414, 723);
}];
}
You should look into a tutorial on how properties work.
And you should look into This Thread to avoid animations getting called multiple times etc by handling gesture states.
By "strange", I am noticing the following behavior:
background color set by the ChildViewController does not appear
despite adding a basic tap gesture recognizer to the ChildViewController, there is no tap recognition
adding a UIButton to the ChildViewController results in the UIButton shown, but there is no response when tapping on the UIButton part
In my ParentViewController, I present the ChildViewController very generically, like so:
UIViewController *viewController;
viewController = (ChildViewController *)[self.storyboard instantiateViewControllerWithIdentifier:#"ChildViewController"];
[self addChildViewController:viewController];
viewController.view.frame = self.view.bounds;
viewController.view.translatesAutoresizingMaskIntoConstraints = NO;
viewController.view.center = self.view.center;
[self.view addSubview:viewController.view];
[self.view bringSubviewToFront:viewController.view];
[viewController didMoveToParentViewController:self];
And here are some very basic things I do in ChildViewController's viewDidLoad:
self.view.backgroundColor = [UIColor yellowColor];
UIButton *tapButton = [UIButton buttonWithType:UIButtonTypeSystem];
[tapButton addTarget:self action:#selector(tappedOnTap) forControlEvents:UIControlEventTouchUpInside];
//button view customization code omitted for brevity
tapButton.userInteractionEnabled = YES;
tapButton.enabled = YES;
[self.view addSubview:tapButton];
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tappedOnView)];
[self.view addGestureRecognizer:tapGesture];
[self.view setUserInteractionEnabled:YES];
[self.view addGestureRecognizer:tapGesture];
I ensured that everything is hooked up correctly - however, there is no acknowledgment in tappedOnView and tappedOnTap when I tap on it, in both simulator or device.
Am I missing something basic about presenting a ChildViewController?
Edit
For those curious, this very basic app is on Github.
The containment calls are fine. But if you use the view debugger (), you'll see the frame is not correct. This is because you've turned off translatesAutoresizingMaskIntoConstraints, but you don't have any constraints. You can either turn that back on (and set autoresizingMask accordingly):
viewController.view.translatesAutoresizingMaskIntoConstraints = true;
Or you can define constraints rather than setting the frame (and redundantly setting the center):
//viewController.view.frame = self.view.bounds;
viewController.view.translatesAutoresizingMaskIntoConstraints = false;
//viewController.view.center = self.view.center;
[self.view addSubview:viewController.view];
[NSLayoutConstraint activateConstraints:#[
[viewController.view.topAnchor constraintEqualToAnchor:self.view.topAnchor],
[viewController.view.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
[viewController.view.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[viewController.view.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor]
]];
Try this:
ChildViewController *viewController;
viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"ChildViewController"];
viewController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleheight;
[self addChildViewController:viewController];
[self.view addSubview:viewController.view];
[viewController didMoveToParentViewController:self];
Usually you don't need to do anything when adding a ViewController to another except those basic methods. since the views will be adjusted automatically and you don't need to set all those properties like size and center etc...
My scenario is that there will be many sub views in the main view and tapping on each sub view should generate different result.
My approach is for each sub view to implement its own tap recognizer, instead of for the main view to have one single tap recognizer and calculate the which sub view's area the user has tapped in. Is this a correct and viable approach?
I have tried this approach but it doesn't seem to work. The tap method never gets called. I read lots of articles on stackoverflow but they didn't seem to help.
For example, although the sub view is not an image view I still manually set its userInteractionEnabled property to YES, as some posts suggested. But that didn't help.
Below is the main code of the sub view:
- (void) handleOneTap:(UITapGestureRecognizer*)paramSender{
// *** Never gets called
NSUInteger touchCounter = 0; for (touchCounter = 0;
touchCounter < paramSender.numberOfTouchesRequired;
touchCounter++){
CGPoint touchPoint = [paramSender locationOfTouch:touchCounter
inView:paramSender.view];
NSLog(#"Touch #%lu: %#",
(unsigned long)touchCounter+1, NSStringFromCGPoint(touchPoint));
}
}
- (void)viewDidLoad {
[super viewDidLoad];
_container = [[UIView alloc] initWithFrame:CGRectMake(20, 50, 280, 300)];
[self.view addSubview:_container];
_container.backgroundColor = [UIColor redColor];
_container.opaque = YES;
// setup tap recognizer
self.tapGestureRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleOneTap:)];
self.tapGestureRecognizer.numberOfTouchesRequired = 1;
self.tapGestureRecognizer.numberOfTapsRequired = 1;
self.view.userInteractionEnabled = YES;
self.container.userInteractionEnabled = YES;
[self.view addGestureRecognizer:self.tapGestureRecognizer];
}
Any help will be appreciated.
If you want to add UIGestureRecognizer to your subview, you should do it like that:
_container = [[UIView alloc] initWithFrame:CGRectMake(20, 50, 280, 300)];
[self.view addSubview:_container];
_container.backgroundColor = [UIColor redColor];
_container.opaque = YES;
_container.userInteractionEnabled = YES;
self.tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleOneTap:)];
self.tapGestureRecognizer.numberOfTouchesRequired = 1;
self.tapGestureRecognizer.numberOfTapsRequired = 1;
self.view.userInteractionEnabled = YES;
[_container addGestureRecognizer:self.tapGestureRecognizer];
It looks like that problem is not in UITapGestureRecognizer, but in case how you add Subview from ViewController.
Right way to add subviewcontoller is:
SSubViewController *pvc = [SSubViewController controllerWithSubViewID:0];
[self.view addSubview:pvc.view];
[self addChildViewController:pvc];
Do it in your ViewController and it should solve your problem
In my app i have a viewcontroller with a calendar in it (CKCalendar). Only one month is shown at the same time and with scrolling one can switch months. On certain days in the calendar are events which a highlighted by a circle.
Months are switched with the code
- (void) nextMonth:(UISwipeGestureRecognizer *)swipe {
[self.calView moveCalendarToNextMonth];
[self drawItemsForCurrentMonth];
}
The method drawItemsForCurrentMonth calls the next piece of code for every items that needs highlighting.
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSArray* res = [self calculateDayCirclePosition:date];
CGFloat x = [[res objectAtIndex:0] floatValue] + (DEFAULT_CELL_WIDTH-DEFAULT_CIRCLE_WIDTH-CELL_BORDER_WIDTH)/2;
CGFloat y = [[res objectAtIndex:1] floatValue] + (DEFAULT_CELL_HEIGHT-DEFAULT_CIRCLE_WIDTH)/2;
AgendaCircleView* circleView = [[AgendaCircleView alloc] initWithFrame:CGRectMake(x,y,self.circleWidth,self.circleWidth)];
circleView.date = date;
AgendaViewController* avc;
if([appDelegate.viewController.frontViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController* navVC = (UINavigationController*)appDelegate.viewController.frontViewController;
avc = (AgendaViewController*)[navVC visibleViewController];
}
UITapGestureRecognizer* tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:avc action:#selector(didSelectDate:)];
tapGesture.enabled = YES;
tapGesture.delegate = avc;
tapGesture.numberOfTapsRequired = 1;
tapGesture.numberOfTouchesRequired = 1;
[circleView addGestureRecognizer:tapGesture];
circleView.userInteractionEnabled = YES;
circleView.alpha = 0.5;
circleView.layer.cornerRadius = (self.circleWidth/2);
if(fill) {
circleView.backgroundColor = [appDelegate.dataController.rijschoolItem getColor1];
}
else {
circleView.layer.borderWidth = 1.0f;
circleView.backgroundColor = [UIColor clearColor];
circleView.layer.borderColor = [UIColor whiteColor].CGColor;
}
[circleView setNeedsDisplay];
[self.calendarContainer addSubview:circleView];
[self.calendarContainer bringSubviewToFront:circleView];
[self.calendarContainer setUserInteractionEnabled:YES];
This code works fine when the viewcontroller is initialized. Clicking on the circle fires the didSelectDate method. However when i change months the tapgesturerecognizer does not work anymore. When months are changed i use the exact same code that is used when initializing the viewcontroller. The strange thing is that when i open an other detailed viewcontroller and throw it on the view stack and go back, it is working again.
I've tried to use
gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer but this does not solve the problem.
Furthermore i've tried to use setNeedsLayout but this is not working either.
Any ideas?
Ok, I found the problem. So for others who might encounter the same type of problem. You have to make sure that the view is not animating while adding the gesturerecognizers.
I had this block of code
[UIView animateWithDuration:0.4 animations:^{
calendar.alpha = 1.0;
}];
[self drawItemsForCurrentMonth];
changed into
[UIView animateWithDuration:0.4 animations:^{
calendar.alpha = 1.0;
} completion:^(BOOL finished) {
if(finished) {
[self drawItemsForCurrentMonth];
}
}];
Having the method drawItemsForCurrentMonth after the animation block does not necessarily guarantee that the block is has actually finished. Luckily there is a completion block which covers that aspect.
I'm using UITapGestureRecognizer
This is my code:
Home.m:
- (void)viewDidLoad {
[super viewDidLoad];
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(tapAnim:)];
[self.view addGestureRecognizer:tapGesture];
UIButton *buttontest = [[UIButton alloc] init];
buttontest.backgroundColor = [UIColor whiteColor];
buttontest.frame = CGRectMake(0, 80, 40, 40);
[buttontest addTarget:self action:#selector(test:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:buttontest];
[self.view bringSubviewToFront:buttontest];
}
- (void)test: (UIButton*)aButton {
// TakePhoto *mvc = [[TakePhoto alloc]initWithNibName:#"TakePhoto" bundle:Nil];
// [self.navigationController pushViewController:mvc animated:YES];
//
// [self.view removeFromSuperview];
if (self.companyController) {
self.companyController = nil;
}
self.companyController = [[TakePhoto alloc] initWithNibName:#"TakePhoto" bundle:nil];
UIView *viewSuper = [[IQAppDelegate shareInstance] currentVisibleController].view;
UIViewController *profile = self.companyController;
profile.view.frame = viewSuper.frame;
[viewSuper addSubview:profile.view];
profile.view.frame = CGRectMake(viewSuper.frame.origin.x, viewSuper.frame.size.height, profile.view.frame.size.width, profile.view.frame.size.height);
[UIView beginAnimations:nil context: nil];
[UIView setAnimationDuration:0.35];
profile.view.frame = CGRectMake(viewSuper.frame.origin.x, viewSuper.frame.origin.x, profile.view.frame.size.width, profile.view.frame.size.height);
[UIView commitAnimations];
}
}
- (void) tapAnim: (UITapGestureRecognizer*)gestureRecognizer {
// Show something
}
TakePhoto.m
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
picker.delegate = self;
picker.allowsEditing = NO;
[(UIViewController *)self.delegate presentModalViewController:picker animated:YES];
I add a view:Takephoto front of Home(i'm not use "push"), like this:
--->Home
--->Take photo (like a popUp show): it has 2 buttons "Choose photo from library" and "Close"
When i use function "Choose photo from gallery", i cant choose a photo and UITapGestureRecognizer is always show.
How to disable UITapGestureRecognizer when choose photo from gallery?
P/S:sorry about my english.
Gesture recognizers have the enabled property. Set this to NO to disable the gesture recognizer. To make this easier you should keep a reference to the tap gesture recognizer using an instance variable.
you can set tapGesture.enabled=NO before you choose photos.
I think you need to implement this below delegate method :-
- (BOOL)gestureRecognizer:
(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch
When you look on the documentation it will return YES (by default) to allow the gesture recognizer to examine the touch object, NO to prevent the gesture recognizer from seeing this touch object. For more details follow this https://developer.apple.com/library/ios/DOCUMENTATION/UIKit/Reference/UIGestureRecognizerDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intfm/UIGestureRecognizerDelegate/gestureRecognizer:shouldReceiveTouch: