UIAlertViewDelegate in separate class crashes application - objective-c

I'm having difficulties with UIAlertView delegation in class other than ViewController.
Everything is fine until user clicks the OK button - then app crashes with
Thread 1: EXC_BAD_ACCESS (code=2, address 0x8)
ViewController.h:
#import <UIKit/UIKit.h>
#import "DataModel.h"
#interface ViewController : UIViewController
#end
ViewController.m:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
DataModel *dataModel = [[DataModel alloc] init];
[dataModel ShowMeAlert];
[super viewDidLoad];
}
#end
DataModel.h
#import <Foundation/Foundation.h>
#interface DataModel : NSObject <UIAlertViewDelegate>
- (void)ShowMeAlert;
#end
DataModel.m
#import "DataModel.h"
#implementation DataModel
- (void)ShowMeAlert;
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Info" message:#"View did load!" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
#pragma mark - UIAlertView protocol
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
NSLog(#"Index: %d", buttonIndex);
}
#end
If code for showing alert and it's delegation methods is in ViewController - works perfectly.
When I remove UIAlertDelegation method ...didDismissWithButtonIndex... -
works without delegation.
When I set UIAlertView delegate to nil -
works without delegation.
Any clues what's wrong?

In this method:
- (void)viewDidLoad
{
DataModel *dataModel = [[DataModel alloc] init];
[dataModel ShowMeAlert];
[super viewDidLoad];
}
you are allocating a DataModel local variable which will be deallocated by ARC at the end of the scope. Hence, when dismiss is executed, your delegate is not there anymore. The fix for this is to store your DataModel in a strong property of your view controller. This way it will not be deallocated. The you would do:
- (void)viewDidLoad
{
self.dataModel = [[DataModel alloc] init];
[self.dataModel ShowMeAlert];
[super viewDidLoad];
}

Related

Custom delegate iOS 6 using ARC not working

