I have a view containing a UIWebView which is loading a google map (so lots of javascript etc). The problem I have is that if the user hits the 'back' button on the nav bar before the web view has finished loading, it is not clear to me how to tidily tell the web view to stop loading and then release it, without getting messages sent to the deallocated instance. I'm also not sure that a web view likes its container view disappearing before it's done (but I've no choice if the user hits the back button before it's loaded).
In my viewWillDisappear handler I have this
map.delegate=nil;
[self.map stopLoading];
this seems to handle most cases OK, as nil'ing the delegate stops it sending the didFailLoadWithError to my view controller. However if I release the web view in my view's dealloc method, sometimes (intermittently) I will still get a message sent to the deallocated instance, which seems to be related to the javascript running in the actual page, e.g.:
-[UIWebView webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:]: message sent to deallocated instance 0x4469ee0
If I simply don't release the webview, then I don't get these messages though I guess I'm then leaking the webview.
If I don't send the 'stopLoading' message, and simply release the webview within viewWillDisappear, then I see messages like this:
/SourceCache/WebCore/WebCore-351.9.42/wak/WKWindow.c:250 WKWindowIsSuspendedWindow: NULL window.
Possibly related, I sometimes (again totally intermittent) get an ugly heisenbug where clicking the back button on some other view's navbar will pop the title, but not the view. In other words I get left with the title of view n on the stack, but the view showing is still view n+1 (the result is you're trapped on this screen and cannot get back to the root view - you can go the other direction, i.e. push more views and pop back to the view that didn't pop corrrectly, just not to the root view. The only way out is to quit the app). At other times the same sequence of pushes and pops on the same views works fine.
This particular one is driving me nuts. I think it may be related to the view disappearing before the web view is loaded, i.e. in this case I suspect it may scribble on memory and confuse the view stack. Or, this could be completely unrelated and a bug somewhere else (i've never been able to reproduce it in debug build mode, it only happens with release build settings when I can't watch it with gdb :-). From my debug runs, I don't think I'm over-releasing anything. And I only seem to be able to trigger it if at some point I have hit the view that has the web view, and it doesn't happen immediately after that.
A variation on this should fix both the leaking and zombie issues:
- (void)loadRequest:(NSURLRequest *)request
{
[self retain];
if ([webView isLoading])
[webView stopLoading];
[webView loadRequest:request];
[self release];
}
- (void)webViewDidStartLoad:(UIWebView *)webView
{
[self retain];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
[self release];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
[self release];
}
- (void)viewWillDisappear
{
if ([webView isLoading])
[webView stopLoading];
}
- (void)dealloc
{
[webView setDelegate:nil];
[webView release];
[super dealloc];
}
There's a few ways to handle it, but this should work. You want the didFailLoadWithError message, it's what tells you it's stopped.
Set a flag isLeaving=YES;
Send the Webview a stopLoading.
In didFailLoadWithError:, check for the error you get when
the webview stops:
if ((thiserror.code == NSURLErrorCancelled) && (isLeaving==YES)) {
[otherClass performSelector:#selector(shootWebview) withObject:nil withDelay:0]
}
release the webView in shootWebview:
variations:
if you want to be cavalier about it, you can do the performSelector:withObject:withDelay: with a delay of [fillintheblank], call it 10-30 seconds without the check and you'll almost certainly get away with it, though I don't recommend it.
You can have the didFailLoadWithError set a flag and clean it up somewhere else.
or my favorite, maybe you don't need to dealloc it all when you leave. Won't you ever display that view container again? why not keep it around reuse it?
Your debug being different from release issue, you might want to check your configuration to make sure that it's exactly the same. Bounty was on the reproducible part of the question, right? ;-).
--
Oh wait a second, you might be taking a whole View container down with the WebView. You can do a variation on the above and wait to release the whole container in shootWebView.
The UINavigationController bug you're describing in the second part of your post might be related to your handling of memory warnings. I've experienced this phenomenon and I"ve been able to reproduce it on view n in the stack by simulating a memory warning while viewing view (n+1) in the stack.
UIWebView is a memory eater, so getting memory warnings wouldn't be surprising when it's used as part of a view hierarchy.
A simple release message in dealloc ought to be enough.
Your second problem sounds like a prematurely deallocated view, but I can't say much without seeing some code.
I had a similar problem to this using a UIWebView in OS3 - this description was a good starting point, however I found than simply nil'ing out the web view delegate before releasing the webView solved my problem.
Reading the sample code (the accepted answer - above) - it seems like a lot of overkill. E.g. [webView release] and webView = nil lines do exactly the same thing given the way the author describes the variable is declared (so you don't need both). I'm also not fully convinced by all the retain and release lines either - but I guess your mileage will vary.
Possibly related, I sometimes (again
totally intermittent) get an ugly
heisenbug where clicking the back
button on some other view's navbar
will pop the title, but not the view.
In other words I get left with the
title of view n on the stack, but the
view showing is still view n+1 (the
result is you're trapped on this
screen and cannot get back to the root
view - you can go the other direction,
i.e. push more views and pop back to
the view that didn't pop corrrectly,
just not to the root view. The only
way out is to quit the app). At other
times the same sequence of pushes and
pops on the same views works fine.
I have the same problem, when I'm use navigation controller with view controllers in stack > 2 and current view controller index > 2, if an memoryWarning occurs in this momens, it raises the same problems.
There is inly 1 solution, which I found after many experiments with overriding pop and push methods in NavigationController, with the stack of view controllers, with views and superviews for stacked ViewControllers, etc.
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#interface FixedNavigationController :
UINavigationController <UINavigationControllerDelegate>{
}
#end
#import "FixedNavigationController.h"
static BOOL bugDetected = NO;
#implementation FixedNavigationController
- (void)viewDidLoad{
[self setDelegate:self];
}
- (void)didReceiveMemoryWarning{
// FIX navigationController & memory warning bug
if([self.viewControllers count] > 2)
bugDetected = YES;
}
- (void)navigationController:(UINavigationController *)navigationController
didShowViewController:(UIViewController *)viewController
animated:(BOOL)animated
{
// FIX navigationController & memory warning bug
if(bugDetected){
bugDetected = NO;
if(viewController == [self.viewControllers objectAtIndex:1]){
[self popToRootViewControllerAnimated:NO];
self.viewControllers = [self.viewControllers arrayByAddingObject:viewController];
}
}
}
#end
It works fine for 3 view controllers in stack.
Related
Random crashes occur in my iphone project, see two call stacks, all in web core, all crash after UIWebView deallocate.
See crash 1, seems web core can't close it completely, is it blocked by other running threads?
See crash 2, why it is still dealing with some issues even if UIWebView has been released?
Any idea or discussion will be appreciated, thanks in advance.
Crash 2 has higher reproducible rate than crash 1, scan WebCore source code, it seems be manifest cache has been deleted (who did it?) when try to access it.
My guess is that you're setting some object (probably your view controller) as the delegate of your webView, either via Interface Builder, or via code that looks something like this:
- (void)viewDidLoad
{
[super viewDidLoad];
self.webView.delegate = self;
}
- (void)dealloc
{
[self.webView release];
[super dealloc];
}
This code has a serious bug that's not immediately obvious if you don't know what to look for. Can you spot it?
When the controller gets dealloc'd it releases the webView, but this doesn't necessarily mean the web view no longer exists. It may still be retained by some code down inside its internal implementation, and it may still be loading content.
When it finishes loading, the web view will call a method like webViewDidFinishLoad: on its delegate. The problem is, your view controller was the delegate, and your view controller no longer exists. The section of memory where your view controller used to live has been reclaimed by the operating system, and is no longer accessible to your application process. The web view doesn't know this though. It still has a reference to that memory address in its delegate property. So it tries to call a delegate method on that object, and whoops... EXC_BAD_ACCESS.
Crashes like this appear to be random since they depend on what your app is doing in the background at the time. This makes them tricky to diagnose. Fortunately they're easy to solve. Simply set the object's delegate to nil before you release it your dealloc statement, like this:
- (void)dealloc
{
self.webView.delegate = nil;
[self.webView release];
[super dealloc];
}
By setting the web view's delegate to nil before you release it, you guarantee that the web view won't try to call delegate methods on your object after it's been deallocated.
If you're using ARC, your class may not have a dealloc method. That's normally fine, but in a case like this you should go ahead and add one. You don't want to call release if you're using ARC, but you should still add a dealloc method and set the delegate property to nil.
To avoid similar crashes in the future, apply this best practice any time you set your object as the delegate of another object.
Before releasing the webview, try to reset the delegate an stop loading.
[webView stopLoading];
webView.delegate = nil;
[webView release];
webView = nil;
My application is loading a first view (used to login into a Web service). When the login is successful, it performs a CATransition (basic kCATransitionFromRight) to show a second view and hides the first view. I've set the delegate of the transition to self so I can use -(void)animationDidStop:(CATransition *)theAnimation finished:(BOOL)flag.
When that method is called (right after the transition is over) I want to release the first view since I won't need it anymore. However, when I call [firstView release] (in animationDidStop:) the retain count doesn't seem to change. I used [loginView retainCount] to check this and since I know it's not always reliable I was wondering: am I doing this right?
Thank you.
taken from the book "Cocoa Touch for iPhone OS 3" is a similar approach.
They set up an animation remove the old subview, add the new one and then commit the animation.
Jilouc in his comment is right, forget to check "retaincount"...
if you want to be sure that your object view firstView just add a
NSLog(#"i'm removing myFirstView");
in its
-(void)dealloc{
}
method...
if you get that NSLog in debugger console window then be sure you had it removed/released in the right way...
btw... the right way could be something like this:
in animationDidStop:
if (firstView!=nil){
[firstView.view removeFromSuperview];
[firstView release];
firstView=nil;
}
I have a function, in UpdateViewController, that is being called by a delegate, MyDownloadController, that will close a modal view (which is the UpdateViewController).
-(void)errorDownloading {
self.downloadController.delegate = nil;
[downloadController release];
[self dismissModalViewControllerAnimated:YES];
}
I've tried doing this with and without messing with the delegate pointer and it still doesn't close the view.
The delegate calls the method like this within MyDownloadController:
-(void)connectionError {
if([delegate respondsToSelector:#selector(errorDownloading)]){
[delegate errorDownloading];
}
}
And this function is called by a different delegate (MyConnectionController).
Is there anything wrong with having this many delegates? And would a pointer error or something with them effect the modalview being able to close? If so, how / why?
I have this structure for the delegations:
UpdateViewController (the actual modal view I am trying to close)
|- MyDownloadController (the controller that abstracts the process being done)
|- MyConnectionController (a helper class I wrote to interact with NSURLConnection)
|- NSURLConnection
What is the best way to diagnose this problem?
If downloadController is the view you want dismissed, I believe you're releasing it too soon.
-(void)errorDownloading {
[self dismissModalViewControllerAnimated:YES];
self.downloadController.delegate = nil;
[downloadController release];
}
Apple documentation says:
dismissModalViewControllerAnimated:
Dismisses the modal view controller that was presented by the receiver.
Mean's you call the the dismissModalViewControllerAnimated: method on the viewController that presented the ModalViewController you want to dismiss. in your case, this is the correct code to use.
-(void)errorDownloading {
self.downloadController.delegate = nil;
[downloadController release];
[self.parentViewController dismissModalViewControllerAnimated:YES];
}
Also answer to your other questions on number of delegates and pointers. Better design usually means you don't have huge strings of delegate objects but there's little reason to say thats wrong, it just gets messy IMHO. Pointers and the such as you described would most likely cause leaks or crashes, the reason it won't close is what I specified above, you weren't calling the method to the proper receiver.
I wrote simple code to test UIImagePickerController:
#implementation ProfileEditViewController
- (void)viewDidLoad {
[super viewDidLoad];
photoTaker_ = [[UIImagePickerController alloc] init];
photoTaker_.delegate = self;
photoTaker_.sourceType = UIImagePickerControllerSourceTypeCamera;
photoTaker_.showsCameraControls = NO;
}
- (void)viewDidAppear: (BOOL)animated {
[self presentModalViewController: photoTaker_ animated: NO];
}
#end
And I'm getting strange warnings like the following:
2010-05-20 17:53:13.838 TestProj[2814:307] Using two-stage rotation animation. To use the smoother single-stage animation, this application must remove two-stage method implementations.
2010-05-20 17:53:13.849 TestProj[2814:307] Using two-stage rotation animation is not supported when rotating more than one view controller or view controllers not the window delegate
Got any idea what this is about? Thanks a lot in advance!
This message will appear if you are presenting the UIImagePickerController within another UIViewController. Because it isn't pushed like a UINavigationController stack, there is confusion at the UIWindow level. I don't know if the warning is a problem, but to eliminate the warning you can do the following:
// self = a UIViewController
//
- (void) showCamera
{
cameraView = [[UIImagePickerController alloc] init];
[[[UIApplication sharedApplication] keyWindow] setRootViewController:cameraView];
[self presentModalViewController:cameraView animated:NO];
}
- (void) removeCamera
{
[[[UIApplication sharedApplication] keyWindow] setRootViewController:self];
[self dismissModalViewControllerAnimated:NO];
[cameraView release];
}
Perhaps you are adding the root UIViewController's view as a subview of the window instead of assigning the view controller to the window's rootController property?
IT ALL FALLS BACK ON THE UI
This warning can be implemented for several different objects: Pickers, keyboard, etc.
I have found that it is related to the UI taking two steps to complete a transition or other animation. Or for any instance where the UI is trying to finish one thing and is being asked to execute another before it has finished. (therefore it covers a wide range of possible triggers)
I have seen the warning appear on 4.0 & 4.2. In my case I was working with rotating the device and catching whether the keyboard was still up-(i.e. text field was still first responder). If so, the keyboard needed to stay up for between the views, but this presented other complications with other views.
Therefore, I implemented a BOOL tracker to keep track if keybaordIsPresent, and if so I was {textfield resignFirstResponder]; when detecting the orientation change and the reset the textfield to becomeFristResponder after the transition that was wrapped in an Animation Block. My BOOL tracker worked better, I still use the NSNotifications for the Keyboard, but there were overlaps of notifications during rotations because the keyboard was being dismissed without requesting such. The BOOL is set to NO on Load, and when the [textfield resignFirstResponder]; is implemented. *not when "-(void)keyboardWillhide is trigger by the NSNotifications, which gives me both working triggers that never conflict. The BOOL is set to YES, only when the user touches textfield which automatically triggers becomeFirstResponder.
I removed the warning by taking the [textfild resignFirstResponder]; out of the
-(void)willAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
//if (keyboardIsPresent == YES) {[self.entryTextField resignFirstResponder];}
}
and placing it back at the top of the code for the:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
if (keyboardIsPresent == YES) {
[self.entryTextField resignFirstResponder];
}
//Determine Which Orientation is Present:
if((fromInterfaceOrientation == UIInterfaceOrientationPortrait) || (fromInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)){
//LANDSCAPE VIEW:
[self configureLandscapeView];
}else {
//PORTRAIT VIEW:
[self configurePortraitView];
}
}
**Even though I had no code inside the -(void)willAnimatFirstHalfOfRotationToInterface:, the warning was still popping up. I think the warning was still popping up because the compiler still has to attempt the method while it is trying to execute the first animation and therefore gets the double animation call or overlap of resources. It doesn't know that there is no executable code with the method until after it runs through it. And by that time it already set aside resource in preparation for handling possible actions within the method.
**To ellimiate the warning I had to remove or nil out the code for the willAnimateFirstHalfOfRotation, so that the compiler does not have to even check to see if there is a possible 2nd animation or action that may need to be executed at the same time.
/*-(void)willAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
//if (keyboardIsPresent == YES) {[self.entryTextField resignFirstResponder];}}*/
After the transition is completed, within the original animation block I check to see if the "keyboardIsPresent" was YES prior to the rotation, and if so, I resign the First Responder once again. I use setAnimationDuration:0.3which comes out pretty clean and not jumpy.
Well, you are presenting UIImagePickerController modally inside viewDidAppear of ProfileEditViewController.
Think about this. That means when ProfileEditViewController view appears, the UIImagePickerController appears, say later you dismiss UIImagePickerController and it goes back to ProfileEditViewController, then viewDidAppear is called again and UIImagePickerController appears, say later you dismiss UIImagePickerController and it goes back to ProfileEditViewController, then viewDidAppear is called again and.... you get the idea.
That warning is rather cryptic though, not sure if that is what it's trying to tell you. I would suggest making a button somewhere on the ProfileEditViewController that calls presentModalViewController and also make sure you have a way to dismiss the UIImagePickerController (I've never used it not sure if it has one automatically).
You may be trying to present two modal view controllers at the same time, and they are fighting for animation resources.
1) There is rarely any UI reason to do this. You could instead just go directly to the second view controller (the image picker); and, after dismissing it, then present the first view or view controller.
2) If you do want two stacked view controllers or a view controller on top of a view, then set a timer in viewDidAppear to present the second view controller after the first one has finished it's animation. (You could display a dummy png image of a blank picker in the first one to prevent too much display flashing until the second view controller goes live.)
EDIT - Added a random code example:
I might try substituting this as an experiment:
- (void)foo {
[self presentModalViewController: photoTaker_ animated: NO];
}
- (void)viewDidAppear: (BOOL)animated {
NSTimer *bar = [ NSTimer scheduledTimerWithTimeInterval: (2.0f)
target: self
selector: #selector(foo)
userInfo: nil
repeats:NO ];
}
A shorter time delay may work as well.
I just had the same problem. In my case was a silly mistake that I'm putting here just in case anyone else falls into that same issue.
In my tabbed app I remove one of the original ViewControllers and added a new one with Storyboard to create a "Settings" section.
This new VC had to be a table view VC and even I designed, compiled and run it without a problem, when I changed the orientation of the app I kept getting this “Using two-stage rotation animation” error.
My problem was that I forgot to change in the original .h file interface "UIViewController" for "UITableViewController".
Once this was done I changed on the Storyboard identity badge the class from the general value to my SettingsViewController and that was the end of it.
I hope it can help someone else. It took me a while to get to bottom of this.
Cheers,
I think the warning here is about Core Animation performance. As a test, I loaded the image picker without any action sheet or other animations going on and the warnings are still there. I think these are warnings coming from the image picker class itself and not from any misuse of the API.
It's one of the simplest things to do, I know. But I've been banging my head against this for days. I've done it plenty of times in the past, but for some reason trying to present a modal view controller just crashes the app to a black screen. Nothing reported in the console or anything. I'm hoping someone might have had this problem and has some advice.
This code is called from a UIViewController class:
MFMailComposeViewController *controller = [[MFMailComposeViewController alloc] init];
controller.mailComposeDelegate = self;
[controller setSubject:#"test subject"];
[controller setMessageBody:#"this is the message body" isHTML:NO];
[self presentModalViewController:controller animated:YES];
As Andrew has pointed out in his comment, do you check
+[MFMailComposeViewController canSendMail]
before trying to push the view controller? The behavior of the MFMailComposeViewController is not well defined if this method returns NO (which may also well be the case when running on the simulator, though I'm not sure). From the documentation:
Before using this class, you must
always check to see if the current
device is configured to send email at
all using the canSendMail method. If
the user’s device is not set up for
the delivery of email, you can notify
the user or simply disable the email
dispatch features in your application.
You should not attempt to use this
interface if the canSendMail method
returns NO.
Have you tried to push another view controller instead? Does this crash your app, too?
Are you showing another modal view controller before trying to show MFMailComposeViewController? I had the same problem and found a workaround:
- (void)peopleMultiPickerNavigationController:(PeopleMultiPickerNavigationController *)peoplePicker
didSelectContacts:(NSArray *)contacts {
[self dismissModalViewControllerAnimated:YES];
// some more code here
[self performSelector:#selector(sendEmail) withObject:nil afterDelay:0.45]; // this works only if delay > ~0.4!
// [self sendEmail]; // this won't work
// some more code here
}
- (void) sendEmail {
Class mailClass = (NSClassFromString(#"MFMailComposeViewController"));
if (mailClass != nil) {
// We must always check whether the current device is configured for sending emails
if ([mailClass canSendMail]) {
[self displayComposerSheet:emails];
} else {
[self launchMailAppOnDevice:emails];
}
} else {
[self launchMailAppOnDevice:emails];
}
}
I know it's an ugly workaround, but I didn't found anything better :(
I don't know how relevant this is, but I have been having horrible problems trying to present the MFMailComposeViewController after returning to my main view controller from ANOTHER modal view controller. It just would not work. I was trying to dismiss this other modal view controller before launching the mail controller, and found my solution to be to not call:
[self dismissModalViewControllerAnimated:YES];
But to instead call:
[self dismissModalViewControllerAnimated:NO];
Then go ahead and present the mail view controller.
That one change made all the difference in my case. I suspect this is connected to the problem sgosha was having. Just switch off the animation, rather than putting a delay in (which is probably just waiting until the animation has completed). Looks like a bug in the framework to me.
I should probably explain further. I have a share button on my main view controller, which modally pops up a table view to allow to user to choose what they want to share. From here, if they tap on Email, they get a UIActionSheet letting them further decide which files they wish to attach to their email.
It is possible the UIActionSheet in the mix is contributing to the problem. The actual dismissal of the modal view controllers is taking place in the delegate methods back in my main view controller, and it is this main view controller which tries to launch the mail view controller after dismissing the modal table view controller.
Yes! I did it! I can not believe, but I solved the problem! That's related to:
Opening an MFMailComposeViewController as modal from (and over) some other opened modal controller
Unfortunately, I have to admit that, again, in times of expensive-like-a-Devil-iPhones5, Apple still forses developers to use old, buggy and not convenient code and components! The great example of that is MFMailComposeViewController.
But now let's follow to more pleasant things.
What do we have:
This problem appeared as on iPhone with iOS 5.1 as on Simulator with iOS 6.
The core problem was when you are trying to open the email controller as a modal one from another modal controller.
[MFMailComposeViewController canSendMail] had absolutely no effect for me - it did not work in both ways (it was crashing with or without email functionality available).
[self dismissModalViewControllerAnimated:NO/YES] did not change general sense - it was crashing in both ways, but with a little different behavior.
I was trying to use a standard approach with mailComposeDelegate (like '.mailComposeDelegate = self').
I was calling the email sending controller from both: common (first-level) controller as well as from modal (second-level) one at the same app.
App was not always crashing - sometimes the controller just could not dismiss itself (buttons 'cancel' and 'send' were active, but no actions processed). Dependenly on conditions (who is parent opened the controller, was animation or not on appearing etc).
There were also no difference if any email recepients were added or not.
So, what my killed 5 working hours discovered is that seems the delegate is "releases" somehow somewhen. I can suppose that if you are opening the email controller as modal over some other modal controller, that (previously modal) controller is being cleaned by garbage collector or some other way and that way the delegate is being cleaned as well (I have no wish to kill several more hours for detailed digging, so I'd leave that to Apple's conscience).
Anyway, in two words, my solution was
to hold the delegate object somewhere with "strong" reference.
in my case, the owner of that delegate was main view controller class (which is always available in my case, as most of the app logic is working with it). That can be AppDelegate instance also.
It would look like:
#interface SharingTools : NSObject <MFMailComposeViewControllerDelegate>
#property UIViewController* currentParentController;
...
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
if (error)
{
...
}
else
{
// I pass it somewhere before calling the email controller dialog opener method,
// because it's different in case we open the email sender
// from first-level controller or second- (the modal one)
[currentParentController dismissViewControllerAnimated:YES completion:^{
if (_onResult) {
((void(^)(bool))_onResult)(result == MFMailComposeResultSent);
}
}];
}
// and of course clearing all the references etc here
currentParentController.mailComposeDelegate = nil;
currentParentController = nil;
}
-(void)sendEmail //... params here
// (possibly, including to store also currentParentController)
{
currentParentController = ... ;
[currentParentController presentViewController:
newlyCreatedEmailController animated:YES completion:nil];
}
#end
#interface MyMainViewController : UIViewController
#property SharingTools* sharing; // initialize somewhere (on viewDidLoad, for instance)
...
-(void)showSettings
{
...
[self presentModalViewController:settingsController animated:YES];
}
#end
#interface SettingsViewController : UIViewController
...
-(void)sendEmailSupport
{
// again, this is up to you where and how to have either reference
// to main controller (or any other owner of the delegate object)
// or sharing directly
[myMainViewControllerInstance.sharing sendEmail: ... parentController:self];
}
#end
Frankly saying, the code is kind of messy, but this is just general thoughts.
I hope, you'll manage your own much nicer.
Good luck and God bless Microsoft! ^^