ViewController to reload tableview data - ios

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

Related

Erratic dismissal of software keyboard

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;
}

Print NSMutableArray into UITableView

In my application I have a tableview with a barbuttonitem. When I press the barbuttonitem, I see an alertcontroller that allows me to enter data. Then later, save these data within a NSMutableArray and my purpose is to print one of these data within the cells of a tableview. The problem is that the data acquisition is correct, but the tableview remains empty. I do not understand why, it seems all right!
Class: FeedInfo
#import <Foundation/Foundation.h>
#interface FeedInfo : NSObject
#property (nonatomic,strong) NSString *feedURL;
#property (nonatomic,strong) NSString *feedTitle;
#property (nonatomic,strong) NSString *feedCategory;
#end
And then I have a barbuttonitem inside a tableview. When I click on this button, I open a controller alert that allows me to enter 3 fields: url, feed, category.
I have declared #property(nonatomic,strong) NSMutableArray *feedArray;
I then insert these dates into the NSMutableArray.
- (IBAction)inserFeed:(UIBarButtonItem *)sender {
UIAlertController * alertController = [UIAlertController alertControllerWithTitle: #"Feed RSS: info?"
message: #""
preferredStyle:UIAlertControllerStyleAlert];
[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.placeholder = #"Feed URL";
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
textField.borderStyle = UITextBorderStyleRoundedRect;
}];
[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.placeholder = #"Feed Title";
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
textField.borderStyle = UITextBorderStyleRoundedRect;
}];
[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.placeholder = #"Feed category";
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
textField.borderStyle = UITextBorderStyleRoundedRect;
}];
[alertController addAction:[UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
NSArray * textfields = alertController.textFields;
UITextField * urlfield = textfields[0];
UITextField * titlefield = textfields[1];
UITextField * categoryfield = textfields[2];
FeedInfo *newFeed = [[FeedInfo alloc] init];
newFeed.feedURL = urlfield.text;
newFeed.feedTitle = titlefield.text;
newFeed.feedCategory = categoryfield.text;
[self.feedArray addObject:newFeed];
[self.tableView reloadData];
}]];
[self presentViewController:alertController animated:YES completion:nil];
}
Everything works here. The problem is printing one of these fields within a cell of my tableview.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"cell"];
FeedInfo *feedToShow = [self.feedArray objectAtIndex:indexPath.row];
cell.textLabel.text = feedToShow.feedTitle;
return cell;
}
Problem with your cell creation. Your cell object is nil
Use the following code to get the cell object.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
dequeueReusableCellWithIdentifier: forIndexPath : newer dequeue method guarantees a cell is returned and resized properly, assuming identifier is registered
If you didn't set the cell identifier on the xib/storyboard then
You must call registerClass or registerNib in order to be able to use the dequeueReusableCellWithIdentifier:forIndexPath: method.
Update :
Datasource array is not allocated. Allocate feedArray inside the viewDidLoad()
- (void)viewDidLoad {
[super viewDidLoad];
self.feedArray = [[NSMutableArray alloc]init];
}

AlertView ClickedButtonAtIndex not working

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);
}
}
}

How to persist selected state of an image in a custom tableview cell?