I am a begginer with Objective-C programming.
I searched here and in other websites to a way to solve my problem but i did not find.
I want to create an login view before the system load. Then, after login i want to dismiss it.
On my project i use ARC and not use Storyboards.
When i debug and look into the function "efetuarLogin" the value of property delegate is 0x000000. I think it is blank, is not?
According to the tutorials and other tips i found on the internet, this is the right way to code delegate. Can you check this for me?
Here is the code:
Login.h
#import <UIKit/UIKit.h>
#class Login;
#protocol LoginDelegate <NSObject>
#required
- (void)loginDidFinish: (Login *)login;
#end
#interface Login : UIViewController{
__weak id<LoginDelegate> delegate;
}
#property (nonatomic, weak) id <LoginDelegate> delegate;
- (IBAction)efetuarLogin:(id)sender;
#end
Login.M
#import "Login.h"
#implementation Login
#synthesize delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)efetuarLogin:(id)sender {
[delegate loginDidFinish:self];
}
#end
MainController.h
#import <UIKit/UIKit.h>
#import "Login.h"
#interface MainController : UITabBarController <LoginDelegate>
#end
MainController.m
#import "MainController.h"
#import "Login.h"
#import "ModalViews.h"
#import "Alerts.h"
#import "ViewPadrao.h"
#import "TableView.h"
#implementation MainController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
ModalViews *modalViews = [[ModalViews alloc] initWithNibName:#"ModalViews" bundle:nil];
Alerts *alerts = [[Alerts alloc] initWithNibName:#"Alerts" bundle:nil];
ViewPadrao *viewPadrao = [[ViewPadrao alloc] initWithNibName:#"ViewPadrao" bundle:nil];
TableView *tableView = [[TableView alloc] initWithNibName:#"TableView" bundle:nil];
self.viewControllers = #[modalViews, alerts, viewPadrao, tableView];
}
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
Login *login = [[Login alloc] initWithNibName:#"Login" bundle:nil];
[login setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self presentViewController:login animated:NO completion:nil];
}
- (void)loginDidFinish: (Login *)login{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Sua mensagem aqui" message:#"Mensagem" delegate:self cancelButtonTitle:#"Fechar" otherButtonTitles:nil];
[alert show];
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
And the method didFinishLaunchingOptions of AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
mainController = [[MainController alloc] init];
window.rootViewController = mainController;
[window makeKeyAndVisible];
return YES;
}
Sorry for the high quantity of code. I am thankful since already!!
I am using the ViewDidAppear method to show Login to users. But when i dismiss and the MainController appears, the method viewdidapper creates the Login again.
How i do to show login once? When i tried to do this on viewdidload it did not work. The login did not appear.
In your MainViewController.m when you create login object
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
Login *login = [[Login alloc] initWithNibName:#"Login" bundle:nil];
login.delegate = self; //add this line
[login setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self presentViewController:login animated:NO completion:nil];
}
EDIT : After seeing OP's edit in the question (which should have been a different thread)
Your view Controller hierarchy is wrong. The ideal flow should be
1) Show Login controller directly from App delegate.
2) After successfull login, show MainViewController from Login Controller.

Delegating UIAlertView to another class / file? Not working?

so I'm new to iOS development and I'm trying to delegate the button click event to another class. Whenever I click a button on the alert, the app crashes and I get an error saying Thread_1 EXC_BAD_ACCESS.
This is my code.
// theDelegateTester.h
#import <UIKit/UIKit.h>
#interface theDelegateTester : UIResponder <UIAlertViewDelegate>
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex;
#end
Implementation..
// theDelegateTester.m
#import "theDelegateTester.h"
#implementation theDelegateTester
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
NSLog(#"Delegated");
}
#end
And here's the implementation for my view file..
#import "appleTutorialViewController.h"
#import "theDelegateTester.h"
#interface appleTutorialViewController ()
- (IBAction)tapReceived:(id)sender;
#end
#implementation appleTutorialViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (IBAction)tapReceived:(id)sender {
theDelegateTester *newTester = [[theDelegateTester alloc] init];
UIAlertView *myAlert = [[UIAlertView alloc] initWithTitle:#"Alert!" message:#"This is a delegated alert" delegate:newTester cancelButtonTitle:#"Close" otherButtonTitles:#"Cool!", nil];
[myAlert show];
}
#end
First of all, you should always start your class names with a capital letter, so you can differentiate between classes and instances or methods easily.
And you probably leak the delegate class. You should declare a strong/retained property TheDelegateTester *myDelegate in your view controller. Then in tapReceived: something like this:
- (IBAction)tapReceived:(id)sender {
if (!self.myDelegate) {
TheDelegateTester *del = [[TheDelegateTester alloc] init];
self.myDelegate = del;
[del release];
}
UIAlertView *myAlert = [[UIAlertView alloc] initWithTitle:#"Alert!" message:#"This is a delegated alert" delegate:newTester cancelButtonTitle:#"Close" otherButtonTitles:#"Cool!", nil];
[myAlert show];
[myAlert release];
}

#autoreleasepool EXC_BAD_ACCESS

I have a simple UIViewControler and when i call the method [self performSelectorInBackground:#selector(load) withObject:nil]; it causes and EXC_BAD_ACCESS
Here is the UIViewControler.m and UIViewControler.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (strong, nonatomic) UITextView *myTextView;
#end
#import "ViewController.h"
#implementation ViewController
#synthesize myTextView;
- (id)init {
self = [super init];
if (self) {
myTextView = [[UITextView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[[self view] addSubview:myTextView];
[self performSelectorInBackground:#selector(load) withObject:nil];
}
return self;
}
- (void) load {
#autoreleasepool {
[myTextView setText:#"LOADING ..."];
//DO SOMETHING ....
}
}
#end
PS.:
The project uses Objective-C ARC
UIKit objects are not thread-safe: you must only access them on the main thread. The line [myTextView setText:#"LOADING ..."]; cannot be safely executed in a background thread.
This may or may not be the reason you are getting an EXC_BAD_ACCESS error but without seeing the rest of the load method I have no way of knowing what else might be wrong.

Delegate Not Working

I'm Trying to dismiss a popover and transfer data at the same time. I implemented a delegate DismissPopoverDelegate but it is failing to work. But there are no errors. If the save button is tapped it registers it and it completes the line after where it calls the delegate. But its not working...
AddEventViewController_iPad.h
#import <UIKit/UIKit.h>
#import <AddressBook/AddressBook.h>
#import <AddressBookUI/AddressBookUI.h>
#import <EventKit/EventKit.h>
#import <EventKitUI/EventKitUI.h>
#import <MessageUI/MessageUI.h>
#import <MessageUI/MFMailComposeViewController.h>
#import <Foundation/Foundation.h>
#import "AboutSme.h"
#import "dateViewPopOverViewController_iPad.h"
#import "addPersonViewControllerPopover_iPad.h"
#import "PreviousEventsTableViewControllerPopover_iPad.h"
#interface AddEventViewController_iPad : UIViewController <UITableViewDelegate, UITableViewDataSource, MFMailComposeViewControllerDelegate, UITextFieldDelegate, UIAlertViewDelegate,UIPopoverControllerDelegate,UINavigationControllerDelegate,UIPopoverControllerDelegate,ABPeoplePickerNavigationControllerDelegate, ABNewPersonViewControllerDelegate, DismissPopoverDelegate> {
UIPopoverController *pop;
AddEventViewController_iPad.m
- (IBAction) selectStartDate:(id) sender {
NSLog(#"Select start date");
dateViewPopOverViewController_iPad *dateViewPopOverViewController = [[dateViewPopOverViewController_iPad alloc] init];
popover2 = [[UIPopoverController alloc] initWithContentViewController:dateViewPopOverViewController];
popover2.delegate = self;
popover2.popoverContentSize = CGSizeMake(320, 460);
CGRect rect = CGRectMake(790, 170, 175, 300);
[popover2 presentPopoverFromRect:rect inView:self.view permittedArrowDirections:UIPopoverArrowDirectionRight animated:YES];
[dateViewPopOverViewController release];
/*
if (dateViewController == nil) {
dateViewController = [[DateViewController_iPad alloc] initWithNibName:#"DateViewController_iPad" bundle:nil];
}
[self presentModalViewController:dateViewController animated:YES];
[dateViewController retain];
*/
}
- (void) dismissWithData:(NSString *)data
{
NSLog(#"%#", data);
[pop dismissPopoverAnimated:YES];
[pop release];
}
dateViewPopOverViewController_iPad.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#protocol DismissPopoverDelegate <NSObject>
#required
- (void) dismissWithData:(NSString *)data;
#end
#interface dateViewPopOverViewController_iPad : UIViewController {
IBOutlet UIDatePicker *datePicker;
IBOutlet UISegmentedControl *segmentedBar;
IBOutlet UILabel *startLabel;
IBOutlet UILabel *endLabel;
IBOutlet UISwitch *allDaySwitch;
NSDate *startDate;
NSDate *endDate;
NSDate *now;
NSDateFormatter *dateFormatter;
id<DismissPopoverDelegate> delegate;
}
#property (retain) id delegate;
- (void) dismissWithData:(NSString *)data;
dateViewPopOverViewController_iPad.m
#implementation dateViewPopOverViewController_iPad
#synthesize startDate, endDate, datePicker, segmentedBar, startLabel, endLabel, now, allDaySwitch, delegate;
- (IBAction) save:(id)sender {
if ([self startDateIsValid] && [self endDateIsValid]) {
//[[self parentViewController] setDatesForEvent:startDate eventEndDate:endDate allDay:[allDaySwitch isOn]];
// [self dismissModalViewControllerAnimated:YES];
NSLog(#"works");
[self.delegate dismissWithData:#"Some text from popover"];
NSLog(#"works1");
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Ooops!" message:#"Please check the dates! Remember the end date must occur after the start date for the event to save." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
}
}
You have a circular reference when including the .h files.
dateViewPopOverViewController_iPad.h includes AddEventViewController_iPad.h and AddEventViewController_iPad.h includes dateViewPopOverViewController_iPad.h, which will cause the compiler to raise an error.
One of the aims to use protocols is to avoid this kind of circular reference. Removing the dateViewPopOverViewController_iPad.h include from your AddEventViewController_iPad.h file might fix the problem
Your call to dismissPopoverAnimated will not trigger the call to the delegate. From Apple's UIPopoverDelegate documentation:
The popover controller does not call this method in response to programmatic calls to the dismissPopoverAnimated method. If you dismiss the popover programmatically, you should perform any cleanup actions immediately after calling the dismissPopoverAnimated method.

What am I doing wrong with my code, when trying to create an email in iOS? (Help me understand protocols/delegates)

I'm trying to take my "myEmail" class and have it be my "all email methods go here" class,
and any other class that will email, will use "myEmail". "myEmail" must include MessageUI framework, and
then instiantiate an email controller to animate on the screen.
The complication is that I don't understand how to use "myEmail", which uses "MFMailComposeViewController",
in my "Documents" view correctly. When I call "sendEmail" in my "Documents" class, it never shows the
email window slide into view.
I understand that I can cut out myEmail as the middle man and use the MessageUI framework methods right
in Documents view, but I don't want to go about it in that way.
If anyone could point out how I'm using protocols/delegates wrong, I'd really appreciate it.
Code in Question
This is my myEmail class
In myEmail.h:
#import <Foundation/Foundation.h>
#import <MessageUI/MessageUI.h>
#protocol myEmailDelegate <MFMailComposeViewControllerDelegate>
#required
-(void)sendEmail;
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error;
#end
#interface myEmail : MFMailComposeViewController {
id <myEmailDelegate> delegate;
}
#property(nonatomic,assign) id<MFMailComposeViewControllerDelegate> myEmailDelegate;
#end
In myEmail.m:
#import "myEmail.h"
#import "ConstructionDocuments.h"
#implementation myEmail
#synthesize myEmailDelegate;
-(void)sendEmail
{
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"mypdfdoc" ofType:#"pdf"];
NSData *myData = [NSData dataWithContentsOfFile:filePath];
MFMailComposeViewController *controller = [[MFMailComposeViewController alloc] init];
controller.mailComposeDelegate = myEmailDelegate;
[controller setSubject:#"Email Example"];
[controller setMessageBody:#"Attached is pdf." isHTML:NO];
[controller addAttachmentData:myData mimeType:#"application/pdf" fileName:filePath];
[self presentModalViewController:controller animated:YES];
[controller release];
}
- (void)addAttachmentData:(NSData*)attachment mimeType:(NSString*)mimeType fileName:(NSString*)filename
{
}
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
[self becomeFirstResponder];
[self dismissModalViewControllerAnimated:YES];
}
- (void) dealloc
{
[myEmailDelegate release];
[super dealloc];
}
#end
This is the View where I'll be using my "myEmail" class
In Documents.h:
#import <UIKit/UIKit.h>
#import "myEmail.h"
#interface Documents : UIViewController <myEmailDelegate> {
}
#property(nonatomic, assign) id<MFMailComposeViewControllerDelegate> myEmailDelegate;
- (IBAction)sendEmail;
#end
#protocol myEmailDelegate <myEmailDelegate>
- (void) sendEmail;
#end
In Documents.m:
- (IBAction)sendEmail
{
myEmail *mymyEmail = [[myEmail alloc] init];
[mymyEmail setmyEmailDelegate: myEmailDelegate];
[myEmailDelegate sendEmail];
}
IIRC you need to call presentModalViewController: on an "active" view controller which is not the case for you. You could do something like this:
-(void)sendEmail:(UIViewController *)externalController
{
// ...
[externalController presentModalViewController:controller animated:YES];
// ...
}
And then call it from your action (which seems to be inside a view controller):
- (IBAction)sendEmail
{
myEmail *mymyEmail = [[myEmail alloc] init];
[mymyEmail setmyEmailDelegate:myEmailDelegate];
[mymyEmail sendEmail:self];
}
BTW, class names start with uppercase letter by convention.