I have a custom cell in a table View, see below.
I have two textFields txt1 and txt2 in my custom cell, as seen below
How can i access the values i enter in each of the text fields so that i can add the values in to seperate Arrays...
the "Addset" button will increment the no of sections of the grouped table View there by incrementing the one more set.
my table view code is as follows.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellID= #"catogoryCell";
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:cellID];
if(cell==nil)
{
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:nil options:nil];
for(id currentObject in nibObjects)
{
if([currentObject isKindOfClass: [CustomCell class]])
{
cell = (CustomCell *)currentObject;
}
}
}
//cell.txt1.text = #"0";
return cell;
}
Thanks all..
Cells are reusable, so they need a more persistent way to keep their info.
Method 1:
You could hold some UITextField objects into an array in case you don't want to reuse the textfields as well, and at cellForRowAtIndexPath you'd only need to set the textfields to their cells such as:
cell.txt1 = [textFieldsArray objectAtindex:indexPath.section*2];
cell.txt2 = [textFieldsArray objectAtindex:indexPath.section*2+1]; //txt1 and txt2 properties should have assign
Method 2:
If you want to reuse the textfields as well I suggest using an array with mutable dictionaries, each dictionary holding the 'settings' for a cell. The textfields will be fully managed by the custom cell (e.g: at the UIControlEventValueChanged event update #"txt1" or #"txt2" values from the dictionary attached to the cell).
///somewhere in the initialization (in the class holding the tableview)
contentArray = [[NSMutableArray alloc] init];
///when adding a new cell (e.g: inside the 'Add set' action)
[contentArray addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:#"", #"txt1", #"", #"txt2", nil]];
//add a new cell to the table (the same way you do now when tapping 'Add set')
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
...
[cell attachDictionary:[contentArray objectAtIndex:indexPath.section]];
return cell;
}
///anywhere where you'd like to access the values inserted inside a cell
NSMutableDictionary *cell3Content = [contentArray objectAtIndex:3];
NSString *text1 = [cell3Content valueForKey:#"txt1"];
NSString *text2 = [cell3Content valueForKey:#"txt2"];
///CustomCell.m
-(id)initWithCoder:(NSCoder *)decoder{
self = [super initWithCoder:decoder];
if(!self) return nil;
[txt1 addTarget:self action:#selector(txt1Changed:) forControlEvents:UIControlEventValueChanged];
[txt2 addTarget:self action:#selector(txt2Changed:) forControlEvents:UIControlEventValueChanged];
return self;
}
-(void)attachDictionary:(NSMutableDictionary *)dic{
contentDictionary = dic;
txt1.text = [contentDictionary valueForKey:#"txt1"];
txt2.text = [contentDictionary valueForKey:#"txt2"];
}
-(void)txt1Changed:(UITextField *)sender{
[contentDictionary setValue:txt1.text forKey:#"txt1"];
}
When you make the IBOutlet connections in your UITableViewCell subclass, connect them to properties in the File Owner (the viewController), instead of the view itself. That way you'll be able to access them from your viewController (the UItableViewDataSource)
Related
Create a Table View.
then create a customize table View cell and place a button on cell and Now I try to get Table View index when click on index.
But when I click on button that place on cell It's not giving me the Index of list.
My Table View class name is SubMenuViewController and cell class name is SubMenuCell
and my code of SubMenuViewController is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
ModelLocator *model = [ModelLocator getInstance];
static NSString *simpleTableIdentifier = #"SubMenuCell";
SubMenuCell *cell = (SubMenuCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"SubMenuCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
if (model.foodSubItemList) {
FoodSubItemVO* foodSubItemTemp = [model.foodSubItemList objectAtIndex:indexPath.row];
[cell.lbSubFoodItem setText: foodSubItemTemp.foodSubItemName];
[cell.lbPrice setText: foodSubItemTemp.price];
[cell setIndexPath:indexPath];
}
return cell;
}
and My SubMenuCell code here
- (IBAction)addItemIntoOrder:(id)sender {
NSLog(#"#%",indexPath);
NSLog(#"#%",indexPath.row);
}
indexPath declare in SubMenuCell.h
Your NSLog's are faulty; try this instead (mind that changed % and # order).
- (IBAction)addItemIntoOrder:(id)sender
{
NSLog(#"%#",indexPath);
NSLog(#"%#",indexPath.row);
}
Make sure that the addItemIntoOrder: action is hooked up to your delegate correctly in IB.
I have a view controller with 6 buttons on it. Each of these buttons push a single table view controller which will be propagated with items depending on what value the button had. Lets say the buttons were 'car', 'van' etc. is it possible to remember the value of the button when the table view is pushed so that the twitter search can be based on the value handed over by the button i.e #car? I can do this with 6 different table views as I can just assign a viewDidLoad method to each based on the search but I would rather only do it once and allow the table view to 'fill in' the value on the button automatically. Here is my code:
- (void)fetchTweets
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData* data = [NSData dataWithContentsOfURL:
[NSURL URLWithString: #"https://api.twitter.com/1/statuses/public_timeline.json"]];
NSError* error;
tweets = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&error];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
});
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return tweets.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"TweetCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
NSString *text = [tweet objectForKey:#"text"];
NSString *name = [[tweet objectForKey:#"user"] objectForKey:#"name"];
cell.textLabel.text = text;
cell.detailTextLabel.text = [NSString stringWithFormat:#"by %#", name];
return cell;
}
Easy man. Set a public property on that TableViewController to hold that value:
In the TVC .h file:
#property(nonatomic,strong) NSString *selectedButtonText;
And synthesize it in the TVC .m file
#synthesize selectedButtonText;
If you are using Storyboard, just make sure you have the segue wired up to the ViewController itself and NOT to the buttons and then in each of the buttons IBActions do something like:
[self performSegueWithIdentifier#"mySegueID" sender:sender];
In the prepareForSegueMethod (implement if you haven't already:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"mySegueID"]) {
// Cast the sender as a UIButton to get the text
UIButton *tappedButton = (UIButton *)sender
MyTableViewController *mtvc = segue.destinationViewController;
mtvc.selectedButtonText = tappedButton.titleLabel.text;
}
}
Then do whatever you want to with that value in your TableViewController
* EDIT *
For a custom attribute on an object (like a UIButton). Add a new file to your project (I put them in a group called Custom Subclasses). This file should be of UIButton class. Name it TweetButton.
Then replace what you have in TweetButton.h with this:
import
#interface TweetButton : UIButton
#property(nonatomic, strong) NSString *buttonName;
#end
TweetButton.m should look like:
import "TweetButton.h"
#implementation TweetButton
#synthesize buttonName;
#end
Then just change the parent class of each of those buttons to TweetButton instead of UIButton (this will be done in Interface Builder).
Then in each of the IBActions, cast that button to type of TweetButton and access/set the name property.
After going through all this, another idea would be to just add in a property (NSString) in the ViewController that is calling the segue (the one with the buttons) and set that to whatever you want and then use that to send to the destination VC.
I have an app consisting of a TabBar with a few TabBarControllers. One Controller contains a very simple table, which is supposed to display the contents of a NSMutableDictionary. When you hit the appropriate button, the Dictionary is updated in a separate Controller and the view switches to the UITableViewController, displaying the newly updated table.
I can see the Dictionary being updated. But the TableView never reflects the changes. In fact, it seems to display the changes only the 1st time I enter that screen.
I have tried [self table.reloadData] and while it gets called, the changes aren't reflected to the UITableView.
Does anyone have any suggestions? I am happy to post code, but am unsure what to post.
Update: the table is updated and refreshed properly only the 1st time it is displayed. Subsequent displays simply show the original contents.
Background:
The tableview gets filled from a dictionary: appDelegate.currentFave. The tableview should get refreshed each time the ViewController is invoked by the TabBarController.
- (void)viewWillAppear:(BOOL)animated
{
NSLog(#"in viewWillAppear");
[super viewWillAppear:animated];
[self loadFavesFile];
[self.tableView reloadData];
}
// load the Favorites file from disk
- (void) loadFavesFile
{
// get location of file
NSString *path = [self getFavesFilePath];
// The Favorites .plist data is different from the Affirmations in that it will never be stored in the bundle. Instead,
// if it exists, then use it. If not, no problem.
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
// read Faves file and store it for later use...
NSMutableDictionary *tempDict = [NSMutableDictionary dictionaryWithContentsOfFile:path];
appDelegate.sharedData.dictFaves = tempDict;
// grab the latest quote. Append it to the list of existing favorites
NSString *key = [NSString stringWithFormat:#"%d", appDelegate.sharedData.dictFaves.count + 1];
NSString *newFave = [NSString stringWithFormat:#"%#", appDelegate.currentFave];
[appDelegate.sharedData.dictFaves setObject:newFave forKey:key];
} else {
NSLog(#"Favorites file doesn't exist");
appDelegate.sharedData.dictFaves = nil;
}
}
// this gets invoked the very first call. Only once per running of the App.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// reuse or create the cell
static NSString *cellID = #"cellId";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:cellID];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
// allow longer lines to wrap
cell.textLabel.numberOfLines = 0; // Multiline
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.textLabel.font = [UIFont fontWithName:#"Chalkduster" size:(16)];
cell.textLabel.textColor = [UIColor yellowColor];
// NOTE: for reasons unknown, I cannot set either the cell- or table- background color. So it must be done using the Label.
// set the text for the cell
NSString *row = [NSString stringWithFormat:#"%d", indexPath.row + 1];
cell.textLabel.text = [appDelegate.sharedData.dictFaves objectForKey:row];
return cell;
}
I found the problem. I was not properly initializing and assignng the TableView in my view controller. See below
- (void)viewDidLoad
{
[super viewDidLoad];
tableView = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame] style:UITableViewStylePlain];
tableView.dataSource = self;
tableView.delegate = self;
tableView.backgroundColor=[UIColor blackColor];
self.view = tableView;
}
Assuming the code you have put up is correct, you want to use [self.table reloadData]. You have the . in the wrong place.
I had this same problem yesterday, for me it turned out I had set the wrong file owner in interface builder and hadn't set up the data source and delegates for the table view properly.
Try going into interface builder and right-clicking on the file owner, this should show you if anything isn't connected up properly.
You should make sure that your Interface Builder connections are set up properly, but what this problem really sounds like is that you have your UITableViewCell setup code in cellForRowAtIndexPath: inside your if(cell == nil) statement. Which it shouldn't be. Let me explain. If you have a list of cells, and you want to set the titles to each cell to a string in an array called myArray, right now your (incorrect) code looks like this:
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cellIdentifier"];
if (cell == nil) {
// No cell to reuse => create a new one
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"cellIdentifier"] autorelease];
[[cell textLabel] setText:[myArray objectAtIndex:[indexPath row]]];
}
return cell;
}
Can you see the problem with that logic? The cell will only get an updated title if no reusable cell can be found, which, in your case, sounds like the situation. Apple says that you should create a 'new' cell each time cellForRowAtIndexPath: is called, which means that you put all of your setup code outside of the if(cell == nil) check.
Continuing with this example, the proper code would look like this:
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cellIdentifier"];
if (cell == nil) {
// No cell to reuse => create a new one
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"cellIdentifier"] autorelease];
}
[[cell textLabel] setText:[myArray objectAtIndex:[indexPath row]]];
return cell;
}
This way, the cell gets assigned the proper string whether or not a reusable cell is found and so calling reloadData will have the desired effect.
So I'm unsure how this dequeueReusableCellWithIdentifier works and if what I'm looking for is possible. I have a custom UITableViewCell with a BOOL showIcon. In the TableViewCell, if it's true, I show it, otherwise, I don't show this icon on my cell. In my cellForRowAtIndexPath, I grab the object in my array from my model, and set it for the UITableViewCell property.
This works at first on what is visible on my screen. Then as I scroll downwards on the table, it does not work and the values I should be seeing for the showIcon just don't show. And then when I scroll back to the top, the original icons that were there, are not there. Is the dequeueReusableCellWithIdentifier still what I want to be using in this case? Or am I doing something wrong with setting and showing data in it? Thanks a bunch.
CODE:
On my custom UITableViewCell, I have a
BOOL showIcon;
In my cellForRowAtIndexPath method, I use the UINib way of getting my custom UITableViewCell like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger row = [indexPath row];
static NSString *OrderTableViewCellIdentifier = #"OrderTableViewCellIdentifier";
OrderTableViewCell *cell = (OrderTableViewCell *)[tableView dequeueReusableCellWithIdentifier:OrderTableViewCellIdentifier];
if (cell == nil) {
UINib *cellNib = [UINib nibWithNibName:#"OrderTableViewCell" bundle:nil];
[cellNib instantiateWithOwner:self options:nil];
cell = self.TbvCell;
[cell.CheckmarkButton addTarget:self action:#selector(CheckmarkButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
self.TbvCell = nil;
}
Order *order = [orderArray objectAtIndexPath:row];
cell.order = order;
}
Then in my TableViewCell, it's something like this:
#property (nonatomic, retain) Order *order;
#property (nonatomic, retain) UIImageView *icon;
overridden setter:
- (void)setOrder:(Order *)newOrder {
if (!order.showIcon) {
icon.hidden = YES;
}
}
Your code reuse your cell so you have to cover all the case in your setOrder method. Try with:
- (void)setOrder:(Order *)newOrder {
if (!order.showIcon) {
icon.hidden = YES;
}
else {
icon.hidden = NO;
}
}
Or simpler:
- (void)setOrder:(Order *)newOrder {
icon.hidden = !order.showIcon;
}
Yes, dequeueReusableCellWithIdentifier works well with any custom UITableViewCell subclasses. However, keep in mind that this method is for memory saving, so you'll have to do the following (even with not subclassed, normal UITableViewCells):
This method returns an already used UITableViewCell instance (or nil if the table view doesn't have enough cells yet). It means that the cell won't be "empty"; you'll need to clear and re-set all its properties. For example, you'll need to be able do decide from the cell's corresponding NSIndexPath whether its icon has to be displayad, and what icon image you'd like to use at all.
So edit your code like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger row = [indexPath row];
static NSString *OrderTableViewCellIdentifier = #"OrderTableViewCellIdentifier";
OrderTableViewCell *cell = (OrderTableViewCell *)[tableView dequeueReusableCellWithIdentifier:OrderTableViewCellIdentifier];
if (cell == nil) {
UINib *cellNib = [UINib nibWithNibName:#"OrderTableViewCell" bundle:nil];
[cellNib instantiateWithOwner:self options:nil];
cell = self.TbvCell;
[cell.CheckmarkButton addTarget:self action:#selector(CheckmarkButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
self.TbvCell = nil;
}
// set your cell's properties to default values.
// e. g.: cell.showIcon = NO; and so on
Order *order = [orderArray objectAtIndexPath:row];
cell.order = order;
// then do a recustomization using the NSIndexPath's -row and -section properties
}
Hope this will help.
When i scroll down my UITableView, it starts showing me the same cells that i've already seen, and scrolling around a bit continues to put cells in the wrong place.
Here's the code i'm using. If anything additional is needed then let me know:
.h
#interface HomeViewController : UITableViewController {
int numberOfRows;
NSArray *allVaults;
}
#property (nonatomic, assign) int numberOfRows;
#property (nonatomic, retain) NSArray *allVaults;
#end
.m
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSString *vaultsPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/Vaults"];
NSFileManager *fileManager = [NSFileManager defaultManager];
self.allVaults = [fileManager contentsOfDirectoryAtPath:vaultsPath error:nil];
numberOfRows = [self.allVaults count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return numberOfRows;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
NSString *vaultsPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/Vaults"];
NSString *dictionaryPath = [NSString stringWithFormat:#"%#/%#",
vaultsPath,
[self.allVaults objectAtIndex:indexPath.row]];
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfFile:dictionaryPath];
cell.backgroundView = [AHCellCreation backgroundView];
cell.selectionStyle = UITableViewCellSelectionStyleGray;
cell.selectedBackgroundView = [AHCellCreation selectedBackgroundView];
cell = [AHCellCreation createCellWithDictionary:dictionary Cell:cell];
}
return cell;
}
Any help is appreciated!
EDIT 1: Image to show what happens when i move most code outside the (cell == nil) if statement:
Before:
After:
EDIT 2:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 82;
}
It looks as if you are only setting the cell content when the you're getting a nil back from dequeueReusableCellWithIdentifier. You need to set the cell contents every time, not just when you need to create a new cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
AHCell *cell = (AHCell*) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
// create a new cell if there isn't one available to recycle
// cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell = [AHCell blankCell];
}
// set the contents of the cell (whether it's a new one OR a recycled one)
NSString *vaultsPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/Vaults"];
NSString *dictionaryPath = [NSString stringWithFormat:#"%#/%#",
vaultsPath,
[self.allVaults objectAtIndex:indexPath.row]];
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfFile:dictionaryPath];
cell.backgroundView = [AHCellCreation backgroundView];
cell.selectionStyle = UITableViewCellSelectionStyleGray;
cell.selectedBackgroundView = [AHCellCreation selectedBackgroundView];
// cell = [AHCellCreation createCellWithDictionary:dictionary Cell:cell];
[cell populateAHCellWithDictionary: dictionary];
return cell;
}
Update updated code to address second issue. Rework AHCell so that the class method, e.g. blankCell returns a new cell with the subviews set up and the instance method, e.g. populateAHCellWithDictionary: sets the content.
In that case AHCellCreation class must add the subviews to the cell and then set the text in one go? You need to layout the cell inside the if statement (add the subviews, UILabels, UIImageView etc, and set their frames etc). And set the content outside the if statement.
Basically whatever doesn't change in each row put inside the if statement, but what changes from row to row put outside the if statement.
This is because the code inside the if statement is only reached when the cell is created, almost always its the cells that are visible on the screen when the Table view loads are created.
When you scroll down the cells that disappear off the top of the screen are reused, and put at the bottom. This means that is you have 100 rows, it won't create 100 cells (it only creates the number of cells that can be visible on the screen at a time and the reuses those) , as this would consume a lot of memory, and the scrolling wouldn't be as smooth.