I am developing an app that allows users to take pictures and send them by mail (xcode version 5.1.1). After the mail is sent, a confirmation message pops up:
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error{
switch (result)
{
case MFMailComposeResultCancelled:
[[[UIAlertView alloc]initWithTitle:#"Message Cancelled" message:nil delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil]show];
break;
case MFMailComposeResultSent:
[[[UIAlertView alloc]initWithTitle:#"Message Sent" message:#"Thank you for your help." delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil]show]; break;
default:
break;
}
[self dismissViewControllerAnimated:NO completion:nil];
}
On click of "OK" in the simulator, Xcode highlights a code in the main.m file, with the phrase "Thread 1: signal SIGABRT":
int main(int argc, char * argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
When I test the app on the iPhone, same thing, it crashes on click of OK.
Do you have any idea on how to solve this issue?
Many thanks in advance for your help and advice
The issue is most likely caused by the fact that you set the alert view's delegate to self and then you dismiss self. When you then tap OK on the alert view, it attempt to access its delegate but the delegate was dismissed so the app crashes.
There are two fixes:
Pass nil to the delegate parameter when creating the alert views. You have no need to process any alert view actions.
When dismissing the mail controller, do it by as follows:
code:
[controller dismissViewControllerAnimated:YES completion:nil];
If I am interpreting your question correctly, it is after you display the UIAlertView, and the user clicks on "OK" to dismiss your UIAlertView that you get the crash. If so, then the crash is most likely happening in your UIAlertView delegate inside:
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
}
Try setting a breakpoint near the top of that method, then step through the code until the error occurs (after you press "OK").
In there you will probably find that you are attempting to access an object that no longer exists. If you cannot locate the error there, then it would be best to post the code for your didDismissWithButtonIndex: method.
Related
I am trying to give "bonus points" for users who either tweet my advertisement message or post my advertisement message on Facebook. How can I detect if...
1) The user actually pressed the post button (this way I can give them the bonus points in app)?
2) The user did not change any text in the message box (my advertisement)?
On a side note, is it possible to make the message box for these un-editable? Here is my code to actually present the message to post each:
- (IBAction)tweetButtonPressed:(id)sender
{
if([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter])
{
SLComposeViewController *tweetSheet = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
[tweetSheet setInitialText:#"My advertisement message goes here."];
[self presentViewController:tweetSheet animated:YES completion:nil];
}
else
{
UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:#"Twitter Connection Failed"
message:#"Make sure your device has an internet connection and you are connected to Twitter through your iPhone settings."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
}
}
- (IBAction)facebookPostButtonPressed:(id)sender
{
if([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook])
{
SLComposeViewController *controller = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
[controller setInitialText:#"My advertisement message goes here."];
[self presentViewController:controller animated:YES completion:nil];
}
else
{
UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:#"Facebook Connection Failed"
message:#"Make sure your device has an internet connection and you are connected to Facebook through your iPhone settings."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
}
}
You can use compliton hander to know if he press the post button, however you can't know what the content of the post.
If you realy want to you need to implement facebook yourself than you can even check the privecy of the post.
I say this because when a app ask me to post I always do it as "Private" so no one see but I still get the "Bonus".
Apple Docs
Possible values for the result parameter of the completionHandler property.
Declaration
OBJECTIVE-C
typedef NS_ENUM (NSInteger,
SLComposeViewControllerResult ) {
SLComposeViewControllerResultCancelled,
SLComposeViewControllerResultDone
};
Constants
SLComposeViewControllerResultCancelled
The view controller is dismissed without sending the post. For example, the user selects Cancel or the account is not available.
Available in iOS 6.0 and later.
SLComposeViewControllerResultDone
The view controller is dismissed and the message is being sent in the background. This occurs when the user selects Done.
Available in iOS 6.0 and later.
https://developer.apple.com/library/ios/documentation/NetworkingInternet/Reference/SLComposeViewController_Class/#//apple_ref/c/tdef/SLComposeViewControllerResult
I'm using below code to send sms. We want to find the numbers user is sending text and give them free credit. I just wonder if it is possible to get the numbers which user sent text to.
-(IBAction)promotionSms{
if(![MFMessageComposeViewController canSendText]) {
UIAlertView *warningAlert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Your device doesn't support SMS!" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[warningAlert show];
return;
}
NSString *message = [NSString stringWithFormat:#"Text Here"];
MFMessageComposeViewController *messageController = [[MFMessageComposeViewController alloc] init];
messageController.messageComposeDelegate = self;
[messageController setBody:message];
// Present message view controller on screen
[self presentViewController:messageController animated:YES completion:nil];
}
- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result
{
[self dismissViewControllerAnimated:YES completion:nil];
if (result == MessageComposeResultCancelled)
NSLog(#"Message cancelled");
else if (result == MessageComposeResultSent)
NSLog(#"Message sent");
else
NSLog(#"Message failed");
}
There is no way to know the number to which user sent the message.
An Alternative would be like:
Implement ABPeoplePickerNavigationController for choosing a contact within your application. If they choose a contact present the MFMessageComposeViewController with your message and the number (chosen on the ABPeoplePickerNavigationController).
In that way you can keep track the number, there is no other way to do that.
In above case also after presenting the MFMessageComposeViewController, user can edit the number from there, you can't get the edited number then.
#MidhunMP answer is good, however there's one other way that you may implement.
Create a view which takes user name (if needed) and his phone number, and then on a button tap open MFMessageComposeViewController to send the message.
As I can see in your code, you should only give him a credit when message status is MessageComposeResultSent.
However, there's always chance of (as Midhun told), that user may change their number in MFMessageComposeViewController. The only way to overcome to this is to send SMS by your app only (doesn't matter via some SDKs or via APIs).
I'm aborting my iOS Application by below methods
-(void)cancelSelected
{
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:nil message:#"Are you sure you want to exit?" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
[alert show];
alert = nil;
}
Method 1 :
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex)
abort();
}
Method 2 :
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex)
[NSException raise:#"Disagree terms and conditions." format:#"Quit, Cancel"];
}
Shall I do this to quit my iOS Application programmatically?
Will this abort() method leads to reject my app?
Thanks!
See QA1561:
Q: How do I programmatically quit my iOS application?
A: There is no API provided for gracefully terminating an iOS
application.
In iOS, the user presses the Home button to close applications. Should
your application have conditions in which it cannot provide its
intended function, the recommended approach is to display an alert for
the user that indicates the nature of the problem and possible actions
the user could take — turning on WiFi, enabling Location Services,
etc. Allow the user to terminate the application at their own
discretion.
Yes the codes above will result in a reject. Use this code instead in your OK button of alert:
UIControl().sendAction(#selector(URLSessionTask.suspend), to: UIApplication.shared, for: nil)
Yes, generally you will get rejected for that.
Just present an alert to the user with a singe option, so they must approve to dismiss the alert. Then, if they dismiss (approve) they can use the app and if they don't they can't and must quit the app manually.
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"Game Over" message:#"Your time is up" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *close = [UIAlertAction actionWithTitle:#"close" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
**exit(0);**
}];
UIAlertAction *playagain = [UIAlertAction actionWithTitle:#"Play again" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
[self viewDidLoad];
}];
[alert addAction:close];
[alert addAction:playagain];
[self presentViewController:alert animated:YES completion:nil];
Use exit(0) for close current application
You can use below code to Quit iOS Application Programmatically with UIAlertView :-
Step 1:
Delegate "UIAlertViewDelegate" to your viewcontroller.h
for example:
#interface User_mail_List : UIViewController< UIAlertViewDelegate >
Step 2:
//create your UIAlertView
UIAlertView *exit_alertView= [[UIAlertView alloc] initWithTitle:#"Bizbilla !" message:#"\nAre you sure you want to Exit ?" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes",nil];
[exit_alertView show];
Step 3:
-(void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex{
if(alertView==exit_alertView){//exit Alert Fns Start,,,,,
if(buttonIndex==1){
exit(0);
}
}//exit Alert Fns End,,,,,
}
thanks,
On your alertview button click
You can use: exit(0)?
Or,
[[NSThread mainThread] exit], using this you can quit ios app.
How about calling fatalError()
function? I've just used it, everything works as expected. (Hope this will not cause a rejection though.)
I would like when my app starts for it to show an Alert view for about 5 seconds, then depending on the outcome of a background process, it will show another Alert view.
The problem I am experiencing is that when I try to use Sleep to wait for a background process to occur. The first Alert does not show and wait the 5 seconds. The app shows the first view of the app, and then after 5 seconds the first Alert shows briefly.
What do I need to do to perform what I wish.
Here is my code.
- (void)viewDidAppear:(BOOL)animated
{
SSGlobalSettings *connSettings = [SSGlobalSettings sharedManager];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Connecting" message:#"Please wait, while your device connects" delegate:Nil cancelButtonTitle:nil otherButtonTitles: nil];
[alertView show];
[NSThread sleepForTimeInterval:5.0f];
[alertView dismissWithClickedButtonIndex: alertView.cancelButtonIndex animated: YES];
if ([connSettings.connectionStatus isEqual: #"Not Found"])
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Connection Failed" message:#"Cannot find your device on the network" delegate:Nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alertView show];
}
else
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Connection Success" message:#"WYour device has been found on the network" delegate:#"OK" cancelButtonTitle:nil otherButtonTitles: nil];
[alertView show];
}
}
Don't use sleep on the main thread. Ever. Also don't update UI from a background thread.
What you want to do is to display your alert on the main thread and return.
Then, when your networking code completes, have it send a message to the main thread. In the main thread, when you receive a notice that it's done, remove the alert.
It's not working right because you are trying to tell the main thread of the app to sleep. If you are telling that thread to sleep, then you're most likely not going to allow any UI updating to occur during that time, because all UI updating happens on the main thread.
What you need to do is move the code for showing the second UIAlertView to a second method, and then call the method - (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay, passing it that second method for the selector, and giving it a delay of 5 seconds. Make sure you remove the call to sleep the main thread.
However, this still seems like a bad practice, because this action should be occurring as a result of your background task completing. So if you can, you should be running the code to show the second alert view upon completion of that task, which most likely could finish in varying amounts of time.
you block the main thread in you way.
i think it seems that you just want the user not to do anything before the first 5 sec(to let you connection connect successfully?), if so, lots ways could do that, e.g. just show a view on the top of the window, until you want the user can do something, you can show the disappear button on that view or just disappear it immediately.
You can use performSelector:withObject:afterDelay: method instead.
(iOS7, xCode 5.1) I have an app that accesses the Calendar for various purposes, and I'm trying to get all of my error messaging in place.
I have 2 UIAlertviews. Both UIAlertviews show when I need them to, but I only get a call to didDismissWIthButtonIndex for one of them. The alertview called _iCloudAlert is the one that works.
If I show the _iCloudAlert, I get a call to didDismissWIthButtonIndex when a button is clicked, but when I show _deniedAccessAlert I get no call at all. I can't even see the outermost NSLog/s.
I have <UIAlertviewDelegate> in my .h file.
Code that shows the alerts, depending on Calendar access:
// Check the authorization status of our application for Calendar
-(void)checkEventStoreAccessForCalendar
{
NSLog(#"Check Status");
EKAuthorizationStatus status = [EKEventStore authorizationStatusForEntityType:EKEntityTypeEvent];
switch (status)
{
// Update our UI if the user has granted access to their Calendar
case EKAuthorizationStatusAuthorized: [self accessGrantedForCalendar];
NSLog(#"Already granted");
break;
// Prompt the user for access to Calendar if there is no definitive answer
case EKAuthorizationStatusNotDetermined: [self requestCalendarAccess];
break;
// Display a message if the user has denied or restricted access to Calendar
case EKAuthorizationStatusDenied:
case EKAuthorizationStatusRestricted:
{
NSLog(#"already denied");
[self performSelectorOnMainThread:#selector(showDeniedAccessAlert) withObject:nil waitUntilDone:NO];
}
break;
default:
break;
}
}
Both alert view methods:
- (void)informUserAboutCloud {
_iCloudAlert = [[UIAlertView alloc]
initWithTitle: #"Important!"
message: #"If you have an iCloud account.....blah, blah, blah..."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[_iCloudAlert show];
}
- (void)showDeniedAccessAlert {
NSLog(#"Show Denied Access Alert");
_deniedAccessAlert = [[UIAlertView alloc]
initWithTitle: #"Attention!"
message: #"It looks like you've blocked access to Calendar data... Blah, Blah, Blah..."
delegate: nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[_deniedAccessAlert show];
}
And here is the code used to take action on the button clicks:
- (void)alertView:(UIAlertView *)alert didDismissWithButtonIndex:(NSInteger)buttonIndex {
NSLog(#"button index: %i", buttonIndex); //only logs when _iCloudAlert is shown
NSLog(#"alertview: %#", alert); //only logs when _iCloudAlert is shown
if (_iCloudAlert) {
[self checkEventStoreAccessForCalendar];
NSLog(#"check for calendar access from dismissed icloud alert...");
}
if (_deniedAccessAlert) {
NSLog(#"dismissed denied access..."); //never logged
}
}
The 2nd UIAlertView has its delegate set to nil. Change that, and it will work!