Can't hide keyboard in UIViewController stack when UIAlertView is on screen - ios

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;
}

Related

UITextFieldShouldReturn does not work with my code

I have searched the entire internet and tried different ways to implement UITextFieldShouldReturn, but when I run it in the simulator it just doesn't work.
What I'm trying to do is move from the first textfield(emailTextField) to the second one( nameTextField ) when user clicks next button on the keyboard. The same for the second textfield's keyboard and dismiss the keyboard when user clicks done button on the last textfield( numberTextField ).
Here is my code, can anyone help me with this?
#import "ViewController.h"
#interface ViewController () <UITextFieldDelegate>
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.emailTextField.placeholder = #"Your Pitt Email(optional)";
self.nameTextField.placeholder = #"Lost ID Name";
self.numberTextField.placeholder = #"Lost ID Series Number(optional)";
[self.emailTextField.delegate self];
[self.nameTextField.delegate self];
[self.numberTextField.delegate self];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dismissKeyboard)];
[self.view addGestureRecognizer:tap];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//release delegate
-(void)dealloc{
self.emailTextField.delegate = nil;
self.nameTextField.delegate = nil;
self.numberTextField.delegate = nil;
}
//dismiss keyboard when it's called
-(void)dismissKeyboard{
[self.emailTextField resignFirstResponder];
[self.nameTextField resignFirstResponder];
[self.numberTextField resignFirstResponder];
}
//when a textfield begins editing, this will happen
-(void)textFieldDidBeginEditing:(UITextField *)textField{
if(textField == self.emailTextField){
textField.placeholder = #"";
}else if(textField == self.nameTextField){
textField.placeholder = #"";
}else{
textField.placeholder = #"";
}
}
//when a textfield ends editing, this will happen
-(void)textFieldDidEndEditing:(UITextField *)textField{
if(textField == self.emailTextField){
textField.placeholder = #"Your Pitt Email(optional)";
[self textFieldShouldReturn:self.emailTextField];
}else if(textField == self.nameTextField){
textField.placeholder = #"Lost ID Name";
[self textFieldShouldReturn:self.nameTextField];
}else{
textField.placeholder = #"Lost ID Series Number(Optional)";
[self textFieldShouldReturn:self.numberTextField];
}
}
//return button set up
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
return YES;
}
//when confirm is pressed
- (IBAction)confirmButton:(id)sender {
[self dismissKeyboard];
}
#end`
Change
[self.emailTextField.delegate self];
to
self.emailTextField.delegate = self;
Also remove those delegates from dealloc. You can put them from viewDidDisappear
Try this code snippet. Here i am taking only two
UITextFields.
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
if (textField==_txtEmail)
{
[_txtEmail resignFirstResponder];
[_txtPassword becomeFirstResponder];
}
else if (textField==_txtPassword)
{
[_txtPassword resignFirstResponder];
}
return YES;
}
This function is enough to return keyboard from text fields.
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
return YES;
}
Your these 3 lines are not working.
[self.emailTextField.delegate self];
[self.nameTextField.delegate self];
[self.numberTextField.delegate self];
If you are using storyboard, connect delegates of the text fields with the view.
You can use this code.It is working for me..
-(void)tool{
UIToolbar *toolbar = [[UIToolbar alloc] init];
[toolbar setBarStyle:UIBarStyleBlackTranslucent];
[toolbar sizeToFit];
UIBarButtonItem *flexButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
UIBarButtonItem *doneButton =[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(resignKeyboard)];
[doneButton setTintColor:[UIColor whiteColor]];
UIBarButtonItem *Next = [[UIBarButtonItem alloc]initWithTitle:#"Next" style:UIBarButtonItemStylePlain target:self action:#selector(Nextbutton)];
[Next setTintColor:[UIColor whiteColor]];
NSArray *itemsArray = [NSArray arrayWithObjects:Next,flexButton,doneButton,nil];
[toolbar setItems:itemsArray];
[FirstNameTextField setInputAccessoryView:toolbar];
[LastNameTextfield setInputAccessoryView:toolbar];
[EmailAddressTextField setInputAccessoryView:toolbar];
[PasswordTextfield setInputAccessoryView:toolbar];
[ConfirmPasswordTextfield setInputAccessoryView:toolbar];}
-(void)Nextbutton{
if ([FirstNameTextField isFirstResponder])
{
[LastNameTextfield becomeFirstResponder];
}
else if ([LastNameTextfield isFirstResponder])
{
[EmailAddressTextField becomeFirstResponder];
}
else if ([EmailAddressTextField isFirstResponder])
{
[PasswordTextfield becomeFirstResponder];
}
else if ([PasswordTextfield isFirstResponder])
{
[ConfirmPasswordTextfield becomeFirstResponder];
}
else if ([ConfirmPasswordTextfield isFirstResponder])
{
[ConfirmPasswordTextfield resignFirstResponder];
}}
-(void)resignKeyboard{
[FirstNameTextField resignFirstResponder];
[EmailAddressTextField resignFirstResponder];
[LastNameTextfield resignFirstResponder];
[PasswordTextfield resignFirstResponder];
[ConfirmPasswordTextfield resignFirstResponder];}
First of all you need to set the delegate right. Then You need to give your next textfield the becomeNextResponder call which should be ideally done in the shouldReturn delegate method.
In your viewDidLoad method, paste this.
self.emailTextField.tag = 2000;
self.nameTextField.tag = 3000;
self.numberTextField.tag = 4000;
Now edit your textFieldShouldReturn method like this:
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
if(textfield.tag<4000)
{
UITextField *nextTextField = (UITextField *)[self.view viewWithTag:textField.tag+1000];
[nextTextField becomeFirstResponder];
}
return YES;
}
you can always use a library to accomplish what you need to do !!
use this library : tpAvoidKeyboard
Read how to use it and there you go all the next buttons and clicks outside text fields to dismiss the keyboard and the the done button on the last textfield to dismiss the keyboard, all those features are already implemented

