Im trying to add a UIAlertView to warn the user that the link will open in Safari. The user can then choose OK(open the url) or cancel which should just close the alert and return to the links.
I have three different UIButtons which has 3 different URLs.
Right now ive added a IBAction to all buttons and all buttons has Tags (which i think i can use somehow :D). I guess - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex: delegate will be good to use to..
My question: how should the UIAlertView know what URL to open i user clicks ok?
I suggest that you use a subclass of UIAlertView that is able to track some more properties along. I do this in all my projects and it is much simpler.
One solution to do this would be to subclass UIAlertView to MyAlertView and add a #property(nonatomic, retain) id userInfo; or #property(nonatomic, retain) NSURL* urlToOpen. Thus you can attach custom data to your UIAlertView and retrieve it in the delegate method to do whatever you need with it.
Another solution, and my preferred one actually, is to add Objective-C blocks support to UIAlertView, to be able to use UIAlertView using the blocks API instead of using a delegate. This is particularly useful if you use multiple UIAlertViews in the same class and with the same delegate, as using a single delegate to handle the different instances is a mess.
I personally use this technique all the time, as it also makes my code more readable by having the code that executes when the button is tapped right next to the code that shows the alert, instead of having it at a complete different places when you use delegate methods.
You can look at my OHAlertView subclass here on GitHub that implement this already. The usage is really simple and allow you to use blocks for each alert view instead of a common delegate, see below.
Usage Example
-(void)confirmOpenURL:(NSURL*)url
{
NSString* message = [NSString string WithFormat:#"Open %# in Safari?",
url.absoluteString];
[OHAlertView showAlertWithTitle:#"Open URL"
message:message
cancelButton:#"No"
okButton:#"Yes"
onButtonTapped:^(OHAlertView* alert, NSInteger buttonIndex)
{
if (buttonIndex != alert.cancelButtonIndex)
{
// If user tapped "Yes" and not "No"
[[UIApplication sharedApplication] openURL:url];
}
}];
}
Then each button can have its own action:
-(IBAction)button1Action
{
[self confirmOpenURL:[NSURL URLWithString:#"http://www.google.com"]];
}
-(IBAction)button2Action
{
[self confirmOpenURL:[NSURL URLWithString:#"http://www.stackoverflow.com"]];
}
Or you can have a common IBAction for all your buttons opening URLs:
-(IBAction)commonButtonAction:(UIButton*)sender
{
NSUInteger tag = sender.tag;
NSString* urls[] = { #"http://www.google.com", #"http://www.stackoverflow.com" };
NSURL* buttonURL = [NSURL URLWithString: urls[tag] ]; // in practice you should check that tag is between 0 and the number of urls to be sure, that's just an example here
[self confirmOpenURL:buttonURL];
}
Solved it like this:
added a tag when creating the UIAlertView. Like this:
- (IBAction)PressedButton {
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Link"
message:#"Want to open in safari?"
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
message.tag = 2; //different tag for each button
[message addButtonWithTitle:#"Cancel"];
[message show];
}
Then when - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex delegate was thrown I did this:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == alertView.cancelButtonIndex){
if (alertView.tag == 1)
{
//go to URL1
}
else if (alertView.tag == 2)
{
//go to URL2
}
else if (alertView.tag == 3)
{
//go to URL3
}
}
}
Your button action method should have a signature like this:
-(void)doSomething:(id)sender;
whereby sender will be the button. Based on this you could find out which URL is meant.
Related
In my iOS 7 app, I need to verify the user wants to deleta a selected record from Cord Data. I have the UIAlertViewDelegate defined in the .h file. This is the code to display the alert:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Warning"
message:#"Are you sure you want to delete this record?"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Delete", nil];
[alert show];
if(alertButtonTapped == 0)
return;
// remainder of code to delete record follows (was omitted)
This is the code to check which button was tapped:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
alertButtonTapped = [NSNumber numberWithInteger:buttonIndex];
return;
}
The problem is the alert is displayed and then immediately falls through the remainder of the code in that method. I have never seen this before; usually it blocks until the user has responded by tapping one of the buttons (at least I thought it did). What do I need to do to make this alert block until the user responds? (I was looking at UIAlertView blocks, but not sure that would do the job since it appears to use a different thread)
This is how UIAlertView works -- it doesn't block, thus why it has the UIAlertViewDelegate methods for actually implementing a response.
If the "remainder of code" is what should happen after they tap a button (e.g. the "Delete" button), then move all of that code into the delegate method, like:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex != alertView.cancelButtonIndex) {
// code to delete record
}
}
EDIT - adding example to answer a comment
So if you have multiple UIAlertViews in the same class, you could differentiate between them using the tag attribute of UIView (UIAlertView is-a UIView). So it could be something like this:
const NSInteger kDeleteAlertTag = 100; // declared at the top of your .m file, perhaps.
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Warning"
message:#"Are you sure you want to delete this record?"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Delete", nil];
alert.tag = kDeleteAlertTag;
[alert show];
Then your delegate response might look like this:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (alertView.tag == kDeleteAlertTag) {
if (buttonIndex != alertView.cancelButtonIndex) {
// code to delete record
}
}
else if (alertView.tag = kDoSomethingElseAlertTag) {
DoSomethingElse();
}
}
I am trying to do Error Checking for a Correct Time in a UItextField. I didn't want an error to pop up while the user is typing (To give them the chance to fix it first) so I thought I would do the Check during an EditingDidEnd Action. I setup ones for Hours, Minutes and Seconds. Here is the code I tried for inHours:
- (IBAction)inHourEditEnd:(id)sender
{
// Check to make sure value is between 0 & 23 Hours
if ([[[self inHour] text] intValue] >= 0 && [[[self inHour] text] intValue] < 24) {
[self updateDuration];
} else {
NSString *errorDescription = [NSString stringWithFormat:#"%# hours is not a valid start time. Please enter an hour between 0 and 23", [[self inHour] text]];
UIAlertView *errorMessage = [[UIAlertView alloc] initWithTitle:#"Ivalid Hour for Start Time"
message:errorDescription
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[errorMessage show];
[_inHour becomeFirstResponder];
}
}
The alert works fine but it won't go back to the textfield (inHour) after showing the alert.
It just stays on whatever textField I tapped to cause EditingDidEnd. Searching here I found a way to make the alertView send the user back to the right textbox using this code:
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
//Checks For Approval
if (buttonIndex == 0) {
[_inHour becomeFirstResponder];
}
}
But this will only work for the first box and I need to make it work with inHour, inMinute and inSeconds.
Any Suggestions how I can make this work?
Is either one of these paths in the right direction?
Thank you for any help.
If you really want to do your validation after the text field has already lost first responder status, you could add a property to your view controller to track the text field that needs to receive becomeFirstResponder:
// in your .m file, declare a class extension for "private" members
#interface MyViewController ()
#property (weak, nonatomic) UITextField *lastValidationFailureField;
#end
// add this to inHourEditEnd:
self.lastValidationFailureField = _inHour;
// use this property in alertView:clickedButtonAtIndex:
[self.lastValidationFailureField becomeFirstResponder];
self.lastValidationFailureField = nil;
Alternatively, you could prevent the user from leaving the text field. Have your view controller implement UITextFieldDelegate and set all of your text fields’ delegate outlets to your view controller. Then implement textFieldShouldEndEditing:
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
if (textField == [self inHour]) {
if ([[textField text] intValue] >= 0 && [[textField text] intValue] < 24) {
[self updateDuration];
} else {
NSString *errorDescription = ...
UIAlertView *errorMessage = ...
[errorMessage show];
// prevent the text field from even losing first responder status
return NO;
}
}
if (textField == [some someOtherTextFieldToValidate)] {
...
}
return YES;
}
I put all of the logic in a single method here for brevity, I would recommend at least some minimal refactoring of that code if you have more than a couple of text fields to validate.
You would not need your actions method such as inHourEditEnd: any more.
I've been looking all over the internet for a solution to this problem, yet to find one that I can understand. So here it goes.
I have my iOS application, on which on the first launch of the application, will display a UIAlertView. I want one of the buttons to send the user to a new viewController to view some essential information.
I have gotten my UIAlertView configured properly, here's the code for the actionSheet:
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0)
{
//Code to open a new viewController???
}
if (buttonIndex == 1)
{
//User can exit the application if they do not wish to continue
exit(0); }
}
}
I have a viewController created named webViewViewController, that's what I want users to reach when they tap the first button. Any ideas on how to do this? I have ARC disabled, other 'solutions' i've come across keep giving me errors.
Thanks all, would appreciate some guidance here.
If your current viewcontroller is in a Navigation Controller:
UIViewController *myViewController = [[UIViewController alloc] init];
myViewController.title = #"My First View";
//to push the UIView.
[self.navigationController pushViewController:myViewController animated:YES];
And never use exit(0)!
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0)
{
NewViewController *controller=[[NewViewController alloc]initWithNibName:#"NewViewController" bundle:nil];
self.modalTransitionStyle=UIModalTransitionStyleCrossDissolve;
[self presentViewController:controller animated:YES completion:nil];
}
drasick has given sufficient answer but if you need to use model view controller refer above.
Use:
UIViewController *yourViewController = [[UIViewController alloc] init];
[self.navigationController presentModalViewController:myViewController animated:YES];
to push a modalViewController.(This is depriciated in iOS 6, but will work)
I have code setting up a UIAlertView:
-(IBAction)showMessage
{
//NSInteger *buttonIndex = NULL;
UIAlertView *message = [[UIAlertView alloc] initWithTitle:nil
message:nil
delegate:nil
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Email",#"SMS",#"Facebook",#"Twitter", nil];
[message show];
}
and code explaining what to do once the user makes a choice:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 1){
[self openMail];
}
else if (buttonIndex == 2)
//etc.
}
But what I can't figure out is how to connect the two. The obvious answer is to add a line to the first piece of code calling alertView: clickedButtonAtIndex on UIAlertView *message, but how do you assign buttonIndex? How do you tell alertView whether the user has chosen Facebook, Email, etc?
Add to the header file if you haven't already:
#interface YourClass : UIViewController <UIAlertViewDelegate>
{
}
...etc
and in your AlertView code change delegate:nil to delegate:self
For more information have a look at the Apple Docs:
http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIAlertViewDelegate_Protocol/UIAlertViewDelegate/UIAlertViewDelegate.html
From the docs:
The button indices start at 0. If this is the cancel button index, the alert view is canceling. If -1, the cancel button index is not set.
(1) You want to set delegate:self when you initialize the message. Also, in your interface declaration, do something like:
#interface myClass: UIViewController <UIAlertViewDelegate>
This way, your handler function automatically gets called when the user makes a selection.
(2) Buttons are indexed in the order you listed them.
(3) Since you have a lot of options for the user to choose from, it is better to use UIActionSheet instead of UIAlertView. Alerts are generally used for simple Yes/No-type selections.
Hope this helps!
I want to open a UIViewController if a UIALertView Button is pressed.
I have the code for that. However, the uiviewcontroller is not being opened :(
I am sure the uialertview is working fine and all. and the code for the uiviewcontroller is fine as well. (it works when called from other places).
Any help ?
Thanks.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0) //Yes, Sign Me Up!
{
NSLog(#"0");
ViewerWebController *web = [[ViewerWebController alloc] initWithURL:[NSURL URLWithString:#"http://www.funimation.com/"]];
[web setShowToolbar:YES];
[self.navigationController pushViewController:web animated:YES];
[web release];
}
else if (buttonIndex == 1) //Remove from List
{
NSLog(#"1");
[[NSUserDefaults standardUserDefaults] setBool:false forKey:#"subscribeButtonOption"];
[_tableView reloadData];
}
else if (buttonIndex == 2) //"Maybe Later"
{
NSLog(#"2");
}
}
The alert view doesn't have a navigation controller. You would need to keep a reference to the navigation controller you want to push the view controller on to.
This may work..
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
// the user clicked one of the OK/Cancel buttons
if (buttonIndex == 0)
{
YourRootNavigationController *navController = [YourRootNavigationController sharedInstance]; // singleton
[navController pushViewController:YOUR_CONTROLLER animated:YES];
}
else
{
NSLog(#"cancel");
}
}
Use didDismissWithButtonIndex rather clickedButtonAtIndex.
The former is invoked after the UIAlertView has been removed from the screen hierarchy, while the latter occurs while the UIAlertView is still on screen. While on screen, UIAlertView alters the nature of the app; that means bad things happen when trying to push views to the nav controller.
This misuse of clickedButtonAtIndex seems to be common misinformation floating around the internet and StackOverflow. Cost me hours of frustration.