Pass data to root controller - ios

I am developing a contacts app. I want to add a contact, by opening a new view like this:
RootViewController.m...
it has a NSMutableArray called contacts
- (IBAction)addContact:(id)sender {
AddContViewController *cont = [[AddContViewController alloc]init];
[self.navigationController presentViewController:cont animated:YES completion:nil];
}
And then come back and add the contact to the array of the root view controller:
AddContViewController.m
- (IBAction)acceptAction:(id)sender {
if ([[firstName text] length] < 1 && [[lastName text] length] < 1)
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Oh no!" message:#"Invalid contact information!" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alert show];
}
else{
// create the contact and put it in the root view controller's array
Contact *cont = [[Contact alloc]initWithFirstName:[firstName text] lastName:[lastName text] andDOB:[dobPicker date]];
// and now I don't know what to do....
[self dismissViewControllerAnimated:YES completion:^{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Success!" message:#"Contact added successfully!" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alert show];
}];
}
}

You should use a delegate to communicate the new Contact object back to your RootViewController.
Define the protocol
#protocol AddContDelegate
-(void)didAddnewContact:(Contact *)contact;
#end
On Your AddContViewController have a delegate property :
#property (nonatomic, assign) id<AddContDelegate> delegate;
In your addContact: method assign the delegate :
- (IBAction)addContact:(id)sender {
AddContViewController *cont = [[AddContViewController alloc]init];
cont.delegate = self;
[self.navigationController presentViewController:cont animated:YES completion:nil];
}
Implement the delegate method in RootViewController :
-(void)didAddnewContact:(Contact *)contact {
[contacts addObject:contact];
}
Call the delegate from AddContViewController :
- (IBAction)acceptAction:(id)sender {
if ([[firstName text] length] < 1 && [[lastName text] length] < 1)
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Oh no!" message:#"Invalid contact information!" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alert show];
}
else{
Contact *cont = [[Contact alloc]initWithFirstName:[firstName text] lastName:[lastName text] andDOB:[dobPicker date]];
if([self.delegate respondsToSelector:#selector(didAddnewContact:)]) {
[self.delegate didAddnewContact:cont];
}
[self dismissViewControllerAnimated:YES completion:^{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Success!" message:#"Contact added successfully!" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alert show];
}];
}
}

There are several ways to pass the data back. I'd suggest setting up a delegate method.
Add this to the top of your AddContViewController.h after any imports:
#class addContViewController
#protocol addContViewControllerDelegate <NSObject>
-(void)addContViewController:(addContViewController *)controller didAddContact:(Contact *)contact;
#end
And after the interface section add
#property (nonatomic, weak) id <addContViewControllerDelegate> delegate;
Then in your RootViewController.h add the protocol to the interface line <addContViewControllerDelegate>
Now in your RootViewController.m method addContact just before you push the new view, add:
cont.delegate = self;
Now in your AddContViewController.m instead of dismissing the view, call:
[self.delegate addContViewController:self didAddContact:cont];
This will call a new method in your RootViewController which it'll pass the Contact and in here you can do with it want you want, but first dismiss the view:
-(void)addContViewController:(addContViewController *)controller didAddContact:(Contact *)contact {
self dismissViewControllerAnimated:YES;
}

You may want to use delegate for this action so that you can communicate to the rootviewcontroller to have contact object.
The same can be achieved using the stack of navigationViewController using [navigationViewController viewControllers] and of the last object is of type of your class you can perform certain selector of your root and dismiss your AddContViewController with a success message.
Hope this will help!!

Related

UIAlertController reuse more times with NSObject

