MFMessageComposeViewController first alloc takes seconds - objective-c

I am trying to show an MFMessageComposeViewController with the following code:
controller = [[MFMessageComposeViewController alloc] init];
if([MFMessageComposeViewController canSendText])
{
controller.body = [NSString stringWithFormat:#"%#%#%#", itemString, amountString, callTimeString];
controller.recipients = [NSArray arrayWithObject:#"12345678"];
controller.messageComposeDelegate = self;
[self presentModalViewController:controller animated:YES];
}
The problem is when I press the button to bring up the MFMessageComposeViewController, the alloc takes a few seconds. I have tried moving the alloc line to my viewDidLoad method, however this just moves the problem and I end up waiting a few seconds for the view to load.
Is there any method to speed up the alloc and showing of my MFMessageComposeViewController or use a delegate method or something?
Thanks.

Well, what you are doing seems about right. You are not doing anything wrong. Check this - Can't set recipients of MFMessageComposeViewController? & the link attached in the answer

I had a similar problem, check out my answer here: I need a callback when MFMessegeComposeViewController finally loads
For me it wasn't the alloc taking too long, it was the presentModalViewController of the MFMessageComposeViewController, which blocks on Apple's code on the UI thread, so no way to background it except for throwing up your own progress view.

Related

UITab freeze in iOS

I am developing an iOS 5.1 application on Xcode 4.2.
I have a uitablcontroller with different tabs. My problem is when a tab is clicked , the application 'freezes' for few seconds and does all the codes it's meant to do, but it does not load the UIAlertView first as it should be.
I have the UIAlertView declared in the viewDidLoad.
Here is a code snippet:
- (void)viewDidLoad
{
NSLog(#"##### VIEW DID LOAD 1 #####");
// Display Alert: Loading
alertView = [[UIAlertView alloc] initWithTitle:#"Loading"
message:#"\n"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:nil];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
spinner.center = CGPointMake(139.5, 75.5); // .5 so it doesn't blur
[alertView addSubview:spinner];
[spinner startAnimating];
[alertView show];
[super viewDidLoad];
NSLog(#"##### VIEW DID LOAD 2 #####");
self.tableView.dataSource = self;
self.tableView.delegate = self;
[self callMainMethod];
}
When the tab is clicked, I can see that the first NSLog's are displayed in the Log, and then the main method is called, but the UIAlertview is not displayed.
When viewDidLoad is running, it may be that the frame of the associated UIView has zero size. I don't know, but the UIAlertView may be trying to present itself in this zero-size frame. Does it make sense if you present the UIAlertView in viewDidAppear?
If [self callMainMethod] is taking lots of compute power, then the display might not be updated until it finishes. You could try moving it to viewDidAppear. You could also try delaying it, so that the main run loop for the UI thread, the thread that the display is updated on and the thread that executesviewDidLoad and all the other view... methods, has time to complete everything and become idle before you start the heavy processing. It's only when the run loop has done all the processing it can that it starts actually to update the display. Like this:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 200000000), dispatch_get_main_queue(), ^{
[self callMainMethod];
}
The documentation for that is at http://developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
If that suspicion is correct, you should then get the UIAlertView popping straight up, only to have everything go dead for a while, while callMainMethod executes.
I wish I knew how to write, "dispatch this block only after you've managed to finish updating the display with everything up to here", but I don't know how to do that. So the dispatch call above should delay the call to callMainMethod by 200ms, which is usually plenty.
If that works, you should probably start another question, something like "How can I stop the display freezing while I execute this method."

MFMailComposeViewController dismisses right away

The situation is the MFMailComposeViewController was going to be presented. I saw it was presented half-way done, but then it got dismissed.
This is the error:
_serviceViewControllerReady:error: Error Domain=_UIViewServiceInterfaceErrorDomain Code=3 "The operation couldn’t be completed. (_UIViewServiceInterfaceErrorDomain error 3.)"
This is my source code to present the MFMailComposeViewController:
-(void) MailExecute {
if ([MFMailComposeViewController canSendMail])
{
MFMailComposeViewController *mailViewController = [[MFMailComposeViewController alloc] init];
mailViewController.mailComposeDelegate = self;
[mailViewController setSubject:NSLocalizedString(#"Check this new look", #"")];
[mailViewController setMessageBody: #"my new look" isHTML:YES];
[self presentModalViewController:mailViewController animated:YES];
[mailViewController release];
}
else
{
UIAlertView *alertInternal = [[UIAlertView alloc]
initWithTitle: NSLocalizedString(#"Notification", #"")
message: NSLocalizedString(#"You have not configured your e-mail client.", #"")
delegate: nil
cancelButtonTitle:NSLocalizedString(#"OK", #"")
otherButtonTitles:nil];
[alertInternal show];
[alertInternal release];
}
}
The weird point is that sometimes it happens, sometimes it doesn't.
Please help me on this! I spend almost 1 working day to resolve this but no succeed.
This problem can occur when displaying a remote view controller -- a view controller run in another process -- as indicated by the UIViewService reference in the error message.
I've had this problem when displaying an SKStoreProductViewController, which is also a remote view controller. I'm not sure what the root cause is; the only thing that seemed to trigger it for me was presenting the view controller repeatedly.
For the SKStoreProductViewController I was able to check for this error in the completion block of the loadProductWithParameters:completionBlock: method. Does the MFMailComposeViewControllerDelegate give you a callback with an error about this? It may be that all you can do is listen for this error and show an error message to the user.
We should both probably file an apple radar about this.
Your code looks correct, and as stated the error message strongly suggests this has something to do with UIView proper (not MFMail specifically). The problem almost surely lies somewhere elsewhere within your code, and might be challenging to troubleshoot.
Some things to look for:
Other animations or view controller transitions/dismissals happening simultaneously or incorrectly (possibly like this)
Release/retain issues, of course
If none of that seems like the fix, try commenting-out everything else happening in the view controller that calls this method and see if it works then.
If you still can't get it working, present the simplest version you can of failing code so we can troubleshoot further. :)
Do you have anything in your viewDidDisappear: or viewWillDisappear methods that would dismiss a view controller?
If not, can you post more of your code for the ViewController that presents the MFMailComposeViewController?
I know this is the late reply, but may be help some other.
Just now face the same problem, By resetting the simulator work fine for me for this same issue. Let me know if this helps.
After I stored the MFMailComposeViewController in a strong property of my class instead of a local variable I could not reproduce the self-dismissing behaviour any more.
The issue for me was an incorrect argument when calling the attachment function. If you are having this issue with an email attachment I suggest following the solution found in this thread, as follows:
NSString *path = [self getDatabaseFilePath];
NSData *myData = [NSData dataWithContentsOfFile:path];
[picker addAttachmentData:myData mimeType:#"application/x-sqlite3" fileName:[path lastPathComponent]];
MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc]init];
if ([MFMailComposeViewController canSendMail])
{
mailComposer.mailComposeDelegate = self;
[mailComposer setToRecipients:[NSArray arrayWithObject:#"support#kapsie.com"] ];
[mailComposer setSubject:#"Kapsie App Contact Support"];
[mailComposer setMessageBody:#"Type Your Feedback message here" isHTML:NO];
[self presentViewController:mailComposer animated:YES completion:nil];
}
Use above code and check it on device.
Use of modelViewController is deprecated in iOS 6 ,
use
[self presentViewController:mailView animated:YES completion:nil];
I face the same problem and the solution was:
I delete the overall application appearence related code like
[[UILabel appearance]setText:#""]
and replace with the code
[[UILabel appearanceWhenContainedIn:[self class], nil] setText:#""];
Now it is working fine so be carefull on overall application appearence changes: it might be navigationbar appearance, so and so

performSelectorInBackground causes random crash when view is dismissing

I'm having some random crashes at this part of my code:
-(void) goBack {
[self performSelectorInBackground:#selector(addActivityIndicator) withObject:nil];
[self.navigationController popViewControllerAnimated:YES];
}
- (void)addActivityIndicator {
#autoreleasepool {
UIActivityIndicatorView *activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
UIBarButtonItem * barButton = [[UIBarButtonItem alloc] initWithCustomView:activityView];
[activityView startAnimating];
self.navigationItem.leftBarButtonItem = barButton;
}
}
When I want to exit the screen where these method exists, the other ViewController have to process some data. To inform the user that processing is occurring I add an activity indicator to the left button in my navigation bar.
The problem is that sometimes I get an exc_bad_access in addActivityIndicator method. The frequency is very random, sometimes the XCode shows the error at the end of #autoreleasepool, sometimes at the line self.navigationItem.leftBarButtonItem = barButton;
I imagine that sometimes my viewController is destroyed but the thread is still running and try to access the navigationItem of a object that don't exists anymore. But I'm not sure if that is the problem and I don't know how to fix it.
I'm using ARC in my project and this problem occurs in all iOS versions that I tested.
Please, anyone can explain me what is happening and how can I fix this?
Thanks.
You should never do UIKit stuff in the background.
By calling [self performSelectorInBackground:#selector(addActivityIndicator) withObject:nil]; you are updating the UI on a background thread. You should only ever update the UI on the main thread.
Edit
Based on your comment you are trying to have the UI update before the view pops. The way to do that would be:
[self addActivityIndicator]
[navigationController performSelector:#selector(popViewControllerAnimated:) withObject:[NSNumber numberWithBool:YES] afterDelay:0];
You could also look into dispatch_after

Image not being set in method

I have a class with a viewDidLoad method and an artworkInfo method as follows:
-(void)viewDidLoad {
mainDelegate = (AppDelegate*)[[UIApplication sharedApplication]delegate];
[super viewDidLoad];
}
-(void)artworkInfo:(NSNumber *)pos{
mainDelegate = (AppDelegate*)[[UIApplication sharedApplication]delegate];
[self.image setImage:(UIImage *)[[mainDelegate.mapAnnotations objectAtIndex:0]image]];
}
the mainDelegate thing is to gain access to the appDelegate where an array is stored, but anyway, with the "[self.image setImage...]" command where it is, the image on the app does not appear, but when I copy that exact line of code into the viewDidLoad method, it shows up like it should. I know that the artworkInfo method is being called because I debugged it and it goes through, so I can't figure out why the command would not be doing anything it's current method while it will in the viewDidLoad...?
Also, here is where the method is called and this new view is loaded from another class:
infoPage *info = [[infoPage alloc] initWithNibName:#"infoPage" bundle:nil];
info.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:info animated:YES];
infoPage *myInfoPage = [[infoPage alloc] init];
[myInfoPage artworkInfo:position];
[info release];
OH, I see the problem. You're instantiating 2 different infoPage classes.
Change this:
infoPage *info = [[infoPage alloc] initWithNibName:#"infoPage" bundle:nil];
info.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:info animated:YES];
infoPage *myInfoPage = [[infoPage alloc] init];
[myInfoPage artworkInfo:position];
[info release];
to this:
infoPage *info = [[infoPage alloc] initWithNibName:#"infoPage" bundle:nil];
info.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:info animated:YES];
[info artworkInfo:position];
[info release];
Ok detailed answer. In order to understand why this image is not displaying properly you have to first look at how Runloops work in Objective C.
While viewDidLoad is the method that is called when a view is loaded and it is technically also called before a view is displayed and it's view objects initialized. Since presentModalViewController is an animation there is actually some threading going on in the works.
viewDidLoad gets called before the animation is created for the presentModalView. This initializes your objects. However, due to some of the inner workings of UI Kit some processes are loaded off into a thread. When they complete they run callback methods on the main UI thread.
Since presentModalViewController is a non-blocking method your artworkInfo method gets added to the mainRunLoop before the initializer form thread adds its callback methods to the main run loop. The best approach would be to have both a UIImage property of your viewController and a UIImageView.
set the value of UIImage by calling artworkInfo BEFORE the presentModalViewController method.
in your ViewDidLoad go ahead and set the value of your UIImageView
[self.imageView setImage:self.image];
Problem solved.
This seems pretty straight forward.
So you initialize your nib and try to call your method artwork before the nib is fully loaded. <-- This is not working for you.
Then you do additional initialization by overrider viewDidLoad per the doco where the nib is loaded <-- This is working for you
So the answer is, when you call setImage before your nib is loaded, then there is nothing to set the image to. When you call setImage in viewDidLoad your nib is loaded and then things should work just fine.
I hope this explains it a bit.

pushViewController resulting in different behaviour when called in different scenarios

I have the following code which works from my 'scorer' class apart from in the following scenario. I push another viewcontroller editscore, in that view do something, return to scorer pop the editscore and then in some circumstances i will end up pushing my legorderviewcontroller as shown below (it calls the same code). Only this time the new navigation bar is written over the top of the 'scorer' navigation bar and legorderviewcontroller doesn't appear. Does anyone know why this happens in this scenario?
legOrderViewController *controller = [[legOrderViewController alloc] initWithStyle:UITableViewStyleGrouped];
controller.leg = self.leg;
controller.delegate = self;
controller.match =self.match;
controller.set = self.set;
controller.managedObjectContext = self.managedObjectContext;
[self.navigationController pushViewController:controller animated:NO];
controller.playerChangeArray = playerOrder;
[controller release];
This might happen if you call pushViewController too fast after performing popviewcontroller WITH an animation. I ran into that issue once, disabling the animation solved it.