How do I add an action to an alert view button? - xcode

I am trying to set up an alert view so that when the "Ok" button is pressed, an action sheet comes up with two options. I believe i have it in the right format and there are no errors, but when I run it, nothing happens. please help and thank you in advanced.
-(IBAction)sendSG:(id)sender{
UIAlertView *message = [[UIAlertView alloc]
initWithTitle:#"Send Study Guides!"
message:#"Please send your study guides to help create a bigger and more efficent network of study guides. You can send them by email, or you can take a picture of your study guide and send it to us."
delegate:self //Changed Here
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Ok", nil];
[message show];
}
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 0) {
UIActionSheet *sendOptions = [[UIActionSheet alloc]
initWithTitle:#"Add study guide"
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:#"Destructive Button"
otherButtonTitles:#"Email", #"Take a picture", nil];
[sendOptions showInView:self.view];
}
}
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 0) {
NSString *emailTitle = #"Study Guides";
NSArray *toRecipents = [NSArray arrayWithObject:#"blank#gmail.com"];
MFMailComposeViewController *mc = [[MFMailComposeViewController alloc] init];
[mc setSubject:emailTitle];
[mc setToRecipients:toRecipents];
[self presentViewController:mc animated:YES completion:NULL];
}
}

set Delegate of your alert view to self.
In your code,
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
is not being called due to this
Yes, and change your button index too… forgot to tell you that. it should be 1 for both alertView & actionSheet.
Make your viewController ActionSheet Delegate. add "UIActionSheetDelegate" in your viewController.h
#interface XYZViewController : UIViewController UIActionSheetDelegate (enclose in angular braces)
Everything Else will work fine.. Let me know if there is any issue

You got the wrong button index.
The button buttonIndex == 0 is the cancel button,buttonIndex == 1 is the ok button.

Try this in alert view's callback:
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// show action sheet here.
});
If it works for you, modify the delayInSeconds as you wish.

Related

UIAlertController Warning Message

