I want to do something when the background was tapped on iPhone.
How to enable the background tapping?
I put a sample background on my app using this code.
- void viewDidLoad{
[super viewDidLoad];
UIGraphicsBeginImageContext(self.view.frame.size);
[[UIImage imageNamed:#"backgroundimage.jpeg"] drawInRect:self.view.bounds];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
How to do background tapping?
For example I want to click the background and a message will pop up "Background was tapped!".
Do i need IBAction for that?
Need help.
What you could do is use an IBAction, which would be a lot simpler code.
In your header file of your view controller, do this:
- (IBAction)backgroundTap:(id)sender;
In your implementation file of your view controller, do this:
- (IBAction)backgroundTap:(id)sender
{
}
In the storyboard, assuming that you have linked up view controller GUI with your view controller class, click on the background of the view controller GUI and then show the Identity Inspector in the Utilities view, which should show up on the right. Then, under custom class, which should currently be blank, type in UIControl. Now go to the connections inspector and link at Touch Down to the background, selecting backgroundTap. Now, anything inside the backgroundTap method will be what will happen when you select the background.
UIButton is the simplest way.
- (void)backgroundButtonClicked:(id)sender
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:#"Background was tapped!" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
[alertView release];
}
- (void)viewDidLoad
{
[super viewDidLoad];
/*
* Your other code here
*/
UIButton *backgroundButton = [UIButton buttonWithType:UIButtonTypeCustom];
backgroundButton.backgroundColor = [UIColor clearColor];
backgroundButton.frame = self.view.bounds;
[backgroundButton addTarget:self action:#selector(backgroundButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:backgroundButton];
[self.view sendSubviewToBack:backgroundButton];
}
BTW, there is no need to draw a background image because [UIImage imageNamed:#"imagename"] returns an image. If you want to present it, try putting the code in your -viewDidLoad:
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"backgroundimage.jpeg"]];
imageView.frame = self.view.bounds;
[self.view insertSubview:imageView belowSubview:backgroundButton];
[imageView release];
Edit:
Thanks to #AlexMDC for reminding me of UITapGestureRecognizer. Here is the UITapGestureRecognizer version:
- (void)tapped:(UITapGestureRecognizer *)g
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:#"Background was tapped!" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
[alertView release];
}
- (void)viewDidLoad
{
[super viewDidLoad];
/*
* Your other code here
*/
UITapGestureRecognizer*tap = [[UITapGestureRecognizer alloc] init];
[tap addTarget:self action:#selector(tapped:)];
[self.view addGestureRecognizer:tap];
[tap release];
}
Both versions meet the requirements. Admittedly, UITapGestureRecognizer is more powerful and more flexible. However, I'd prefer UIButton to do the trick this time. It's more lightweight than gesture recognizer. I needn't care about the state of the gesture recognizer, whether touch events have been blocked by it or how to implement the UIGestureRecognizerDelegate.
It's more likely the case that we want to add some other UIView or subclasses of UIView on the controller's view. At this point, the UITapGestureRecognizer version need to exclude all the non-background areas in – gestureRecognizerShouldBegin: delegate method.
If detecting double tap is the new requirement, it is still not too late to refactor the UIButton to the UITapGestureRecognizer.
Related
i have working in image and video overlay in that i put one image view, i assigned the tapGestureRecognizer to that image view its not working,For video i put one MPMoviePlayerController and i set the tapGestureRecognizer to it this one working fine but the image view only not working.Below is my code
- (void)viewDidLoad {
[super viewDidLoad];
videoimg=[[UIImageView alloc]init];//OvelayImage
videoimg.image=[UIImage imageNamed:#"fb.png"];
videoimg.userInteractionEnabled=YES;
videoimg.multipleTouchEnabled=YES;
[self.imgView bringSubviewToFront:videoimg];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imagePan:)];
tap.numberOfTapsRequired=1;
tap.numberOfTouchesRequired=1;
[videoimg addGestureRecognizer:tap];
[self.imgView addSubview:videoimg];//add the overlay image to Main imageView
[self.videoPlayerView.view addSubview:videoimg];//MPMoviePlayerController-add the overlay image to VideoView
}
//Gesture
-(void)imagePan:(UITapGestureRecognizer*)ges{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Image" message:#"FB Image Clicked..."
delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
videoimg=[[UIImageView alloc]init];
videoimg.image=[UIImage imageNamed:#"fb.png"];
videoimg.userInteractionEnabled=YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imagePan:)];
[videoimg setGestureRecognizers:[NSArray arrayWithObject:tap]];
tap.numberOfTapsRequired=1;
[self.videoPlayerView.view addSubview:videoimg];
use it may help you....
i have working in image and video overlay in that i put one image view, i assigned the tapGestureRecognizer to that image view its not working,For video i put one MPMoviePlayerController and i set the tapGestureRecognizer to it this one working fine but the image view only not working.Below is my code
- (void)viewDidLoad {
[super viewDidLoad];
videoimg=[[UIImageView alloc]init];//OvelayImage
videoimg.image=[UIImage imageNamed:#"fb.png"];
videoimg.userInteractionEnabled=YES;
videoimg.multipleTouchEnabled=YES;
[self.imgView bringSubviewToFront:videoimg];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imagePan:)];
tap.numberOfTapsRequired=1;
tap.numberOfTouchesRequired=1;
[videoimg addGestureRecognizer:tap];
[self.imgView addSubview:videoimg];//add the overlay image to Main imageView
[self.videoPlayerView.view addSubview:videoimg];//MPMoviePlayerController-add the overlay image to VideoView
}
//Gesture
-(void)imagePan:(UITapGestureRecognizer*)ges{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Image" message:#"FB Image Clicked..."
delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
videoimg=[[UIImageView alloc]init];
videoimg.image=[UIImage imageNamed:#"fb.png"];
videoimg.userInteractionEnabled=YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imagePan:)];
[videoimg setGestureRecognizers:[NSArray arrayWithObject:tap]];
tap.numberOfTapsRequired=1;
[self.videoPlayerView.view addSubview:videoimg];
use it may help you....
I want to add a UISlider on a UIAlerView but I am not able to do that. I also google that but I found only two answers and both of them are not showing any any result here is code what I found and applied in my project:
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"title" message:#"msg" delegate:self cancelButtonTitle:#"cancel" otherButtonTitles:#"ok", nil];
alertView.title = #"title";
CGRect frame = CGRectMake(alertView.frame.origin.x+10,alertView.frame.origin.y+20,100, 100.0);
UISlider *slider = [[UISlider alloc] initWithFrame:frame];
[slider addTarget:self action:#selector(sliderAction:) forControlEvents:UIControlEventValueChanged];
[slider setBackgroundColor:[UIColor clearColor]];
slider.minimumValue = 0.0;
slider.maximumValue = 10.0;
slider.continuous = YES;
slider.value = 25.0;
[alertView addSubview:slider];
[alertView show];
I think the frame of slider is not proper thats why its not being displayed.
can anyone help me in that.....
Thank you in advance...
You are adding the Slider in Alert view not in parent view of Alert view.
So your frame value will add the slider some where wide in alert view, result will be hidden on alert view.
so replace this line
CGRect frame = CGRectMake(alertView.frame.origin.x+10,alertView.frame.origin.y+20,100, 100.0);
To
CGRect frame = CGRectMake(10,20,100, 100.0);
The UIAlertView is also a UIView. I indeed, you could add the subview in it. But Apple says
"The UIAlertView class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified."
So Better to have custom view which will act as alertview.Like this one
I want to dismiss UIAlertView anywhere outside it with one tap. I want to show a UIAlertView without any button.
I have standard UIAlertView codes here, but I need input how to dismiss it, if it is possible.
With UITouch? With UITapGestureRecognizer?
Thank you.
EDIT:
in viewDidLoad
alertview initialization here with name "alert"
if (alert)
{
emptyview = [[UIView alloc]initWithFrame:CGRectMake(0,0,320,480)];
emptyview.backgroundColor = [UIColor clearColor];
[self.view addSubview:emptyview];
[emptyview addSubview:alert];
NSLog (#"emptyview is there!");
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
[emptyview addGestureRecognizer:singleTap];
[singleTap release];
}
But this emptyview doesnt not respond at all and it does not respond to handleSingleTap selector, which I rewrote a bit:
-(void)handleSingleTap:(UITapGestureRecognizer *)sender{
[alert dismissWithClickedButtonIndex:0 animated:YES];
[emptyview removeFromSuperview];
}
I need this emptyview being upon alert when alert is shown then I can dismiss alert with one tap.
I tried:
if (alert)
{
emptyview = [[UIView alloc]initWithFrame:CGRectMake(0,0,320,480)];
emptyview.backgroundColor = [UIColor clearColor];
[self.view addSubview:emptyview];
[emptyview addSubview:alert];
NSLog (#"emptyview is there!");
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
[alert addGestureRecognizer:singleTap];
[singleTap release];
}
Of course, alert did respond to handleSingleTap function. What did I wrong with emptyview?
SECOND EDIT:
What I want to achieve in this case is to show a small view with explanation after selecting a word, similar function in Kindle app, if you have one.
Maybe I should create a UIView instead of UIAlertView? But the small view in Kindle app is so nice with shadow below it, how is it possible?
It sounds like you are essentially trying to recreate a "Toast" on iOS. Good news, someone has already done that. See this project.
Edit: Don't want to use iToast. I like your style, less code it is. Here is what I come up with. It would seem obvious as others have said that the only way to overcome the modal nature of the UIAlertView is to add a superview to handle touch events. But you don't have to do that manually every time, consider subclassing UIAlertView. Try something like this:
Edit: #wagashi, Thanks for accepting my answer, and thanks for the heads up about setFrame: being a good place to adjust the size. Your code does make a very toast-like little alert, however when I tried it I found that if the message was to long the view seemed to fall apart. So I have modified setFrame: to simply reduce the size of the alert by about the size of one button, and to remain centered on the screen. So that the class accurately answers the question title "iOS How to dismiss UIAlertView with one tap anywhere?"
NoButtonAlertView.h
#import <UIKit/UIKit.h>
#interface _NoButtonAlertViewCover : UIView
#property (nonatomic,assign) UIAlertView *delegate;
#end
#interface NoButtonAlertView : UIAlertView
-(id)initWithTitle:(NSString *)title message:(NSString *)message;
#end
NoButtonAlertView.m
#import "NoButtonAlertView.h"
#implementation _NoButtonAlertViewCover
#synthesize delegate = _delegate;
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
[self removeFromSuperview];
[_delegate dismissWithClickedButtonIndex:0 animated:YES];
}
#end
#implementation NoButtonAlertView
-(void)show{
[super show];
_NoButtonAlertViewCover *cover = [[_NoButtonAlertViewCover alloc] initWithFrame:[UIScreen mainScreen].bounds];
cover.userInteractionEnabled = YES;
cover.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:.01];
cover.delegate = self;
[self.superview addSubview:cover];
}
-(id)initWithTitle:(NSString *)title message:(NSString *)message{
if ((self = [super initWithTitle:title message:message delegate:nil cancelButtonTitle:nil otherButtonTitles:nil, nil])){
}
return self;
}
- (void)setFrame:(CGRect)rect {
// Called multiple times, 4 of those times count, so to reduce height by 40
rect.size.height -= 10;
self.center = self.superview.center;
[super setFrame:rect];
}
#end
With this simple UIAlertView subclass and its UIView subclass for a cover, you can use it as simply as you would a standard UIAlertView. Like so:
NoButtonAlertView *alert = [[NoButtonAlertView alloc] initWithTitle:#"Hello" message:#"I am the very model of a modern major general; I'm information, vegitable, animal, and mineral."];
[alert show];
Will yield:
After showing UIAlertView add to your view controller (or even window) new empty UIView with full screen size. Attach to this wiew UITapGestureRecognizer
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
[view addGestureRecognizer:singleTap];
[singleTap release];
Now in handleSingleTap method you can dismiss UIAlertView and remove this view from window
-(void)handleSingleTap:(UITapGestureRecognizer *)sender{
[myAlert dismissWithClickedButtonIndex:0 animated:YES];
[view removeFromSuperView];
}
I just spent most of a day tracking down a very strange case where calling resignFirstResponder on the active UITextField did not hide the keyboard, even though the textfield was the first responder. This happens when I push a view controller on top of another view controller with an active text field. The keyboard goes away (as expected). But if I bring the keyboard back by touching a textfield in the 2nd view controller, subsequent calls to resignFirstResponder have no effect.
Here's simple code to reproduce the issue. This code is a view controller with a nav bar button to hide the keyboard, and another to push another copy of itself (with a confirmation UIAlertView). The first copy works without problem. However, if you push a 2nd copy (when the first copy has a visible keyboard) it is impossible to dismiss the keyboard. This only happens if there is a UIAlertView (the confirmation) on the screen when the 2nd copy is pushed. If you remove the #define ALERT line, everything works.
Does anyone know what is happening here? It looks like the UIALertView window is somehow interfering with the keyboard and keeping it's window from disappearing, which then confuses the next view. Is there any solution here other than pushing the 2nd view controller on a timer after the UIALertView is gone?
Sorry for the complex description. This is runnable code. I hope that the code is clear.
#implementation DemoViewController
- (id) init {
if (!(self = [super init]))
return nil;
return self;
}
- (void) dealloc {
[_inputTextfield release];
[super dealloc];
}
- (void) loadView {
UIView *view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
_inputTextfield = [[UITextField alloc] initWithFrame:CGRectMake(0., 0., 320., 44.)];
_inputTextfield.borderStyle = UITextBorderStyleRoundedRect;
_inputTextfield.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
_inputTextfield.keyboardAppearance = UIKeyboardAppearanceAlert;
_inputTextfield.autocapitalizationType = UITextAutocapitalizationTypeNone;
_inputTextfield.autocorrectionType = UITextAutocorrectionTypeNo;
_inputTextfield.keyboardType = UIKeyboardTypeDefault;
[view addSubview:_inputTextfield];
self.view = view;
[view release];
}
- (void) viewWillAppear:(BOOL) animated {
[super viewWillAppear:animated];
UIButton *downButton = [UIButton buttonWithType:UIButtonTypeCustom];
[downButton setTitle: #"keyboard down" forState:UIControlStateNormal];
[downButton addTarget:self action:#selector(downButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[downButton sizeToFit];
self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithCustomView:downButton] autorelease];
UIButton *nextButton = [UIButton buttonWithType:UIButtonTypeCustom];
[nextButton setTitle: #"next" forState:UIControlStateNormal];
[nextButton addTarget:self action:#selector(nextButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[nextButton sizeToFit];
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithCustomView:nextButton] autorelease];;
}
- (void) viewWillDisappear:(BOOL) animated {
[super viewWillDisappear:animated];
[_inputTextfield resignFirstResponder];
}
- (void) downButtonPressed:(id)sender {
[_inputTextfield resignFirstResponder];
}
#define ALERT
- (void) alertView:(UIAlertView *) alertView didDismissWithButtonIndex:(NSInteger) buttonIndex {
if (alertView.cancelButtonIndex == buttonIndex) {
return;
}
[self _nextButtonPressed];
}
- (void) _nextButtonPressed {
DemoViewController *nextViewController = [[DemoViewController alloc] init];
[self.navigationController pushViewController:nextViewController];
[nextViewController release];
}
- (void) nextButtonPressed:(id)sender {
#ifdef ALERT
UIAlertView *alert = [[UIAlertView alloc] init];
alert.message = #"Next view?";
alert.cancelButtonIndex = [alert addButtonWithTitle:#"No"];
[alert addButtonWithTitle:#"Yes"];
alert.delegate = self;
[alert show];
[alert release];
#else
[self _nextButtonPressed];
#endif
}
If you had bad luck resigning your first responders, here are a few solutions that might help:
Determine who has remained the first responder after your last call to resign first responder.
Try resigning all first responders by a single call to self.view (container view)
[self.view endEditing:YES];
ONLY if you've tried all the above methods and none worked, consider using this workaround.
-(BOOL)textViewShouldEndEditing:(UITextView *)textView {
NSArray *wins = [[UIApplication sharedApplication] windows];
if ([wins count] > 1) {
UIWindow *keyboardWindow = [wins objectAtIndex:1];
keyboardWindow.hidden = YES;
}
return YES;
}