Expected behaviour:
user clicks inside TextField1, the keyboard pops up, user enters a value, NextButton is pressed, keyboard should be dismissed.
Anomaly: the keyboard gets dismissed upon pressing NextButton, however it then pops up again after the alerts that follow are dismissed! Why?
On the other hand, if the alert is never called (//[self showDisclaimer]) the keyboard gets dismissed correctly...
I know that alertView is deprecated, but this is not the source of the error, because I get exactly the same behaviour if I use UIAlertController instead.
Can someone shed some light on this?
- (IBAction) NextButton: (id) sender
{
[self backgroundTouch:id]; //Dismisses the keyboard
[self showDisclaimer];
}
- (void) showDisclaimer {
UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:#"Disclaimer" message: #"bla bla bla"
delegate:self
cancelButtonTitle: nil
otherButtonTitles:#"Don't agree", #"I AGREE", nil];
[alertView show];
}
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"I AGREE"])
{
[self showAlert];
}
else if([title isEqualToString:#"Don't agree"])
{
//Do something else
}
}
- (IBAction) backgroundTouch: (id)sender {
[TextField1 resignFirstResponder];
}
My answer is for you
ViewController
.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController<UITextFieldDelgate>
#property (nonatomic, strong) IBOutlet UITextField *txtFld;
#property (nonatomic, strong) UITextField *currentTxtFld;
- (IBAction)NextButton:(id)sender;
#end
.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize currentTxtFld;
#synthesiz txtFld;
- (void)viewDidLoad {
txtFld.delegate = self;
}
- (IBAction) NextButton: (id) sender
{
[currentTxtFld resignFirstResponder];
[self showDisclaimer];
}
- (void) showDisclaimer
{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"Disclaimer" message:#"bla bla bla" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *agreeBtnAction = [UIAlertAction actionWithTitle:#"I AGREE" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action){
.......//Your code HEre
}];
UIAlertAction *dontagreeBtnAction= [UIAlertAction actionWithTitle:#"Don't agree" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action){
.......//Your code Here
}];
[alert addAction:agreeBtnAction];
[alert addAction:dontagreeBtnAction];
[self presentViewController:alert animated:YES completion:nil];
}
#pragma mark - UITextField Delegate methods
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
currentTxtFld = textFld;
return YES;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return YES;
}
Related
I call a UIAlertController from a ViewController. When I press Ok in the UIAlertController will prompt another OK dialog.
Now the problem is when I clicked on the Ok button from the dialog, I able to exit the UIAlertController but what i want is exit the UIAlertController and refresh the primary ViewController.
Can any one help me out? :(
- (IBAction)btnAddDidPressed:(id)sender {
AddCashValueVC *addCashValueVC = [storyboard instantiateViewControllerWithIdentifier:#"addCashValueVC"];
alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(#"nav_Add_Credit", nil) message:nil preferredStyle:UIAlertControllerStyleAlert];
[alertController setValue:addCashValueVC forKey:#"contentViewController"];
[self presentViewController:alertController animated:YES completion:nil];
}
Above is the Primary ViewController. Show how i call the UIAlertController.
- (IBAction)btnProceedDidPressed:(id)sender {
[self convertCashValue];
[self dismissKeyboard];
}
-convertCashValue:{
self->alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(#"msg_App", nil) message:[result objectForKey:#"msg"] preferredStyle:UIAlertControllerStyleAlert];
self->cashValueVC.update= YES;
UIAlertAction *openAction = [UIAlertAction actionWithTitle:NSLocalizedString(#"btn_Ok", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
CashValueVC *cashVC = [[CashValueVC alloc] initWithNibName:nil bundle:nil];
[self dismissViewControllerAnimated:YES completion:^{
[cashVC viewDidLoad];
[cashVC viewWillAppear:YES];
[cashVC.tableView reloadData];
}];
}];
[self->alertController addAction:openAction];
[self presentViewController:self->alertController animated:YES completion:nil];
}
Above is the another UIAlertController in UIAlertController.
Following code firing one OK dialog and after tapping OK, fire another one and if tapped OK - tableview updates on main thread:
#interface ViewController ()<UITableViewDelegate,UITableViewDataSource>
#property (weak, nonatomic) IBOutlet UITableView *table;
#property (strong, nonatomic) NSArray *array; //table view dataSource
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
// update button tapped
- (IBAction)action:(id)sender {
//changing table data source
_array = #[
#"afasfasf",
#"afasfasf",
#"afasfasf",
#"afasfasf",
#"afasfasf",
#"afasfasf",
#"afasfasf"
];
//alert controllers
UIAlertController *firstAlertController = [UIAlertController alertControllerWithTitle:#"First alert"
message:nil
preferredStyle:UIAlertControllerStyleAlert];
UIAlertController *secondAlertController = [UIAlertController alertControllerWithTitle:#"Second alert"
message:nil
preferredStyle:UIAlertControllerStyleAlert];
//Actions
UIAlertAction *firstControllerOKAction = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
//firing second dialog
[self presentViewController:secondAlertController animated:YES completion:nil];
}];
UIAlertAction *secondControllerOKAction = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
//calling on main thread table view update
dispatch_async(dispatch_get_main_queue(), ^{
[self.table reloadData];
});
}];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:#"cancel"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {}];
//adding actions to first dialog
[firstAlertController addAction:firstControllerOKAction];
[firstAlertController addAction:cancel];
//adding actions to second dialog
[secondAlertController addAction:secondControllerOKAction];
[secondAlertController addAction:cancel];
//firing first dialog
[self presentViewController:firstAlertController animated:YES completion:nil];
}
//delegate and datasource methods
- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
[tableView registerClass:UITableViewCell.class forCellReuseIdentifier:#"23"];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"23"];
cell.textLabel.text = _array[indexPath.row];
return cell;
}
- (NSInteger)tableView:(nonnull UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _array.count;
}
#end
I've read the related posts but must be missing something since my ClickedButtonAtIndex method is not being called after hitting the OK button on my alert. I set a breakpoint within the ClickedButtonAtIndex method, it is never reached and the NSLog is never executed.
Here's the .h code:
#import <UIKit/UIKit.h>
#interface MainPage : UIViewController <UIAlertViewDelegate> {
IBOutlet UILabel *sendtxtlabel;
}
- (IBAction)button:(id)sender;
#end
Here is the .m code here:
#interface MainPage ()
#end
#implementation MainPage
- (void)viewDidLoad {
[super viewDidLoad];
sendtxtlabel.text= [NSString stringWithFormat:#"Number 800-555-1212"];
// Do any additional setup after loading the view.
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Vehicle Owner"
message:#"Enter Phone Number"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"ok", nil];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;
alert.tag =12;
//[alert addButtonWithTitle:#"ok"];
[alert show];
NSLog(#"ok");
}
- (void) alertView: (UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
// Capture the phone number input from the alert pop-up window. UIAlertView Delegate added to allow the OS trigger this method to read the data.
if (alertView.tag == 12) {
if (buttonIndex == 1) {
UITextField *textfield = [alertView textFieldAtIndex:0];
NSLog(#"phonenumber: %#", textfield.text);
}
}
}
I have this code on my textFieldShouldReturn (is not in the same class as the method called):
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
LoginViewController *loginViewController = [[LoginViewController alloc]init];
if (textField.returnKeyType == UIReturnKeyJoin) [loginViewController logIn];
return (textField.returnKeyType == UIReturnKeyDone);
}
Everything works perfect until this point, this is the code of the "login" method:
- (IBAction)logIn{
NSString *username = [[self.usernameLoginField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] lowercaseString];
NSString *password = [[self.passwordLoginField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] lowercaseString];
NSLog(#"Username: %# - Password: %# ",username,password);
//Whatever
}
On my view, I have a button that calls that method, when I use that button the NSLog shows what the UITextField contains in that moment, otherwise, If the method is called from the "Join" (Return key) from de keyboard, the NSLog shows null content on the variables.
What am I missing?.
In my opinion you have 3 possible solutions:
Option 1: The best option IMHO.
You can use UIAlertView with UIAlertViewStyle = UIAlertViewStyleLoginAndPasswordInput
self.alertView = [[UIAlertView alloc] initWithTitle:nil
message:#"LogIn"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Cancel", #"Join", nil];
self.alertView.alertViewStyle = UIAlertViewStyleLoginAndPasswordInput;
[self.alertView show];
Then with the delegate:
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (alertView == self.alertView)
{
if (buttonIndex == 1)
{
[self logIn:[[alertView textFieldAtIndex:0] text] andPassword:[[alertView textFieldAtIndex:1] text]];
}
else if (buttonIndex == 0)
{
[self.navigationController popViewControllerAnimated:YES];
}
}
}
Remember to add: UIAlertViewDelegate
Option 2:
Working with [self performSegueWithIdentifier:#"identifier" sender:self] and
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
#pragma mark - UITextFieldDelegate
- (BOOL) textFieldShouldReturn:(UITextField *)textField
{
if (textField == self.usernameLoginField)
{
[self.usernameLoginField resignFirstResponder];
[self.passwordLoginField becomeFirstResponder];
}
else if (textField == self.passwordLoginField)
{
[self.passwordLoginField resignFirstResponder];
//This Identifier is in the Storyboard
[self performSegueWithIdentifier:#"option2Segue" sender:self];
}
return true;
}
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
[(YourViewController *)segue.destinationViewController setUsername:self.usernameLoginField.text];
[(YourViewController *)segue.destinationViewController setPassword:self.passwordLoginField.text];
}
In YourViewController.h you need:
#property (nonatomic, strong) NSString* username;
#property (nonatomic, strong) NSString* password;
Remember to add: UITextFieldDelegate
Option 3
To send back information, you should use Delegation:
In Your firstViewController.h:
#import "DelegateViewController.h"
#interface FirstViewController : UIViewController
(void) logIn: (NSString *) username password: (NSString *) password;
In Your firstViewController.m:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"option3Delegate"])
{
((DelegateViewController *) segue.destinationViewController).delegate = self;
}
}
In Your delegateViewController.h:
#protocol Option3Delegate <NSObject>
- (void) logIn: (NSString *) username password: (NSString *) password;
#end
#interface DelegateViewController : UIViewController
{
id myDelegate;
}
#property (nonatomic, assign) id<Option3Delegate> delegate;
#end
In Your delegateViewController.m:
#pragma mark - UITextFieldDelegate
- (BOOL) textFieldShouldReturn:(UITextField *)textField
{
if (textField == self.usernameLoginField)
{
[self.usernameLoginField resignFirstResponder];
[self.passwordLoginField becomeFirstResponder];
}
else if (textField == self.passwordLoginField)
{
[self.passwordLoginField resignFirstResponder];
//This Identifier is in the Storyboard
if ([_delegate respondsToSelector:#selector(logIn:password:)])
{
[_delegate logIn:self.usernameLoginField.text password:self.passwordLoginField.text];
}
[self.navigationController popViewControllerAnimated:YES];
}
return true;
}
Remember to add: UITextFieldDelegate
Option 4: With KeyboardController
KeyboardController is a small solution to handle the Keyboard interaction inside UITextFields. However only handle with Next and Done Return keys. To handle the Join key you can do it in your ViewController. In the view controller where you allocate KeyboardController:
#property (strong, nonatomic) IBOutlet UITextField *usernameLoginField;
#property (strong, nonatomic) IBOutlet UITextField *passwordLoginField;
#property (strong, nonatomic) KeyboardController *keyboardController;
- (void)viewDidLoad
{
[super viewDidLoad];
id fields = #[self.usernameLoginField, self.passwordLoginField];
self.keyboardController = [KeyboardController controllerWithFields:fields];
//Important
self.passwordLoginField.delegate = self;
}
#pragma mark - UITextFieldDelegate
- (BOOL) textFieldShouldReturn:(UITextField *)textField
{
if (textField == self.passwordLoginField)
{
[self.passwordLoginField resignFirstResponder];
[self logIn];
}
return true;
}
- (IBAction)logIn
{
NSLog(#"Username: %# - Password: %#", self.usernameLoginField.text, self.passwordLoginField.text);
//Whatever
}
You can download an example with the 4 options here:
I have two views - one is viewcontroller and another is enterdetails .
My question is my view- enterdetails is loading in the same view(viewcontroller) when I clicked alertview button it is not navigating to enterdetails
#import "enterDetails.h"
#interface enterDetails ()
#end
#implementation enterDetails
enterdetails.m
- (void)viewDidLoad
{
[super viewDidLoad];
UIImageView *tab_image=[[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 100, 100)];
tab_image.image=[UIImage imageNamed:#"bar.jpeg"];
[self.view addSubview:tab_image];
// Do any additional setup after loading the view from its nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
viewcontroller.m (alertview)
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 0) {
enterDetails *det=[[enterDetails alloc]init];
[self presentViewController:det animated:NO completion:nil];
}
}
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"" message:#" Already Sucessfully Register"delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:#"enter", nil];
[alert show];
[alert release];
I have a storyboard app which has a UIViewController and a UICollectionViewController. In the view controller, the user chooses multiple photos from the iPhone's photo library (Since there is no API for multi-select in iOS, I used ELCImagePickerController to achieve this). And it segues to the collection view controller where the selected photos should be shown in little image views.
The image library shows up and I am able to select multiple photos. But when it segues to the collection view controller, it throws the -[UIImage length]: unrecognized selector sent to instance error in the collection view's cellForItemAtIndexPath event.
Below is the code I have so far.
ViewController.h
#import <UIKit/UIKit.h>
#import "ELCImagePickerController.h"
#import "ELCAlbumPickerController.h"
#import "ELCAssetTablePicker.h"
#import "GalleryViewController.h"
#interface ViewController : UIViewController
#property (strong, nonatomic) NSMutableArray *cameraImages;
- (IBAction)chooseImages:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (IBAction)chooseImages:(id)sender
{
UIActionSheet *photoSourcePicker = [[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:#"Take Photo", #"Choose from Library", nil, nil];
[photoSourcePicker showInView:self.view];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
switch (buttonIndex) {
case 0:
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
ELCAlbumPickerController *albumController = [[ELCAlbumPickerController alloc] initWithNibName:nil bundle:nil];
ELCImagePickerController *elcPicker = [[ELCImagePickerController alloc] initWithRootViewController:albumController];
albumController.parent = elcPicker;
elcPicker.delegate = self;
if ([self.view respondsToSelector:#selector(presentViewController:animated:completion:)]){
[self presentViewController:elcPicker animated:YES completion:nil];
} else {
[self presentViewController:elcPicker animated:YES completion:nil];
}
}
else {
UIAlertView *alert;
alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"This device doesn't have a camera"
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil, nil];
[alert show];
}
break;
case 1:
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
ELCAlbumPickerController *albumController = [[ELCAlbumPickerController alloc] initWithNibName:nil bundle:nil];
ELCImagePickerController *elcPicker = [[ELCImagePickerController alloc] initWithRootViewController:albumController];
albumController.parent = elcPicker;
elcPicker.delegate = self;
if ([self.view respondsToSelector:#selector(presentViewController:animated:completion:)]){
[self presentViewController:elcPicker animated:YES completion:nil];
} else {
[self presentViewController:elcPicker animated:YES completion:nil];
}
}
else {
UIAlertView *alert;
alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"This device doesn't support photo libraries"
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil, nil];
[alert show];
}
break;
}
}
- (void)elcImagePickerController:(ELCImagePickerController *)picker didFinishPickingMediaWithInfo:(NSArray *)info
{
[self dismissViewControllerAnimated:YES completion:nil];
self.cameraImages = [[NSMutableArray alloc] initWithCapacity:info.count];
for (NSDictionary *camImage in info) {
UIImage *image = [camImage objectForKey:UIImagePickerControllerOriginalImage];
[self.cameraImages addObject:image];
}
/*
for (UIImage *image in info) {
[self.attachImages addObject:image];
}
*/
NSLog(#"number of images = %d", self.cameraImages.count);
if (self.cameraImages.count > 0) {
[self performSegueWithIdentifier:#"toGallery" sender:nil];
}
}
- (void)elcImagePickerControllerDidCancel:(ELCImagePickerController *)picker
{
if ([self respondsToSelector:#selector(dismissViewControllerAnimated:completion:)]) {
[self dismissViewControllerAnimated:YES completion:nil];
} else {
[self dismissViewControllerAnimated:YES completion:nil];
}
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"toGallery"]) {
GalleryViewController *galleryVC = [segue destinationViewController];
galleryVC.selectedImages = self.cameraImages;
}
}
#end
GalleryViewController.h
#import <UIKit/UIKit.h>
#import "ELCImagePickerController.h"
#import "ELCAlbumPickerController.h"
#import "ELCAssetTablePicker.h"
#import "ImageCell.h"
#interface GalleryViewController : UICollectionViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate, UICollectionViewDelegate, UICollectionViewDataSource>
#property (strong, nonatomic) NSMutableArray *selectedImages;
#end
GalleryViewController.m
#import "GalleryViewController.h"
#interface GalleryViewController ()
#end
#implementation GalleryViewController
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return self.selectedImages.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
ImageCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"imgCell" forIndexPath:indexPath];
UIImage *image;
int row = indexPath.row;
image = [UIImage imageNamed:self.selectedImages[row]]; //This is where it throws the error
cell.imageView.image = image;
return cell;
}
#end
To further demonstrate the issue, I've slapped together a demo project which you can download from here.
I know thus question has been asked many time before here on SO. I tried them all but to no avail prior to posting my question here.
I'd appreciate if someone can tell me how to get rid of this error.
Thank you.
Hey I understood your problem you are already having an array of images, why using imageNamed: constructor again.
image = [UIImage imageNamed:self.selectedImages[row]];
cell.imageView.image = image;
//This throws a exception because, you have UIImage objects in your array and here imageNamed: takes NSString as an argument , so you are trying to pass a UIImage object instead of a NSString object
Directly take out image from array and assign like this:
cell.imageView.image = (UIImage*) [self.selectedImages objectAtIndex:row];
UIImage * is probably not needed.
self.selectedImages[row] should be a NSString. It seems like it is a UIImage instead of an NSString. Its trying to call length method on the UIImage instance.