In my app, UIAlertController uses much time.So, I create NSObject Helper.
Here is Helper.h
#import <Foundation/Foundation.h>
#import "PrefixHeader.pch"
#import "LectureViewController.h"
#interface Helper : NSObject
+(void) showNotice:(NSString *) message;
+(void) showNoticeWithAction;
#end
Here is Helper.m
#import "Helper.h"
#import "AppDelegate.h"
#implementation Helper
+(void) showNotice:(NSString *) message{
NSArray *versionArray = [[[UIDevice currentDevice] systemVersion] componentsSeparatedByString:#"."];
if ([[versionArray objectAtIndex:0] intValue] >= 9) {
//ver.IOS >= 9
UIAlertController* alert = [UIAlertController alertControllerWithTitle:nil
message:message
preferredStyle:UIAlertControllerStyleAlert];
alert.view.backgroundColor = [UIColor grayColor];
// [self presentViewController:alert animated:YES completion:nil];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[alert dismissViewControllerAnimated:YES completion:^{
}];
});
} else {
//ver.IOS < 9
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:message delegate:nil cancelButtonTitle:nil otherButtonTitles:nil, nil];
[alert performSelector:#selector(show) withObject:nil afterDelay:5];
}
}
#end
This line cant run in Helper: [self presentViewController:alert animated:YES completion:nil]; because of presentViewController. Please help me
You are getting this error because the base class is NSObject and self represent the object of that class and NSObject does'nt have any method named presentViewController.
To resolve it you need to pass an object of UIViewController in that method and on that object use presentViewController like this
+(void) showNotice:(NSString *) message andViewController : (UIViewController *) controller;
And use it like this
[controller presentViewController:alert animated:YES completion:nil];
the batter way create c function in #end of the class nsobject
void showAlertView( NSString *title,NSString *message) {
[[[UIAlertController alloc]initWithTitle:title message:message delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil] show];
}
and call in any class like this
showAlertView(#"Done", [NSString stringWithFormat:#"%#",responseObject[#"message"]]);
Try this while your controller is pushed and presented .
UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
[[vc presentedViewController] ? vc.presentedViewController : vc presentViewController:alert animated:YES completion:nil];

UIAlertView button action not working

I have one alert view and when I click on yes button it is supposed to produce another alert view and a toast message,but it is not happening. I couldn't figure it out. Here is my code:
-(void)myMethod {
UIAlertView *saveAlert = [[UIAlertView alloc] initWithTitle:#"First Message"
message:#"My First message"
delegate:nil
cancelButtonTitle:#"No"
otherButtonTitles:#"Yes", nil];
saveAlert.tag=0;
[saveAlert performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:NO];
}
This is the method I am using to provide the functionality for different alert views.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if(alertView.tag==0) {
if (buttonIndex == 0)
{
//Code for Cancel button
}
if (buttonIndex == 1)
{
//code for yes button
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];
hud.mode = MBProgressHUDModeText;
hud.labelText = #"Successfully displayed First Message";
hud.margin = 10.f;
hud.yOffset = 150.f;
hud.removeFromSuperViewOnHide = YES;
[hud hide:YES afterDelay:3];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Second Message"
message:#"My second message"
delegate:nil
cancelButtonTitle:#"No"
otherButtonTitles:#"Yes",nil];
alert.tag=1;
[alert performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:YES];
}
}
if (alertView.tag==1) {
if (buttonIndex == 0)
{
//Code for Cancel button
}
if (buttonIndex == 1)
{
//Code for yes Button
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];
hud.mode = MBProgressHUDModeText;
hud.labelText = #"Succesfully displayed Second Message";
hud.margin = 10.f;
hud.yOffset = 150.f;
hud.removeFromSuperViewOnHide = YES;
[hud hide:YES afterDelay:3];
}
}
}
Can anyone help in finding the issue. Why I cannot get my second alert after clicking yes button in first alert?
You have not set the delegate for your UIAlertView and also make sure your delegate conforms to UIAlertViewDelegate protocol. Find the code snippet below.
You controller conforms to UIAlertViewDelegate protocol:
#interface YourViewController : UIViewController <UIAlertViewDelegate>
Create UIAlertView and set the deleagte:
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"First Message"
message:#"Show second message"
delegate:self
cancelButtonTitle:#"No"
otherButtonTitles:#"Yes", nil];
[alertView show];
Implement UIAlertViewDelegate delegate method:
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if( 0 == buttonIndex ){ //cancel button
[alertView dismissWithClickedButtonIndex:buttonIndex animated:YES];
} else if ( 1 == buttonIndex ){
[alertView dismissWithClickedButtonIndex:buttonIndex animated:YES];
UIAlertView * secondAlertView = [[UIAlertView alloc] initWithTitle:#"Second Message"
message:#"Displaying second message"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[secondAlertView show];
}
}
You are specifying nil as the delegate for your alert views. You need to specify an object so the alertView:clickedButtonAtIndex: method can be called!
If you'd like to handle this more clear, you could use a block-based AlertView.
Create new file->Subclass of->UIAlertView
SuperAlertView.h
#import <UIKit/UIKit.h>
#class MySuperAlertView;
typedef void (^MySuperAlertViewBlock) (MySuperAlertView *alertView);
#interface MySuperAlertView : UIAlertView
- (instancetype) initWithTitle:(NSString *)title message:(NSString *)message;
- (void) addButtonWithTitle:(NSString *)buttonTitle block:(MySuperAlertViewBlock) block;
#end
SuperAlertView.m
#import "MySuperAlertView.h"
#interface MySuperAlertView()<UIAlertViewDelegate>
#property NSMutableArray *blocks;
#end
#implementation MySuperAlertView
- (instancetype)initWithTitle:(NSString *)title message:(NSString *)message
{
if (self = [super initWithTitle:title message:message delegate:self cancelButtonTitle:nil otherButtonTitles:nil])
{
self.blocks = [NSMutableArray array];
}
return self;
}
- (void)addButtonWithTitle:(NSString *)buttonTitle block:(MySuperAlertViewBlock)block
{
[self addButtonWithTitle:buttonTitle];
[self.blocks addObject:block ? [block copy] : [NSNull null]];
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
MySuperAlertViewBlock block = self.blocks[buttonIndex];
if ((id) block != [NSNull null]){
block(self);
}
}
#end
Usage:
MySuperAlertView *alertView = [[MySuperAlertView alloc] initWithTitle:#"Info" message:NSLocalizedString(#"EMAIL_SENT_SUCCESSFULL", nil)];
[alertView addButtonWithTitle:#"Ok" block:^(MySupertAlertView *alertView) {
// handle result from ok button here
}];
[alertView addButtonWithTitle:#"cancel" block:NULL];
dispatch_async(dispatch_get_main_queue(), ^{
[alertView show];
});

I have seven methods in which I am calling alert function, is it possible i do call only one time alert function, so to optimize my code

I have six methods, in which I'm calling alert function.
Is it possible to call only the alert function only once, so to optimize my code will be?
This is my code:
// #import "ViewController.h"
//.h file
.m file
#interface ViewController ()
#end
#implementation ViewController
#synthesize usernameText;
#synthesize emailText;
#synthesize passwordText;
#synthesize `repasswordTex`t;
#synthesize postalText;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
//for keyboard hide on textfield return
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {
[theTextField resignFirstResponder];
return YES;
}
//function for Name validation
- (BOOL) validatename:(NSString *) candidate{
[postalText setKeyboardType:UIKeyboardTypeAlphabet];
NSString *nameRegex = #"[A-Za-z]+";
NSPredicate *codeTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#",nameRegex];
return [codeTest evaluateWithObject:candidate];
}
// email validation
- (BOOL) validateEmail: (NSString *) candidate {
NSString *emailRegex = #"[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
NSPredicate *emailTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", emailRegex];
return [emailTest evaluateWithObject:candidate];
}
// password code validation
- (BOOL)paswordvalidation:(NSString *) candidate
{
}
// postal code validation
- (BOOL) validatecode: (NSString *) candidate {
NSString *codeRegex = #"^\d{5}(?:[-\s]\d{4})?$";
NSPredicate *codeTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", codeRegex];
return [codeTest evaluateWithObject:candidate];
}
//method call on submit button
- (IBAction)submitButn:(id)sender{
//user name method call
if(![self validatename:usernameText.text])
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Please Ensure that you have insert character only"delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
return;
}
//Email Method Call
else if(![self validateEmail:emailText.text])
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Please Insert Valid Email Address"delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
return;
}
//Password validate Method Call
else if([passwordText.text length] <6)//&& [repasswordText.text length] <= 6)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Password should contain minimun 6 "delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
return;
}
else if (![passwordText.text isEqualToString:repasswordText.text])
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"confirm password should be same"delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
return;
}
//Postal Code Method Call
else if(!([self validatecode:postalText.text] ||[postalText.text length] <6))
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"postal Code should cotain 5 "delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
return;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Write this method and call where ever you want.
-(void)showAlert :(NSString *)alertTitleString WithAlertMessage :(NSString *)alertMessage{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:alertTitleString alertMessage delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
}
It is possible, add this method name in AppDelegate.h
-(void)alertFuction:(NSString *)title message:(NSString *)message
Then add this function to AppDelegate.m
-(void)alertFuction:(NSString *)title message:(NSString *)message
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:title message:message delegate:self cancelButtonTitle:#"Done" otherButtonTitles:nil, nil];
[alert show];
[alert release];
alert = nil;
[delegate.window setUserInteractionEnabled:YES];
}
Then in any other view controller put this
.h file
#class AppDelegate
.m file
#import AppDelegate
in viewDidload method
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
then you can call the alert method by
[delegate alertFuction:#"title" message:#"message"];
How about a generalised helper class with class methods which cover a number of common scenarios (which you may obviously want to reconfigure / add to):
#implementation AlertHelper
+ (UIAlertView *)showAlertWithMessage:(NSString *)message
{
[AlertHelper showAlertWithTitle:nil message:message delegate:nil cancelButton:NSLocalizedString(#"OK", nil) otherButton:nil];
}
+ (UIAlertView *)showAlertWithTitle:(NSString *)title message:(NSString *)message
{
[AlertHelper showAlertWithTitle:title message:message delegate:nil cancelButton:NSLocalizedString(#"OK", nil) otherButton:nil];
}
+ (UIAlertView *)showAlertWithTitle:(NSString *)title message:(NSString *)message delegate:(id < UIAlertViewDelegate >)delegate cancelButton:(NSString *)cancel otherButton:(NSString *)other
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:delegate cancelButtonTitle:cancel otherButtonTitles:(other ? #[ other ] : nil)];
[alert show];
}
#end
Note that the alert view is returned just in case it is required (say you need to automatically remove the alert view once an operation is complete).
Surely you can, you even should! You just need to refactor it to a separate methods witch will receive a few attributes or (better option) an object with variables from which you could set a required values for the alert you want to show.
- (void)showAlertWithTitle:(NSString *)alertTitle andMessage:(NSString *)alertMessage {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:alertTitle message:alertMessage delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
It could be than a way better but according to your case that will do what you want. It is just extracting a method from your code.

NSOperaionQueue and UIAlertView

The problem is that if I create and display two alert - the second will override the first, and after it closed displayed first. So not pretty.
I'm trying to create a queue alerts with NSOperationQueue. That you could add a few alerts and they show a sequence to close. But I can not do so would be that I add operations are performed sequentially, waiting for the previous one. They are executed in parallel.
AlertOperation.h
#import <Foundation/Foundation.h>
#interface AlertOperation : NSOperation<UIAlertViewDelegate>
#property (nonatomic,assign) BOOL isFinishedAlert;
- (AlertOperation *)initWithAlert:(UIAlertView *)alert;
#end
AlertOperation.m
#import "AlertOperation.h"
#interface AlertOperation()
{
UIAlertView *_alert;
}
#end
#implementation AlertOperation
#synthesize isFinishedAlert = _isFinishedAlert;
- (AlertOperation *)initWithAlert:(UIAlertView *)alert
{
self = [super init];
if (self)
{
_alert = alert;
_alert.delegate = self;
[_alert show];
}
return self;
}
- (void) main
{
_isFinishedAlert = NO;
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
} while (!_isFinishedAlert);
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
_isFinishedAlert = YES;
}
- (BOOL) isConcurrent
{
return NO;
}
#end
Here is run code
UIAlertView *u1 = [[UIAlertView alloc] initWithTitle:#""
message:#"Hello i am first alert" delegate:nil
cancelButtonTitle:#"OK" otherButtonTitles:nil];
UIAlertView *u2 = [[UIAlertView alloc] initWithTitle:#""
message:#"Hello i am second alert" delegate:nil
cancelButtonTitle:#"OK" otherButtonTitles:nil];
NSOperation *alertOp1 = [[AlertOperation alloc] initWithAlert:u1];
NSOperation *alertOp2 = [[AlertOperation alloc] initWithAlert:u2];
alertsQueue = [[NSOperationQueue alloc] init];
[alertsQueue setMaxConcurrentOperationCount:1];
[alertsQueue addOperation:alertOp1];
[alertsQueue addOperation:alertOp2];
Make this easier on yourself. Create a mutable array. When you have new alerts to show then push them onto the array. Every time an alert finishes (gets its delegate message), then dispatch the next alert onto the main queue:
NSMutableArray *alerts;
... end of Alert Delegate message
if([alert count]) {
UIAlert *alrt = [alerts objectAtIndex:0];
[alerts removeObjectAtIndex:0];
dispatch_async(dispatch_get_main_queue(), ^{ [alrt show]; } );
}
I moved the [_alert show] to -(void)main method and it worked! Thank you, #phix23 for help!

UIAlertview exc_bad_access [duplicate]

This question already has answers here:
UIAlertView fails to show and results in “EXC_BAD_ACCESS” error
(6 answers)
Closed 8 years ago.
I add a function to dismiss the UIAlertView after several seconds.The whole code is like:
- (void)netWorkAlert
{
UIAlertView *netWork = [[UIAlertView alloc] initWithTitle:#"error" message:#"network has problems" delegate:self cancelButtonTitle:nil otherButtonTitles: nil];
[netWork show];
[self performSelector:#selector(dismissAlert:) withObject:netWork afterDelay:2];
}
- (void)dismissAlert:(UIAlertView *)alert
{
if(alert)
{
[alert dismissWithClickedButtonIndex:0 animated:YES];
[alert release];
}
}
the netWorkAlert is invoked when the network is unavailable.
Now the problem I met is when the netWorkAlert is invoked at the second time, the app is broken and the Xcode shows error in
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([ZJAppDelegate class]));
//Thread 1 :EXC_BAD_ACCESS(code=1,address=xc0000004)
}
}
I didn;t use ARC and I don't know why it crashes. Even I comment the [alert release];, it still has the same problem at the second time.
Could anyone help me to check it?
thanks!
The EXC_BAD_ACCESS is caused by accessing a released object. To avoid this make your call to UIAlertView kind of modal:
Function body:
-(void)checkSaving
{
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Do you want to add these results to your database?"
message:#"\n\n"
delegate:self
cancelButtonTitle:#"No"
otherButtonTitles:#"Save", nil];
alert.alertViewStyle = UIAlertViewStyleDefault;
[alert show];
//this prevent the ARC to clean up :
NSRunLoop *rl = [NSRunLoop currentRunLoop];
NSDate *d;
d= (NSDate*)[d init];
while ([alert isVisible]) {
[rl runUntilDate:d];
}
}
Your choice result:
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
// the user clicked one of the OK/Cancel buttons
if (buttonIndex == 1)//Save
{
//do something
}
if (buttonIndex == 0)//NO
{
//do something
}
}
Register the functions in the interface declaration:
#interface yourViewController ()
-(void)checkSaving
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
//...
#end
To call:
[self checkSaving];
I wish this will help you.
The UIAlertView could be out of scope by the time the dismissAlert method is called (your checking for alert being nil will prevent this code crashing. There is, however, a better way of implementing this where alert will never be out of scope.
Your class that defines the networkAlert method should implement the <UIAlertViewDelegate> protocol. The code below allows you to intercept the user clicking the 'cancel' button and perform a custom action. The default action of pressing cancel is to close the UIAlertView.
#interface YourClassName : UIViewController <UIAlertViewDelegate> {}
#implementation YourClassName
-(void) networkAlert
{
UIAlertView *netWork = [[UIAlertView alloc] initWithTitle:#"error"
message:#"network has problems"
delegate:self
cancelButtonTitle:#"cancel"
otherButtonTitles:nil];
[netWork show];
}
- (void) alertViewCancel:(UIAlertView*)alertView
{
what ever it is you want to do when the cancel button is pressed here
}

Resources