I am trying to get a value from a table view controller and send it to another table view controller.
I want to achieve it through the method didSelectRowAtIndexPath where I have used
UITableViewCell *cell=[tableView cellForRowAtIndexPath:indexPath];
NSString *num=cell.textLabel.text;
The problem here is that the cell contains a number and a string but I only want the number.
Is there a way to get only the number?
There's almost no reason I can think of where trying to store information about your data inside labels or tags of your views is a good idea. It makes the program harder to write and harder to understand. Instead, use the same methods that provided the data to put in the view in the first place. Here's an example:
- (void)tableView:(UITableView *)tv didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
id dataObject = [self dataForIndexPath:indexPath]; // assuming you called this in ..cellForRow... to provide the "vc" instance you mentioned in your comment
int value = dataObject.number;
//Now go do work with the value you extracted.
}
This method dataForIndexPath: just represents the work done to get the vc instance you referred to in your comment as being the source for the data used to populate table cell's label. Notice that this method doesn't access the views at all - it goes right to the original source of the data to get exactly what you need. Unlike the other solution, this will also work for data of any type, not just integers.
Hope this helps, and let me know if you have any questions!
Instead of trying to extract it from the textlabel, set the cell's tag value to the number in cellForRowAtIndexPath method and use it in the method didSelectRowAtIndexPath.
cellForRowAtIndexPath
cell.tag = vc.Number;
didSelectRowAtIndexPath
UITableViewCell *cell=[tableView cellForRowAtIndexPath:indexPath];
NSUInteger num=cell.tag;
Related
I have TableViewController and ViewController. In 3 cells change pictures depending on the index and intForString in ViewController.
In the first session I work with all cells. In the second I work with one cell. When I go to the third session image stay only to the cell in which I worked the second session. And the other two images disappear. How to make that all the images were displayed?
I don't understand what you are asking when you say "How to make that all the images were displayed?"
However, this bit is very wrong:
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell1 = [tableView cellForRowAtIndexPath:indexPath];
The table view data source method tableView:cellForRowAtIndexPath: is supposed to configure and return a new (or recycled) table view cell.
The method you're calling in the first line is the table view instance method cellForRowAtIndexPath. That method only returns a cell if there is a cell on-screen for the specified indexPath.
That won't work.
You want to use code that calls dequeueReusableCellWithIdentifier:forIndexPath. If you've defined a class for your identifier, that method will always return a valid cell. If not, you need to add code that checks for nil and creates a new cell using the UITableViewCell method initWithStyle:reuseIdentifier:
I am working on an ios application,
I have a normal table view. When calling heightForRowAtIndexPath I am doing the folowing
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellId = [self getCellIds][indexPath.row];
BaseTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
return [cell calculateHeigh];
}
Basically I am dequeueing the cell because I have a function calculateHeigh inside every cell that will do the height calculation. this is working fine as intended however I have a concern:
Is it safe to call dequeueReusableCellWithIdentifier: inside the heightForRowAtIndexPath ? will it cause any issue?
EDIT:
Just to clarify why I did this, I have a big amount of custom cells with different identifier that needs to be loaded. and to avoid having a huge if-else statement in my heightForRowAtIndexPath I placed the getter of the cell height in the custom cell that way I just ask it to return it (no calculation is made there), I can't do it as a class method as I don't know which class, I can get the object from the identifier and not the class. And I want to avoid a big if-else just for code readability.
So my concern was with the dequeueReusableCellWithIdentifier: is it heavy to call it when getting the height? will it cause memory issues or lags? or is it worth to just do a bug if-else of use a dictionary?
First of all you should avoid any calculations in table drawing methods(such as heightForRow, cellForRow, etc). These methods are called a lot and although your table may be short and/or not complicated(with custom cells with a lot of labels, buttons and images) you should always try to optimize this drawing process or otherwise user will experience some nasty lag when scrolling.
So you should call some method to prepare data before calling 'reloadTable'
-(void)prepareMethod
{
//get only one cell to calculate all row heights
BaseTableViewCell *cell = [_myTableView dequeueReusableCellWithIdentifier:cellId];
for (NSDictionary* dataObj in _dataArray)
{
//loop through all rows data and set new property for row height
dataObj[#"rowHeight"] = [cell calculateHeigh];
}
}
And then when calling heightForRow just pass this value without any expensive operation(such as probably string calculations):
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
//always make sure you don't access unexisting array index
return ( indexPath.row < _dataArray.count ) ? _dataArray[indexPath.row][indexPath.row][#"rowHeight"] : 1.0;
}
Of course you don't need separate method just to populate row heights in your data array - you can populate this value when populating(formatting) your data array to avoid second array iteration. It all depends on your current implementation.
Just remember that expensive drawing methods(not only for table though) should always be as short as possible and just get data needed for drawing and draw. It's really so simple. If you need to make some complicated calculations do it before that(maybe in view init) so your data is prepared before actual drawing. This way your application will be working smoothly even with bigger tables(because no matter how big the table is, UITableViewController draws only visible cells).
Regards,
hris.to
I don't like to have big if statements in heightForRowAtIndexPath and accessing a cell using dequeueReusableCellWithIdentifier. Your approach getting cell height from each cell quite is reasonable. I believe your calculateHeigh return value depends on the table data you pass into the cell.
In BaseTableViewCell.h
+ (CGFloat)heightWithData:(id)data;
In BaseTableViewCell.m
+ (CGFloat)heightWithData:(id)data
{
//put your calculateHeigh logic here. I believe your calculateHeigh depends on the data each cell has.
}
Then you can do
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [BaseTableViewCell heightWithData:[self.tableData objectAtIndex:indexPath.row]];
}
If you do this, you don't need to access each cell object to get cell height.
You should not use this method to provide the calculation. Based on what I can see on your setup, you are calculating the height based on the values already on your cell. What happens is that the cell dequeue system will give you a cell to reuse, but because it's sharing cells from multiple index paths, that cell probably has data that belongs to a record of an index path different from the current one. Get the calculate height code and try to reproduce it inside the datasource callback you are using.
I have a UITableView for which I created two different custom cells, let's call one "RegularCell" and the other "BigCell". The reason to do so is that I need different representation for the data model objects, where in a certain case I wish to present the data differently.
I read a bit about ways to approach it via heightForRowAtIndexPath vs. cellForRowAtIndexPath, but I'm not clear how to approach it in my case >> In my table, I don't know, in advance, which row will include which custom cell; I only get this data in cellForRowAtIndexPath where I check in the data array which case I need to represent for a specific row.
It seems silly to do the calculation in heightForRowAtIndexPath since it's called before cellForRowAtIndexPath and the whole idea there is that you don't create all the cells in advance and just "make room" for things like the scroller size.
On the other hand, only when I realise which content I'm representing, I can tell which cell I require and therefore what should be the row height.
Anyone encountered a case like that and can share some wisdom?
David is right!
I don't agree with you saying
In my table, I don't know, in advance, which row will include which
custom cell; I only get this data in cellForRowAtIndexPath where I
check in the data array which case I need to represent for a specific
row.
in heightForRowAtIndexPath you can access you datasource the same way you do it in cellForRowAtIndexPath:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGFloat bigCellHeight = 80.0;
CGFloar regularCellHeight = 44.0;
MyDataObject *object = [myArray objectAtIndex: indexPath.row];
if ([object anyConditionToChoseBigCell]) {
return bigCellHeight;
}
return regularCellHeight;
}
heightForRowAtIndexPath is called before cellForRowAtIndexPath as the tableView layouter needs to know how big the scroll view is going to be as well as what cells should be visible on screen at the time its going to be displayed, this may vary depending on the height of each cell.
I've come across this problem before and my recommendation is that you calculate the height of each of the cells in your model before your tableView is even run.
I've had similar problem. That's what I ended up with:
-(CGFloat)tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
return cell.frame.size.height;
}
OK...
I found an easy solution - Since I have an array of data objects, I can check per each row, the relevant data object relevant property >> in my case, I have a BOOL value called isSuper >> and in this way I can set the row height per each object in the DB without the need to create or upload an actual UITableViewCell in heightForRowAtIndexPath.
I have a UITableView and each UITableViewCell contains an editable UITextView.
My data source is a NSMutableArray containing NSMutableDictionary that holds the text value and some styling keys for the text.
How can I (efficiently) make it so that any changes a user makes to the UITextView are updated in the corresponding datasource NSMutableDictionary?
A rather simple way is to utilize the index path of the table, it is NOT the cleanest so it depends on the complexity of your datasource, and if you have multiple tables etc.
What you can do is when the user ends editing the textView or selects another row in tableView, you read the indexPath of the selected row (That requires that the row keeps actually being in the selected state while editing the textView which it should by default). From there you call your update method.
To catch the end of editing you implement
-(void)textViewDidEndEditing:(UITextView *)textView
{
NSIndexPath *selectedpath = [myTable indexPathForSelectedRow];
[self myUpdateMethodForIndexPath:selectedpath];
}
To catch deselect of the table row and the above doesnt get called, you implement
-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self myUpdateMethodForIndexPath:indexPath];
}
Your update method must then read the value of the textView at the corresponding cell at the indexPath and handle this in the datasource. To care for sections of course you need to correctly handle the indexPath, in the example just the row is used (1 section).
-(void)myUpdateMethodForIndexPath:(NSIndexPath *)editPath
{
UITableViewCell *editCell = [myTable cellForRowAtIndexPath:editPath];
NSString *newText = editCell.theTextView.text;
....
NSMutableDictionary *dict = [myDictArray objectAtIndex:editPath.row];
....
}
First of all, you must assign a tag to each UITextView, to know exactly which UITextView are you refering.
Then you must implement UITextViewDelegate in your view controller which holds the tableview. Then, make this view controller the delegate of each UITextView. Read here how to implement it: UITextViewDelegate reference.
Look for the protocol method that better fits your needs (probably – textView:shouldChangeTextInRange:replacementText:, wich is called each time the text changes in any range.
In the delegate method, you can read the text with UITextView.text property, and assign this value to your data model (the dictionary).
Another possible approach is to use KVO pattern, but it requires more coding and a better understanding both, the pattern and the implementation. Hope it helps!
Make your view controller the delegate of each text view. Listen for appropriate events to get the updated text. Then have the view controller update the data model with the updated text.
If you have custom cells then have the cell be the text view delegate. Then the cell should notify its delegate (the view controller) about the updated text. Of course this requires that your custom cell class define its own delegate protocol and the view controller should make itself the delegate of each cell.
That's as specific as an answer can be for such a vague question.
I have a UITableViewController with prototype cells containing UITextFields. To configure these custome cells, I've created a UITableViewCell subclass. I've conected the textField to the cell subclass via an outlet (nonatomic, weak).
On this subclass I've created a protocol for which the UITableViewController is its delegate so that everytime something changes in these textFields, the TableViewController knows about it. Basically I wanted this to save the values on the NSUserDefaults
Besides, in order to dynamically obtain values from these textFields, I can do something like this:
((TextFieldCell*)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]]).textField.text
It works ok most of the times. However when the textField is outside of the view because it has scrolled, the vaulue I get from textField.text is (null). As soon as it gets in the view again, everything goes back to normal.
I tried to change the outlet from weak to strong but to no avail.
I guess I could define some private NSStrings on the class, and fill them out when the delegate protocol gets called. The thing is that I wanted to get my code as generic as possible, keeping the need for private variables as low as possible, mostly to simplify the cell generation code.
Is there any other way to get the values of the textFields when they are outside of the view?
Thanks in advance!
But you know that UITableView only keeps Cells for the visible rect?
When a cell leaves the screen, and a new cell is needed for another cell moving into the visible area, the old cell is reused for the new content.
So there is not one cell for each row of your table view.
And if your table contains a lot data, there are far more rows than cells.
As Thyraz said, the UITableView only keeps cells for the visible rect -- and a reasonable buffer to allow for scrolling. Thats why 'reuse identifiers' are so very important, they indicate which cells can be used for which tables (critical when you have more than one table to worry about). Unfortunately, that doesn't answer your question by itself.
The responsibility for storing the contents of those textViews isn't on the UITableView's shoulders. It's your job to provide that data through the data source delegate protocols, and therefore you should be querying the data source for that information.
Edit: Which means that yes, you should be storing this data somewhere else, usually in the form of properties on the view controller class that contains the table view. I'd recommend the use of NSArray for the purpose, but you can also do it through dicts or even, at the last resort (and this is more a in theory you can do this, but it's an incredibly bad idea kind of thing), a series of properties. Personally, I almost always use NSArrays because they're structured in a manner appropriate to the problem, but you could theoretically do it other ways. (I've used a dict based structure exactly once, and that was a situation where my data was nested inside itself in a recursive structure)
UITableViewController doesn't keep cells around once off the screen. You can use the following pattern to get a previously used one as a memory management optimization, but you MUST assume that cells need to have the values reset on them every time they come onto the screen (even if dequeued) because there is no guarantee what the values will be.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier1 = #"Cell1";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier2];
if( cell == nil ) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier1] autorelease];
cell2.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell2.editingAccessoryType = UITableViewCellAccessoryNone;
}
switch( indexPath.section ) {
case first_Section:
if( row == 0 ) {
cell1.textLabel.text = #"Some Text";
cell1.accessoryView = [self myCustomViewControl];
cell = cell1;
}
... etc
}
}