Ok, I have new dilemma. I have been searching all over S.O. to find the answer, but I cannot seem to get any of the code to work. I have also looked at many tutorials and other resources but can't seem to find anything to help.
Here's the deal: I have a tableview with a custom cell with custom objects. The custom cell contains an image that can be selected to indicate whether the type of transportation listed in the cell has been used or not. The image changes from a grayed "unselected" state to a blue "selected" state (or back from "selected" to "unselected"). A tap gesture on the image fires up an alert view where the user can select "yes" or "no". The alert view then causes the image to change to a "selected" or "unselected" state, essentially working like a checkmark. This all works perfectly. (I added the alert view as a safeguard against an unintentional tap gesture on the image).
http://tinypic.com/r/s5d3s3/8
The problem occurs when the cell disappears during scrolling and then re-appears again, the image becomes deselected. Lots of people have asked about this problem. It looks like I need to:
(1) set up a mutable array to hold the selections
(2) perform some check in cellForRowAtIndexPath to tell the cell what to show
(3) and, somewhere, set the state of the image selected so it can be added to the mutable array.
I've tried implementing ideas from some the given answers but a lot of them use a button or cell accessory. Part of the problem is where to implement some of this code since I've got the gesture recognizer and alert view firing on the image. Also, would saving the selection to nsuserdefaults keep the state of the image during scrolling as well, or would I need to deal with both problems separately? (meaning, add code to keep the state during scrolling as well as code to save to nsuserdefaults). I've tried some S.O. answers to use nususerdefaults, but have the same problem...where do I put this code? How do I save the image state instead of saving a button state or cell accessory state?
I also want to note that the tableview is embedded in a navigation controller and each cell goes to a detail view which adds to the problem when adding code to didSelectCellAtIndexPath (which most of the S.O. answers include). How do I single out the image that is tapped and not the whole cell (which often causes a segue to occur along with the image selection).
Any kind of help would be most appreciated!! Thank you ahead of time. :)
Below is all my code:
**TRANSPORT.h**
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface Transport : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) UIImage *transportImage;
#property (nonatomic, strong) UIImage *usedTransportImage;
#end
**TRANSPORTDATACONTROLLER.h**
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "Transport.h"
#interface TransportDataController : NSObject
#property (nonatomic, strong) NSMutableArray *transportDataArray;
-(NSMutableArray *)populateDataSource;
#end
**TRANSPORTDATACONTROLLER.m**
#import "TransportDataController.h"
#implementation TransportDataController
-(NSMutableArray *)populateDataSource
{
_transportDataArray = [[NSMutableArray alloc] init];
Transport *transportData = [[Transport alloc] init];
transportData.name = #"Bus";
transportData.transportImage = [UIImage imageNamed:#"Bus"];
transportData.usedTransportImage = [UIImage imageNamed:#"stamp-grayed"];
[_transportDataArray addObject:transportData];
transportData = [[Transport alloc] init];
transportData.name = #"Helicopter";
transportData.transportImage = [UIImage imageNamed:#"Helicopter"];
transportData.usedTransportImage = [UIImage imageNamed:#"stamp-grayed"];
[_transportDataArray addObject:transportData];
transportData = [[Transport alloc] init];
transportData.name = #"Truck";
transportData.transportImage = [UIImage imageNamed:#"Truck"];
transportData.usedTransportImage = [UIImage imageNamed:#"stamp-grayed"];
[_transportDataArray addObject:transportData];
transportData = [[Transport alloc] init];
transportData.name = #"Boat";
transportData.transportImage = [UIImage imageNamed:#"Boat"];
transportData.usedTransportImage = [UIImage imageNamed:#"stamp-grayed"];
[_transportDataArray addObject:transportData];
transportData = [[Transport alloc] init];
transportData.name = #"Bicycle";
transportData.transportImage = [UIImage imageNamed:#"Bicycle"];
transportData.usedTransportImage = [UIImage imageNamed:#"stamp-grayed"];
[_transportDataArray addObject:transportData];
transportData = [[Transport alloc] init];
transportData.name = #"Motorcycle";
transportData.transportImage = [UIImage imageNamed:#"Motorcycle"];
transportData.usedTransportImage = [UIImage imageNamed:#"stamp-grayed"];
[_transportDataArray addObject:transportData];
transportData = [[Transport alloc] init];
transportData.name = #"Plane";
transportData.transportImage = [UIImage imageNamed:#"Plane"];
transportData.usedTransportImage = [UIImage imageNamed:#"stamp-grayed"];
[_transportDataArray addObject:transportData];
transportData = [[Transport alloc] init];
transportData.name = #"Train";
transportData.transportImage = [UIImage imageNamed:#"Train"];
transportData.usedTransportImage = [UIImage imageNamed:#"stamp-grayed"];
[_transportDataArray addObject:transportData];
transportData = [[Transport alloc] init];
transportData.name = #"Car";
transportData.transportImage = [UIImage imageNamed:#"Car"];
transportData.usedTransportImage = [UIImage imageNamed:#"stamp-grayed"];
[_transportDataArray addObject:transportData];
transportData = [[Transport alloc] init];
transportData.name = #"Scooter";
transportData.transportImage = [UIImage imageNamed:#"Scooter"];
transportData.usedTransportImage = [UIImage imageNamed:#"stamp-grayed"];
[_transportDataArray addObject:transportData];
transportData = [[Transport alloc] init];
transportData.name = #"Caravan";
transportData.transportImage = [UIImage imageNamed:#"Caravan"];
transportData.usedTransportImage = [UIImage imageNamed:#"stamp-grayed"];
[_transportDataArray addObject:transportData];
return _transportDataArray;
}
#end
**TRANSPORTCELL.h**
#import <UIKit/UIKit.h>
#interface TransportCell : UITableViewCell
#property (nonatomic, weak) IBOutlet UILabel *nameLabel;
#property (nonatomic, weak) IBOutlet UIImageView *transportImageView;
#property (nonatomic, weak) IBOutlet UIImageView *grayedImageView;
#end
**MAINTABLEVIEWCONTROLLER.h**
#import <UIKit/UIKit.h>
#import "Transport.h"
#import "TransportDataController.h"
#import "DetailTableViewController.h"
#interface MainTableViewController : UITableViewController
#property (nonatomic, strong) TransportDataController *transportController;
#property (nonatomic, strong) NSMutableArray *dataSource;
#end
**MAINTABLEVIEWCONTROLLER.m**
#import "MainTableViewController.h"
#import "TransportCell.h"
#interface MainTableViewController ()
#end
#implementation MainTableViewController
-(void)viewDidLoad
{
[super viewDidLoad];
_transportController = [[TransportDataController alloc] init];
self.dataSource = _transportController.populateDataSource;
self.title = #"Transportation Types";
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _dataSource.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"mainCell";
TransportCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[TransportCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
Transport *transportData = [self.dataSource objectAtIndex:indexPath.row];
cell.nameLabel.text = transportData.name;
cell.transportImageView.image = transportData.transportImage;
cell.grayedImageView.image = transportData.usedTransportImage;
UITapGestureRecognizer *grayedImageTouched = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(transportImageTapped:)];
grayedImageTouched.numberOfTapsRequired = 1;
[cell.grayedImageView addGestureRecognizer:grayedImageTouched];
cell.grayedImageView.userInteractionEnabled = YES;
return cell;
}
-(void)transportImageTapped:(UIGestureRecognizer *)gesture
{
UIImageView *selectedImageView = (UIImageView *)[gesture view];
UIImage *grayedImage = [UIImage imageNamed:#"stamp-grayed"];
UIImage *darkImage = [UIImage imageNamed:#"stamp-color"];
UIAlertController *transportAlert = [UIAlertController alertControllerWithTitle:#"Yes, it's true..." message:#"I have used this type of transport before." preferredStyle:UIAlertControllerStyleAlert];
[transportAlert addAction:[UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action){
NSLog(#"cancel");
}]];
[transportAlert addAction:[UIAlertAction actionWithTitle:#"Yes" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){
if (selectedImageView.image == grayedImage)
{
selectedImageView.image = grayedImage;
[selectedImageView setImage:grayedImage];
}
else
{
selectedImageView.image = darkImage;
[selectedImageView setImage:darkImage];
}
if (selectedImageView.image == darkImage)
{
selectedImageView.image = darkImage;
[selectedImageView setImage:darkImage];
}
else
{
selectedImageView.image = grayedImage;
[selectedImageView setImage:grayedImage];
}
NSLog(#"has taken this transport before");
}]];
[transportAlert addAction:[UIAlertAction actionWithTitle:#"No" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){
if (selectedImageView.image == darkImage)
{
selectedImageView.image = grayedImage;
[selectedImageView setImage:grayedImage];
}
else
{
selectedImageView.image = grayedImage;
[selectedImageView setImage:grayedImage];
}
if (selectedImageView.image == grayedImage)
{
selectedImageView.image = grayedImage;
[selectedImageView setImage:grayedImage];
}
NSLog(#"has not taken this transport before");
}]];
[self presentViewController:transportAlert animated:YES completion:nil];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showDetail"])
{
DetailTableViewController *detailController = [segue destinationViewController];
detailController.transport = [self.dataSource objectAtIndex:[self.tableView indexPathForSelectedRow] .row];
}
}
#end
Step away from the details and think about what you're doing.
A table view presents information about an ordered collection of things.
The things are the data model. The table view is the view object, and the view controller is the controller object in the MVC design pattern.
When the user interacts with the view in a way that needs to make a persistent change, the controller should record the changes in the model and tell the view to update it's appearance.
Then, if a cell scrolls off-screen and then back on-screen, the data source should set up the recycled cell with the new state for that entry from the data model.
For a non-sectioned table view it's quite common to store the data model as an NSArray of some sort of data object. You can create a custom data container object or just use a dictionary.
Let's say we have a custom data object.
Simply add a property to your data object that tells whether or not a transportation item has been used or not.
When the user taps on the view object, the view controller should respond to the messages from the gesture recognizer by changing the "has been used" property of the data model object for the specific index path and then tell the view to redraw itself.
In your cellForRowAtIndexPath method, set the view object based on the state of the "has been used" flag. Since you saved the change of state into the data model, next time the user displays a cell for a given index in your table data, it shows the changed state.
As you suspect, you need to change your datasource data upon button click such that cellForRowAtIndexPath can load the desired image appropriately. In this case, you can simply change your TransportCell's usedTransportImage (i.e. the UIImage tied to the `UIGestureRecognizer) to dark or gray depending on the alert view's input.
As your code stands now though, your logic within your alert blocks is flawed to the point that I'm not sure what you're trying to accomplish -- your "Yes" block always leaves your image as is, your "No" block always sets your image to gray, and you're repeatedly setting the image multiple times within each block. I'm guessing that maybe you want a "Yes" response to set the usedTransportImage to dark and a "No" response to set the usedTransportImage to gray (?)... So using that assumption as an example, this is what I recommend:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
// Set the UITapGestureRecognizer's image view's tag to
// indexPath.row
cell.grayedImageView.tag = indexPath.row;
...
return cell;
}
- (void)transportImageTapped:(UIGestureRecognizer *)gesture
{
// Fetch the datasource object associated with the current row
Transport *transportData = [self.dataSource objectAtIndex:gesture.view.tag];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:gesture.view.tag inSection:0];
UIAlertController *transportAlert = [UIAlertController alertControllerWithTitle:#"Yes, it's true..." message:#"I have used this type of transport before." preferredStyle:UIAlertControllerStyleAlert];
[transportAlert addAction:[UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action){
NSLog(#"cancel");
}]];
[transportAlert addAction:[UIAlertAction actionWithTitle:#"Yes" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){
NSLog(#"has taken this transport before");
// Create the image within the block so it's only
// created if needed
UIImage *darkImage = [UIImage imageNamed:#"stamp-color"];
// Set the Transport object's "used" image to the
// darkened image
transportData.usedTransportImage = darkImage;
// Replace the old Transport object associated with the
// current row with the updated Transport object
[self.dataSource replaceObjectAtIndex:gesture.view.tag withObject:transportData];
// Reload the table data at that row so it reflects those changes
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation: UITableViewRowAnimationNone];
}]];
[transportAlert addAction:[UIAlertAction actionWithTitle:#"No" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){
NSLog(#"has not taken this transport before");
// Create the image within the block so it's only
// created if needed
UIImage *grayedImage = [UIImage imageNamed:#"stamp-grayed"];
// Set the Transport object's "used" image to the
// grayed image
transportData.usedTransportImage = grayedImage;
// Replace the old Transport object associated with the
// current row with the updated Transport object
[self.dataSource replaceObjectAtIndex:gesture.view.tag withObject:transportData];
// Reload the table data at that row so it reflects those changes
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation: UITableViewRowAnimationNone];
}]];
[self presentViewController:transportAlert animated:YES completion:nil];
}
And if you want help for the second part of your question about preventing the view's transition upon button press, please post your code from didSelectRowAtIndexPath:.
This is what you need to do as given below. Changed the implementation of transportImageTapped method. Code is not tested so there is a possibility of minor error. Let me know if this helps.
Create a NSMutableArray say NSMutableArray *selected;
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"mainCell";
TransportCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[TransportCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
Transport *transportData = [self.dataSource objectAtIndex:indexPath.row];
cell.nameLabel.text = transportData.name;
cell.transportImageView.image = transportData.transportImage;
if([selected containsObject:indexPath]) {
cell.grayedImageView.image = transportData.usedTransportImage;
} else {
cell.grayedImageView.image = transportData.deSelectedTransportImage;
}
UITapGestureRecognizer *grayedImageTouched = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(transportImageTapped:)];
grayedImageTouched.numberOfTapsRequired = 1;
[cell.grayedImageView addGestureRecognizer:grayedImageTouched];
cell.grayedImageView.userInteractionEnabled = YES;
return cell;
}
-(void)transportImageTapped:(UIGestureRecognizer *)gesture
{
CGPoint point= [gesture locationInView:self.tableView];
NSIndexPath *theIndexPath = [theTableView indexPathForRowAtPoint:point];
UIAlertController *transportAlert = [UIAlertController alertControllerWithTitle:#"Yes, it's true..." message:#"I have used this type of transport before." preferredStyle:UIAlertControllerStyleAlert];
[transportAlert addAction:[UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action){
NSLog(#"cancel");
}]];
[transportAlert addAction:[UIAlertAction actionWithTitle:#"Yes" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){
if(selected containsObject:theIndexPath ) {
//Already selected - Remove the selection
[selected removeObject:theIndexPath];
} else {
// Set the object as selected
[selected addObjetc:theIndexPath];
}
NSLog(#"has taken this transport before");
NSArray* indexArray = [NSArray arrayWithObjects:theIndexPath, nil];
// Launch reload for the two index path
[self.tableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationFade];
}]];
[transportAlert addAction:[UIAlertAction actionWithTitle:#"No" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){
if(selected containsObject:theIndexPath ) {
//Already selected - Remove the selection
[selected removeObject:theIndexPath];
} else {
// Set the object as selected
[selected addObjetc:theIndexPath];
}
NSLog(#"has not taken this transport before");
NSArray* indexArray = [NSArray arrayWithObjects:theIndexPath, nil];
// Launch reload for the two index path
[self.tableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationFade];
}]];
[self presentViewController:transportAlert animated:YES completion:nil];
}

-[UIImage length]: unrecognized selector sent to instance error with a NSMutableArray with Images

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.

Resources