Change tint color of UIAlertview and UIActionsheet buttons

I am trying to adapt my application for iOS 7. The issue I am having is I can not change the tint color of some controls.
I did add
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
if (IOS7_OR_LATER)
self.window.tintColor = [self greenTintColor];
to my app delegate's
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
It mostly helped but color of message box and action sheet buttons is still the default blue.
How can I recolor all such buttons too?
Some screenshots:
As UIAlertView is deprecated You can. Use UIAlertController.
You can use tintColor property.
OLD
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.
-From Apple Doc
You can use tintColor property or You can use Some Custom Library for that, you can find it at cocoacontrols.com.
I was able to change the cancel button's text color to white in app delegate.
[[UIView appearance] setTintColor:[UIColor whiteColor]];
For Actionsheet You can use
Utilize the willPresentActionSheet delegate method of UIActionSheet to change the action sheet button color.
- (void)willPresentActionSheet:(UIActionSheet *)actionSheet
{
for (UIView *subview in actionSheet.subviews) {
if ([subview isKindOfClass:[UIButton class]]) {
UIButton *button = (UIButton *)subview;
button.titleLabel.textColor = [UIColor greenColor];
}
}
}
Combining best answers above, and updated for deprecation:
[[UIView appearanceWhenContainedInInstancesOfClasses:#[[UIAlertController class]]] setTintColor:[UIColor greenColor]];
or Swift:
UIView.appearance(whenContainedInInstancesOf: [UIAlertController.self]).tintColor = .green
Works in 2018, Swift 4 / iOS 12.
You can adjust the color by searching and modifying the UILabel in the subview hierarchy of the alert window that is created right after showing the alert:
- (void)setButtonColor:(UIColor*)buttonColor {
dispatch_after(dispatch_time(0,1), dispatch_get_main_queue(), ^{
NSMutableArray *buttonTitles = [NSMutableArray array];
for (NSUInteger index = 0; index < self.numberOfButtons; index++) {
[buttonTitles addObject:[self buttonTitleAtIndex:index]];
}
for (UILabel *label in [[[UIApplication sharedApplication] keyWindow] recursiveSubviewsOfKind:UILabel.class]) {
if ([buttonTitles containsObject:label.text]) {
label.textColor = buttonColor;
label.highlightedTextColor = buttonColor;
}
}
});
}
[alert show];
[alert setButtonColor:UIColor.redColor];
The recursiveSubviewsOfKind: method is a category on UIView that returns an array of views in the complete subview hierarchy of the given class or subclass.
for UIAlertView with colored buttons you can use the cocoapod "SDCAlertView"
about CocoaPods: http://www.cocoapods.org
how to install CocoaPods: https://www.youtube.com/watch?v=9_FbAlq2g9o&index=20&list=LLSyp50_buFrhXC0bqL3nfiw
In iOS 6.0 create custom view in App delegate
.h
UIView* _loadingView;
UIView* _subView;
UIActivityIndicatorView*loadingIndicator;
UITabBarController *tabBar_Controller;
NSTimer *timer;
#property (strong, nonatomic) UIView* _loadingView;
#property (strong, nonatomic) UIView* _subView;
.m- (void)fadeScreen
{
[UIView beginAnimations:nil context:nil]; // begins animation block
[UIView setAnimationDuration:3.0]; // sets animation duration
[UIView setAnimationDelegate:self]; // sets delegate for this block
[UIView setAnimationDidStopSelector:#selector(finishedFading)];
self.txtview.alpha = 0.0; // Fades the alpha channel of this view
[UIView commitAnimations]; // commits the animation block.  This
}
- (void) finishedFading
{
[self.txtview removeFromSuperview];
}
- (void)showConnectivity:(NSString *)strTitle
{
[_loadingView setBackgroundColor:[UIColor clearColor]];
[_loadingView setAlpha:0.5];
[_loadingView.layer setCornerRadius:10];
[self.window addSubview:_loadingView];
[_loadingView setHidden:NO];
[_subView.layer setCornerRadius:7];
[_subView setBackgroundColor:[UIColor colorWithHue:0.0f saturation:0.0f brightness:0.0f alpha:0.6]];
[_subView setOpaque:YES];
[self.window addSubview:_subView];
[_subView setHidden:NO];
[_loadingView setHidden:NO];
[_subView setHidden:NO];
loadingIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[loadingIndicator setFrame:CGRectMake(85,10,35,35)];
[_subView addSubview:loadingIndicator];
[loadingIndicator setBackgroundColor:[UIColor redColor]];
[loadingIndicator startAnimating];
UILabel *_lab=[[UILabel alloc]initWithFrame:CGRectMake(8,10,72,45)];
[_lab setText:strTitle];
[_lab setTextColor:[UIColor whiteColor]];
[_lab setBackgroundColor:[UIColor clearColor]];
[_lab setFont:[UIFont boldSystemFontOfSize:13.0]];
[_lab setTextAlignment:NSTextAlignmentCenter];
[_subView addSubview:_lab];
}
- (void)CoonectingViewHidden
{
[_loadingView setHidden:YES];
[_subView setHidden:YES];
NSArray *_aryViews = [_subView subviews];
for(int i = 0; i<[_aryViews count];i++)
{
id obj = [_aryViews objectAtIndex:i];
if(![obj isKindOfClass:[UIActivityIndicatorView class]])
[obj removeFromSuperview];
}
[loadingIndicator stopAnimating];
[loadingIndicator hidesWhenStopped];
}
in using .m
#import"Appdelegate.h"
- (void)showLoadingIndicator:(NSString *)message
{
AppDelegate *delegateObj2=(AppDelegate *)[UIApplication sharedApplication].delegate;
[delegateObj2 showConnectivity:message];
}
-(void)stopLoading
{
AppDelegate *delegateObj3=(AppDelegate *)[UIApplication sharedApplication].delegate;
[delegateObj3 CoonectingViewHidden];
}
// [self showLoadingIndicator:#"Loading"];
n
[self stopLoading];

how to move to the new view fast in ipad application

I have many view controller when i click on tableView cell it move to new view controller problem is that it takes alot of time to move to the next view may be due to view which is to load fetches data from server here is my code for the view which loads
- (void)viewDidLoad {
appDelegate = (MultipleDetailViewsWithNavigatorAppDelegate *)[[UIApplication sharedApplication] delegate];
UIImageView *bottomImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Nav.png"]];
[bottomImageView setFrame:CGRectMake(0,690,1024,34)];
[self.view addSubview:bottomImageView];
UIButton *button1=[UIButton buttonWithType:UIButtonTypeCustom];
[button1 setFrame:CGRectMake(10.0, 2.0, 88, 40.0)];
[button1 addTarget:self action:#selector(loginPressed) forControlEvents:UIControlEventTouchUpInside];
[button1 setImage:[UIImage imageNamed:#"logoutN.png"] forState:UIControlStateNormal];
UIBarButtonItem *button = [[UIBarButtonItem alloc]initWithCustomView:button1];
self.navigationItem.rightBarButtonItem = button;
self.title=#"Catalog";
popImageView.hidden=YES;
passwordLabel.hidden=YES;
userLabel.hidden=YES;
userNameTextField.hidden=YES;
userPasswordTextField.hidden=YES;
signInButton.hidden=YES;
tableView.hidden=NO;
searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
searching = NO;
letUserSelectRow = YES;
if(!categoryArray){
categoryArray =[[NSMutableArray alloc] init];
}
if(!userArray){
userArray =[[NSMutableArray alloc] init];
}
if(!subCategoryArray){
subCategoryArray =[[NSMutableArray alloc] init];
}
if(!subCategoryArrayOne){
subCategoryArrayOne =[[NSMutableArray alloc] init];
}
if(!subCategoryArrayTwo){
subCategoryArrayTwo =[[NSMutableArray alloc] init];
}
[self setUpData];
[self setUpDataSub];
[self setUpDataSubOne];
[self setUpDataSubTwo];
int count=[appDelegate.coffeeArray count];
NSLog(#"Arrays Content Are %d",count);
tableView.backgroundView = nil;
[super viewDidLoad];
}
is there any way so that view loads fast
Your view is not loading fast because of I guess those data set operation in viewDidLoad method . I think those are :
[self setUpData];
[self setUpDataSub];
[self setUpDataSubOne];
[self setUpDataSubTwo];
The one thing you could do is to move this operations to perform on the background thread . For that move this operations to the separate function and call that to perform on background from the view did load method :
-(void)dataOperations
{
[self setUpData];
[self setUpDataSub];
[self setUpDataSubOne];
[self setUpDataSubTwo];
}
and in viewDidLoad call this function in background:
[self performSelectorInBackground:#selector(dataOperations) withObject:nil];
Or you can directly call those method from viewDidLoad like :
[self performSelectorInBackground:#selector(setUpData) withObject:nil];

Create a custom UIButton class with delete function

I have a grid of UIButtons. When I hit an 'edit' button, I want a delete button to appear over each of these buttons, which when pressed, deletes the button (and associated data). A bit like apple's home screen, when you hold down a button and it starts to wiggle with an X in the corner.
According to this post: Subclass UIButton to add a property I can use Associative References to add a property to each of my buttons. I've tried to add a UIButton as a property of my custom UIButton but I can't seem to get it to appear and have the feeling this isn't the right way to go. Here's my custom button main:
#import "UIButton+Property.h"
#import <objc/runtime.h>
#implementation UIButton(Property)
static char UIB_DELETEBUTTON_KEY;
#dynamic deleteButton;
- (void)setDeleteButton:(UIButton *)deleteButton {
deleteButton = [UIButton buttonWithType:UIButtonTypeInfoDark];
deleteButton.frame = CGRectMake(100, 100, 50, 50);
objc_setAssociatedObject(self, &UIB_DELETEBUTTON_KEY, deleteButton, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIButton *)deleteButton {
return (UIButton *)objc_getAssociatedObject(self, &UIB_DELETEBUTTON_KEY);
}
#end
And here's where I add the buttons programmatically:
//Create a custom button for each custom book doc
for (int i = 0; i < [customBookDocs count]; ++i) {
BookDoc *customBookDoc = [customBookDocs objectAtIndex:i];
NSString *bookTitle = customBookDoc.book.title;
//create a button for each book
CGRect frame = CGRectMake(xCoord, yCoord, 200, 200);
UIButton *bookButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
bookButton.bookDoc = customBookDoc;
[bookButton setFrame:frame];
[bookButton setTitle:bookTitle forState:UIControlStateNormal];
[bookButton addTarget:self action:#selector(bookButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
xCoord += 250;
[self.view addSubview:bookButton];
[self.view addSubview:bookButton.deleteButton];
}
Is there an easier more sensible way to do this? Or am I on the right track?
ORIGINAL RESPONSE BEGAN:
... Someone else may have more to say about that, but I'm not sure why you'd need to use object association here. You can certainly add another button to your button as a property using regular subclassing, which is the route that I would take. ...
EDITS BELOW:
I thought that I had subclassed a UI control directly, but I realized that I was mistaken when I went to look for the code. #Joe rightly pointed out in the comments that there are issues with directly subclassing UI controls.
I was able to implement something like the functionality you described without using Associated Objects, by creating a wrapper class to hold the button and its related delete button. It works, but it's not very flexible, so I would generally recommend #Joe's method as a better solution.
Here's the relevant code:
I threw all of the code into the appDelegate to keep it simple. I don't recommend that in real life.
AppDelegate.m:
#implementation AppDelegate
#synthesize window = _window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
UIButton *toggleDeleteButtons = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[toggleDeleteButtons setFrame:CGRectMake(20, 45, 280, 45)];
[toggleDeleteButtons setTitle:#"Toggle Delete" forState:UIControlStateNormal];
[toggleDeleteButtons addTarget:self action:#selector(toggleDeleteButtonAction) forControlEvents:UIControlEventTouchUpInside];
[[self window] addSubview:toggleDeleteButtons];
ButtonWrapper *myButtonWrapper = [[ButtonWrapper alloc] init];
[[myButtonWrapper button] setFrame:CGRectMake(20, 100, 200, 45)];
[[myButtonWrapper button] setTitle:#"This is my button" forState:UIControlStateNormal];
[[myButtonWrapper deleteButton] addTarget:self action:#selector(buttonDeleteRequested:) forControlEvents:UIControlEventTouchUpInside];
[[myButtonWrapper deleteButton] setTag:0];
[[self window] addSubview:[myButtonWrapper button]];
buttonWrapper1 = myButtonWrapper;
// Added instance called anotherButtonWrapper with tag 1, as above
// Added instance called stillAnotherButtonWrapper with tag 2, as above
[self.window makeKeyAndVisible];
return YES;
}
- (void)toggleDeleteButtonAction {
static BOOL deleteButtonsShown;
[buttonWrapper1 showDeleteButton:!deleteButtonsShown];
[buttonWrapper2 showDeleteButton:!deleteButtonsShown];
[buttonWrapper3 showDeleteButton:!deleteButtonsShown];
deleteButtonsShown = !deleteButtonsShown;
}
- (void)buttonDeleteRequested:(UIButton *)deleteButton {
// delete the specified button here
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Delete" message:[NSString stringWithFormat:#"Delete was pressed on button %i",[deleteButton tag]]delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
ButtonWrapper.m:
#implementation ButtonWrapper
#synthesize button;
#synthesize deleteButton;
- (ButtonWrapper *)init {
ButtonWrapper *newWrapper = [ButtonWrapper alloc];
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[myButton setFrame:CGRectZero];
UIButton *myDeleteButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[myDeleteButton setFrame:CGRectMake(0, 0, 100, 40)];
[myDeleteButton setTitle:#"Delete" forState:UIControlStateNormal];
[myDeleteButton setHidden:TRUE];
[myButton addSubview:myDeleteButton];
[newWrapper setButton:myButton];
[newWrapper setDeleteButton:myDeleteButton];
return newWrapper;
}
- (void)showDeleteButton:(BOOL)showButton {
if (showButton) {
[[self deleteButton] setHidden:FALSE];
[[self deleteButton] setEnabled:TRUE]; }
else {
[[self deleteButton] setHidden:TRUE];
[[self deleteButton] setEnabled:FALSE];
}
}
#end
This solution did not require me to implement all of the UI properties, but it did require extra work to hook up the embedded delegates, which is cumbersome. There may be a way to pass the delegates into the wrapper at initialization, but I couldn't make it work.

iOS How to dismiss UIAlertView with one tap anywhere?

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];
}

Resources