I am using the below code for UIAlertController in my project.
if([[[UIDevice currentDevice] systemVersion]floatValue] >= 8.0){
UIAlertController * alert= [UIAlertController
alertControllerWithTitle:#"Input Error"
message:#"Please enter a valid email."
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* okAction = [UIAlertAction
actionWithTitle:#"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
[alert dismissViewControllerAnimated:YES completion:nil];
}];
[alert addAction:okAction];
[self presentViewController:alert animated:YES completion:nil];
}
else
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Input Error"
message:#"Please enter a valid email"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil, nil];
[alertView show];
}
I am getting the below waring message:
Warning: Attempt to present <UIAlertController: 0x7f8da58df1f0> on <MBComplaintsViewController: 0x7f8da36454d0> which is already presenting (null)
Kindly guide me how to properly use UIAlertController using Objective C.
Thanks,
Abin Koshy Cheriyan
Yes as per #Alexander you should not be dismissing the alert controller like this explicitly.
As per an apple engineer a new window gets added each time an UIAlertController is displayed so when we go for dismissing it the window is still there though the alert controller disappears.
So there are two ways to handle this -
Way 1 - No explicit dismiss
Do not explicitly dismiss the UIAlertController, let it be done by the user
Way 2 - Use your own window
Simply create a category on UIAertController
Here is the sample code -
.h
#import <UIKit/UIKit.h>
#interface UIAlertController (MyAdditions)
#property(nonatomic,strong) UIWindow *alertWindow;
-(void)show;
#end
In .m
#import "UIAlertController+MyAdditions.h"
#import <objc/runtime.h>
#implementation UIAlertController (MyAdditions)
#dynamic alertWindow;
- (void)setAlertWindow:(UIWindow *)alertWindow {
objc_setAssociatedObject(self, #selector(alertWindow), alertWindow, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIWindow *)alertWindow {
return objc_getAssociatedObject(self, #selector(alertWindow));
}
- (void)show {
self.alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.alertWindow.rootViewController = [[UIViewController alloc] init];
// window level = topmost + 1
UIWindow *topWindow = [UIApplication sharedApplication].windows.lastObject;
self.alertWindow.windowLevel = topWindow.windowLevel + 1;
[self.alertWindow makeKeyAndVisible];
[self.alertWindow.rootViewController presentViewController:self animated:YES completion:nil];
}
-(void)hide {
self.alertWindow.hidden = YES;
self.alertWindow = nil;
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
// just to ensure the window gets desroyed
self.alertWindow.hidden = YES;
self.alertWindow = nil;
}
To show the alert controller
UIAlertCntroller *alert = ## initialisation##;
// will show the alert
[alert show];
//to dismiss
[alert hide];
[alert dismissViewControllerAnimated:YES completion:nil];
Even you can checkout one of my sample implementation here
I don't know about your problem, but you shouldn't do that
handler:^(UIAlertAction * action)
{
[alert dismissViewControllerAnimated:YES completion:nil];
}];
It will be anyway dismissed on any of your actions.

iOS 8 UIAlertView not showing buttons

Display some alerts in my application using the UIAlertView in iOS 7.1 works perfectly in iOS 8 the alert appears, but without the buttons to cancel, OK, and others ... This causes the user can not close the alert and consequently gets stuck on this screen, having to close the application.
I tried to implement the UIAlertView and previous versions for iOS UIAlertController 8, see the code below:
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.0) {
UIAlertView *alerta = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"s000xS2", #"Alerta") message:NSLocalizedString(#"s000xS40", nil) delegate:self cancelButtonTitle:NSLocalizedString(#"s000xS34", #"Não") otherButtonTitles:NSLocalizedString(#"s000xS35", #"Sim"), nil];
[alerta show];
}else{
UIAlertController * alert= [UIAlertController
alertControllerWithTitle:NSLocalizedString(#"s000xS2", #"Alerta")
message:NSLocalizedString(#"s000xS40", nil)
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* sim = [UIAlertAction
actionWithTitle:NSLocalizedString(#"s000xS35", #"Sim")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
[Util abrirSite:[[[Player sharedPlayer] emissora] site]];
[alert dismissViewControllerAnimated:YES completion:nil];
}];
UIAlertAction* nao = [UIAlertAction
actionWithTitle:NSLocalizedString(#"s000xS34", #"Não")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
[alert dismissViewControllerAnimated:YES completion:nil];
}];
[alert addAction:sim];
[alert addAction:nao];
[self presentViewController:alert animated:NO completion:nil];
}
With this code I have the same problem, the buttons are not displayed in the alert, any suggestions to get around this?
Note, I'm using strings for internationalization, they usually work, already tested by placing a string directly (# "...") but it did not work.
Try this:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"ALERTA!" message:#"What will you do?" **preferredStyle:UIAlertControllerStyleAlert**];
__weak ViewController *wself = self;
UIAlertAction *nao = [UIAlertAction actionWithTitle:#"I'm doing something" ***style:UIAlertActionStyleCancel*** handler:^(UIAlertAction *action) {
__strong ViewController *sself = wself;
sself.**lbl**.text = #"You did something!"; **//the text "You did something!" gets displayed on a label(if created) named lbl**
}];
[alert addAction:nao];
[self presentViewController:alert animated:NO completion:nil];

Usernotes in ios

I want to create a user note form in my application,currently am using one textview inside a view its looking bad !! is there any other control suits for this purpose? Main aim is when user click the button a small textview will appear they can add comments there and save it into plist.
I want something like this(check the image)
i want that kind of usernotes (its my image) please give me some advices and helps to develop this..
Using UIAlertView with UITextView can be useful for you.
Implement UIAlertViewDelegate in .h file.
UITextView *comments;
-(IBAction)btnAddClicked:(id)sender
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Noreply Email" message:#"\n\n\n\n\n" delegate:self cancelButtonTitle:#"Close" otherButtonTitles:#"Send", nil];
comments = [[UITextView alloc] initWithFrame:CGRectMake(15,45, 255, 100)];
[comments setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"mailbody.png"]]];
[comments setFont:[UIFont fontWithName:#"Helvetica" size:15]];
comments.scrollEnabled = YES;
[comments becomeFirstResponder];
[alert addSubview:comments];
[alert show];
[alert release];
}
Here alert will be prompted with small textview you can add comments and then handle text inside delegate method like this.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if(buttonIndex==1) //Send button pressed
{
//handle your comment here
NSLog(#"%#",comments.text);
}
}

UIAlertview is called multiple times

I have the following code in my application. On a view controller I have two UIButton controls that each do different operations. When I press the first button I have a UIAlertView to confirm the operation. This works fine. I setup the second button the same way. When I press the second button the first UIAlertView appears briefly, and then the second UIAlertView appears. It works okay at that point but then the first UIAlertView appears again.
If I take out the UIAlertViews completely and just update a label on the view to indicate which button was pressed I don't get either button called a second time so I have isolated this to the inclusion of the UIAlertViews.
Can anyone point to something in my code that is causing this? Here's the code.
- (IBAction)clearInspectionsClicked {
UIAlertView *alertClear = [[UIAlertView alloc] initWithTitle:#"Please Confirm"
message:#"Clear out all inspection data?"
delegate:self
cancelButtonTitle:#"Clear"
otherButtonTitles:#"Cancel", nil];
[alertClear show];
}
- (IBAction)loadSampleDataClicked {
UIAlertView *alertLoad = [[UIAlertView alloc] initWithTitle:#"Please Confirm"
message:#"Load Sample data?"
delegate:self
cancelButtonTitle:#"Load"
otherButtonTitles:#"Cancel", nil];
[alertLoad show];
}
-(void) alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if ([title isEqualToString:#"Clear"])
{
[self clearInspections];
[self.StatusLabel setText:#"Inspection data has been cleared!"];
}
if ([title isEqualToString:#"Load"])
{
[self loadSampleData];
[self.StatusLabel setText:#"Sample data has been loaded!"];
}
}
Is it possible that you have one of the buttons wired to two of those actions? it is possible to wire multiple actions to one given control in Interface Builder, and it would cause this exact behavior.

Wait for [NSAlert beginSheetModalForWindow:...];

When I display an NSAlert like this, I get the response straight away:
int response;
NSAlert *alert = [NSAlert alertWithMessageText:... ...];
response = [alert runModal];
The problem is that this is application-modal and my application is document based. I display the alert in the current document's window by using sheets, like this:
int response;
NSAlert *alert = [NSAlert alertWithMessageText:... ...];
[alert beginSheetModalForWindow:aWindow
modalDelegate:self
didEndSelector:#selector(alertDidEnd:returnCode:contextInfo:)
contextInfo:&response];
//elsewhere
- (void) alertDidEnd:(NSAlert *) alert returnCode:(int) returnCode contextInfo:(int *) contextInfo
{
*contextInfo = returnCode;
}
The only issue with this is that beginSheetModalForWindow: returns straight away so I cannot reliably ask the user a question and wait for a response. This wouldn't be a big deal if I could split the task into two areas but I can't.
I have a loop that processes about 40 different objects (that are in a tree). If one object fails, I want the alert to show and ask the user whether to continue or abort (continue processing at the current branch), but since my application is document based, the Apple Human Interface Guidelines dictate to use sheets when the alert is specific to a document.
How can I display the alert sheet and wait for a response?
We created a category on NSAlert to run alerts synchronously, just like application-modal dialogs:
NSInteger result;
// Run the alert as a sheet on the main window
result = [alert runModalSheet];
// Run the alert as a sheet on some other window
result = [alert runModalSheetForWindow:window];
The code is available via GitHub, and the current version posted below for completeness.
Header file NSAlert+SynchronousSheet.h:
#import <Cocoa/Cocoa.h>
#interface NSAlert (SynchronousSheet)
-(NSInteger) runModalSheetForWindow:(NSWindow *)aWindow;
-(NSInteger) runModalSheet;
#end
Implementation file NSAlert+SynchronousSheet.m:
#import "NSAlert+SynchronousSheet.h"
// Private methods -- use prefixes to avoid collisions with Apple's methods
#interface NSAlert ()
-(IBAction) BE_stopSynchronousSheet:(id)sender; // hide sheet & stop modal
-(void) BE_beginSheetModalForWindow:(NSWindow *)aWindow;
#end
#implementation NSAlert (SynchronousSheet)
-(NSInteger) runModalSheetForWindow:(NSWindow *)aWindow {
// Set ourselves as the target for button clicks
for (NSButton *button in [self buttons]) {
[button setTarget:self];
[button setAction:#selector(BE_stopSynchronousSheet:)];
}
// Bring up the sheet and wait until stopSynchronousSheet is triggered by a button click
[self performSelectorOnMainThread:#selector(BE_beginSheetModalForWindow:) withObject:aWindow waitUntilDone:YES];
NSInteger modalCode = [NSApp runModalForWindow:[self window]];
// This is called only after stopSynchronousSheet is called (that is,
// one of the buttons is clicked)
[NSApp performSelectorOnMainThread:#selector(endSheet:) withObject:[self window] waitUntilDone:YES];
// Remove the sheet from the screen
[[self window] performSelectorOnMainThread:#selector(orderOut:) withObject:self waitUntilDone:YES];
return modalCode;
}
-(NSInteger) runModalSheet {
return [self runModalSheetForWindow:[NSApp mainWindow]];
}
#pragma mark Private methods
-(IBAction) BE_stopSynchronousSheet:(id)sender {
// See which of the buttons was clicked
NSUInteger clickedButtonIndex = [[self buttons] indexOfObject:sender];
// Be consistent with Apple's documentation (see NSAlert's addButtonWithTitle) so that
// the fourth button is numbered NSAlertThirdButtonReturn + 1, and so on
NSInteger modalCode = 0;
if (clickedButtonIndex == NSAlertFirstButtonReturn)
modalCode = NSAlertFirstButtonReturn;
else if (clickedButtonIndex == NSAlertSecondButtonReturn)
modalCode = NSAlertSecondButtonReturn;
else if (clickedButtonIndex == NSAlertThirdButtonReturn)
modalCode = NSAlertThirdButtonReturn;
else
modalCode = NSAlertThirdButtonReturn + (clickedButtonIndex - 2);
[NSApp stopModalWithCode:modalCode];
}
-(void) BE_beginSheetModalForWindow:(NSWindow *)aWindow {
[self beginSheetModalForWindow:aWindow modalDelegate:nil didEndSelector:nil contextInfo:nil];
}
#end
The solution is to call
[NSApp runModalForWindow:alert];
after beginSheetModalForWindow. Also, you need to implement a delegate that catches the "dialog has closed" action, and calls [NSApp stopModal] in response.
Here is a NSAlert category that solves the issue (as suggested by Philipp with the solution proposed by Frederick and improved by Laurent P.: I use a code block instead of a delegate, so it is simplified once again).
#implementation NSAlert (Cat)
-(NSInteger) runModalSheetForWindow:(NSWindow *)aWindow
{
[self beginSheetModalForWindow:aWindow completionHandler:^(NSModalResponse returnCode)
{ [NSApp stopModalWithCode:returnCode]; } ];
NSInteger modalCode = [NSApp runModalForWindow:[self window]];
return modalCode;
}
-(NSInteger) runModalSheet {
return [self runModalSheetForWindow:[NSApp mainWindow]];
}
#end
Just in case anyone comes looking for this (I did), I solved this with the following:
#interface AlertSync: NSObject {
NSInteger returnCode;
}
- (id) initWithAlert: (NSAlert*) alert asSheetForWindow: (NSWindow*) window;
- (NSInteger) run;
#end
#implementation AlertSync
- (id) initWithAlert: (NSAlert*) alert asSheetForWindow: (NSWindow*) window {
self = [super init];
[alert beginSheetModalForWindow: window
modalDelegate: self didEndSelector: #selector(alertDidEnd:returnCode:) contextInfo: NULL];
return self;
}
- (NSInteger) run {
[[NSApplication sharedApplication] run];
return returnCode;
}
- (void) alertDidEnd: (NSAlert*) alert returnCode: (NSInteger) aReturnCode {
returnCode = aReturnCode;
[[NSApplication sharedApplication] stopModal];
}
#end
Then running an NSAlert synchronously is as simple as:
AlertSync* sync = [[AlertSync alloc] initWithAlert: alert asSheetForWindow: window];
int returnCode = [sync run];
[sync release];
Note there is potential for re-entrancy issues as discussed, so be careful if doing this.
Unfortunately, there is not much you can do here. You basically have to make a decision: re-architect your application so that it can process the object in an asynchronous manner or use the non-approved, deprecated architecture of presenting application modal alerts.
Without knowing any information about your actual design and how you processes these objects, it's hard to give any further information. Off the top of my head, though, a couple of thoughts might be:
Process the objects in another thread that communicates with the main thread through some kind of run loop signal or queue. If the window's object tree gets interrupted, it signals the main thread that it was interrupted and waits on a signal from the main thread with information about what to do (continue this branch or abort). The main thread then presents the document-modal window and signals the process thread after the user chooses what to do.
This may be really over-complicated for what you need, however. In that case, my recommendation would be to just go with the deprecated usage, but it really depends on your user requirements.
Swift 5:
extension NSAlert {
/// Runs this alert as a sheet.
/// - Parameter sheetWindow: Parent window for the sheet.
func runSheetModal(for sheetWindow: NSWindow) -> NSApplication.ModalResponse {
beginSheetModal(for: sheetWindow, completionHandler: NSApp.stopModal(withCode:))
return NSApp.runModal(for: sheetWindow)
}
}
here is my answer:
Create a global class variable 'NSInteger alertReturnStatus'
- (void)alertDidEndSheet:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
{
[[sheet window] orderOut:self];
// make the returnCode publicly available after closing the sheet
alertReturnStatus = returnCode;
}
- (BOOL)testSomething
{
if(2 != 3) {
// Init the return value
alertReturnStatus = -1;
NSAlert *alert = [[[NSAlert alloc] init] autorelease];
[alert addButtonWithTitle:#"OK"];
[alert addButtonWithTitle:#"Cancel"];
[alert setMessageText:NSLocalizedString(#"Warning", #"warning")];
[alert setInformativeText:#"Press OK for OK"];
[alert setAlertStyle:NSWarningAlertStyle];
[alert setShowsHelp:NO];
[alert setShowsSuppressionButton:NO];
[alert beginSheetModalForWindow:[self window] modalDelegate:self didEndSelector:#selector(alertDidEndSheet:returnCode:contextInfo:) contextInfo:nil];
// wait for the sheet
NSModalSession session = [NSApp beginModalSessionForWindow:[alert window]];
for (;;) {
// alertReturnStatus will be set in alertDidEndSheet:returnCode:contextInfo:
if(alertReturnStatus != -1)
break;
// Execute code on DefaultRunLoop
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
// Break the run loop if sheet was closed
if ([NSApp runModalSession:session] != NSRunContinuesResponse
|| ![[alert window] isVisible])
break;
// Execute code on DefaultRunLoop
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
}
[NSApp endModalSession:session];
[NSApp endSheet:[alert window]];
// Check the returnCode by using the global variable alertReturnStatus
if(alertReturnStatus == NSAlertFirstButtonReturn) {
return YES;
}
return NO;
}
return YES;
}
Hope it'll be of some help,
Cheers
--Hans
This is the version of Laurent, et al., above, translated into Swift 1.2 for Xcode 6.4 (latest working version as of today) and tested in my app. Thanks to all those who contributed to make this work! The standard documentation from Apple gave me no clues as to how go about this, at least not anywhere that I could find.
One mystery remains to me: why I had to use the double exclamation point in the final function. NSApplication.mainWindow is supposed to be just an optional NSWindow (NSWindow?), right? But the compiler gave the error shown until I used the second '!'.
extension NSAlert {
func runModalSheetForWindow( aWindow: NSWindow ) -> Int {
self.beginSheetModalForWindow(aWindow) { returnCode in
NSApp.stopModalWithCode(returnCode)
}
let modalCode = NSApp.runModalForWindow(self.window as! NSWindow)
return modalCode
}
func runModalSheet() -> Int {
// Swift 1.2 gives the following error if only using one '!' below:
// Value of optional type 'NSWindow?' not unwrapped; did you mean to use '!' or '?'?
return runModalSheetForWindow(NSApp.mainWindow!!)
}
}
Unlike Windows I don't believe there's a way to block on modal dialogs. The input (e.g. the user clicking a button) will be processed on your main thread so there's no way of blocking.
For your task you will either have to pass the message up the stack and then continue where you left off.
When one object fails, stop processing the objects in the tree, make a note of which object failed (assuming that there is an order and you can pick up where you left off), and throw up the sheet. When the user dismisses the sheet, have the didEndSelector: method start processing again from the object that it left off with, or don't, depending on the returnCode.
- (bool) windowShouldClose: (id) sender
{// printf("windowShouldClose..........\n");
NSAlert *alert=[[NSAlert alloc ]init];
[alert setMessageText:#"save file before closing?"];
[alert setInformativeText:#"voorkom verlies van laatste wijzigingen"];
[alert addButtonWithTitle:#"save"];
[alert addButtonWithTitle:#"Quit"];
[alert addButtonWithTitle:#"cancel"];
[alert beginSheetModalForWindow: _window modalDelegate: self
didEndSelector: #selector(alertDidEnd: returnCode: contextInfo:)
contextInfo: nil];
return false;
}
You can use dispatch_group_wait(group, DISPATCH_TIME_FOREVER);:
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:#"alertMessage"];
[alert addButtonWithTitle:#"Cancel"];
[alert addButtonWithTitle:#"Ok"];
dispatch_async(dispatch_get_main_queue(), ^{
[alert beginSheetModalForWindow:progressController.window completionHandler:^(NSModalResponse returnCode) {
if (returnCode == NSAlertSecondButtonReturn) {
// do something when the user clicks Ok
} else {
// do something when the user clicks Cancel
}
dispatch_group_leave(group);
}];
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
//you can continue your code here
Hope that helps.

Resources