I have a NSTimer that calls this method every fourth second:
- (void)timerDecrement
{
timerCount = timerCount-1;
[OtherViewControllerAccess updateTimeLeftLabel];
}
In the updateTimeLeftLabel in the other class:
- (void)updateTimeLeftLabel
{
int timeLeft = OtherClassAccess.timerCount;
UILabel *timeLeftLabel = [[UILabel alloc] initWithFrame:CGRectMake(200, 10, 120, 20)];
timeLeftLabel.text = [NSString stringWithFormat:#"Tid kvar: %ih", timeLeft];
[cell addSubview:timeLeftLabel];
}
Basically I want my app to update a label in a cell in the tableview with the current time left, but the above method doesn't do anything to the call. So my question is, how can I add this subview to the cell outside the cellForRowAtIndexPath:delegate method, and then make it update that label every time the method is called.
So my question is, how can I add this subview to the cell outside the
cellForRowAtIndexPath:delegate method, and then make it update that
label every time the method is called.
The answer is, don't add subviews to a table view cell outside of cellForRowAtIndexPath. The cells belong to the table view, and you absolutely, categorically, should NOT try to modify them. That's the table view's job.
Just as a small example of what's wrong with your code, you would be adding an ever-increasing number of label views to your table view cell, one every 1/4 second. That's bad.
Second point: Which cell is "cell"? A table view manages a whole table of cells. If the user scrolls, some cells are scrolled off-screen and replaced with different cells.
Instead, you should figure out which indexPath contains the cell with your data in it, change the data in your model, and tell your table view to update the cell at the appropriate indexPath. That will cause it to redraw with updated contents.
Here is how I did something similar. I created a custom UITableViewCell class that has a timestamp UILabel:
#property (nonatomic) UILabel *labelTimestamp;
In that cell's layoutSubviews, I update the label size based on its title.
- (void)layoutSubviews {
[super layoutSubviews];
[self.labelTimestamp sizeToFit];
...
}
I then have an NSTimer firing every minute in my UIViewController that update that label in every visible cell (you could adapt to update only one cell with a specific indexPath).
- (void)timerDidFire
{
NSArray *visibleCells = [self.tableView visibleCells];
for (GroupViewCell *cell in visibleCells) {
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
[cell.labelTimestamp setText:[self.groupController statusUpdateDateAtIndexPath:indexPath]];
[cell setNeedsLayout];
}
}
I would keep the set up of the cell's centralized in cellForRowAtIndexPath: method. You can keep using your NSTimer to call reloadRowsAtIndexPaths:withRowAnimation: with the indexes of the cell/cells you want to update and therefore cellForRowAtIndexPath: will be called again.
Why don't you put that value (which you want to display in the cell) in a variable and assign a UILabel that value. In your updateTimeLeftLabel just call [self.tableView reloadData].
You can reload whole table or some specific rows but that needs connection of datasource with your views . You have to change your dataset first and then you have to call
[self.tableview reloadData];
another method is that, after changing dataset call
[self.tableview reloadRowsAtIndexPaths:indexPath withAnimation:animation];
2nd method requires indexPath i.e. you know that which cell you need to edit .
My problem was same . In my project there were 2 TextFields and 1 label in each cell . Now depending on the values of 2 textfields, I have to show their multiplication in UILabel. and this is my code.
-(void)textFieldDidChange:(id)sender{
UITextField *_sender = (UITextField *)sender;
int tag = _sender.tag;
int row = tag / 3;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
((UILabel *)[[self.tableView cellForRowAtIndexPath:indexPath] viewWithTag:row * 3 + 2]).text = #"hello";
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"here");
static NSString *CellIdentifier = #"procedureDetailsCell";
UITableViewCell *cell = (procedureCell *)[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if ( cell == nil ) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
[((procedureCell *)cell).Quantity setTag:indexPath.row + 0];
[((procedureCell *)cell).Quantity addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
[((procedureCell *)cell).Cost setTag:indexPath.row + 1];
[((procedureCell *)cell).Cost addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
[((procedureCell *)cell).total setTag:indexPath.row + 2];
return cell;
}
Logic is simple I have used Custom TableViewCell which contains 2 textfields and 1 label. when one of the two textfield's value is changed we are calling "textFieldDidChange" method which finds indexpath and then find UITableViewcell and then updates its lastview's text value, that is our UILabel. we have to give unique tag to each of our views .
Related
i was trying to updated the cell item while updating the height of the cell but its not working.
[_tblBillHistory beginUpdates];
[_tblBillHistory endUpdates];
these method i have used to updated the cell height
Err- > EXC_BAD_ACCESS(code=2,address=0x7fff5b4daf98)
this error message getting for cellForRowAtIndexPath method. Nslog also printing the selected row correctly. any idea??? thank you.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if((selectedPath!=nil ) & ([indexPath isEqual:selectedPath])) {
NSLog(#"%ld",indexPath.row);
UITableViewCell *tempCell = [_tblBillHistory cellForRowAtIndexPath:indexPath];
UIStackView *stkItems=(UIStackView *)[tempCell viewWithTag:8];
for(int i=0;i<20;i++)
{
UIButton *btn=[UIButton buttonWithType:UIButtonTypeSystem];
[btn setTitle:#"test button" forState:UIControlStateNormal];
[stkItems addArrangedSubview:btn];
}
return 700;
}
return 250;
}
I think this method is not good place to update cell's components.
If you want to add element to the cell you can create it at any other methods, for example:
- tableView:willDisplayCell:forRowAtIndexPath:
heightForRowAtIndexPath may be called many times (every reloadData or animation block).
You should divide your code, in heightForRowAtIndexPath you should only calculate height without request the cell (because the tableView not have cells in this method).
Adding or removing elements to the cell you can create in the didSelectMethod (but more better solution move this code to the cell or create a custom class for selected cell):
- tableView:didSelectRowAtIndexPath:
Please refer this
EXC_BAD_ACCESS in heightForRowAtIndexPath iOS
Try to replace following line
UITableViewCell *tempCell = [_tblBillHistory cellForRowAtIndexPath:indexPath];
with this one
UITableViewCell *tempCell = [self tableView:_tblBillHistory cellForRowAtIndexPath:indexPath];
This will stop recursive call as this is calling cellForRowAtIndexPath as a function not as data source of table view....
I want to make cells overlapping.
What I did is
Adjust the tableview's contentSize:
int count = [self.tableView numberOfRowsInSection:0];
self.tableView.contentSize = CGSizeMake(self.view.frame.size.width ,(100 * count - 30 * (count - 1)));
And set the cellForRowAtIndexPath :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
cell.contentView.backgroundColor = [UIColor orangeColor];
}
if (indexPath.row != 0) {
UITableViewCell *currentCell = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row + 1 inSection:0]];
[currentCell setFrame:CGRectMake(0, - 30, self.view.frame.size.width, 100)];
}
return cell;
}
But when I test, this is not working.
Any help? Thanks.
What is happing with your code is that the values that you are manually setting inside tableView:cellForRowAtIndexPath: are getting overwritten by the UITableViewController layout methods.
You are not supposed to edit the frame of a table view cell directly since this is an attribute that is set by UITableViewController internally. You should only interact with this by using methods of the UITableViewDelegate protocol. (tableView:heightForRowAtIndexPath:, ...)
IMHO the best way (cleanest one) to implement what you are describing is to subclass UICollectionView instead of UITableView and define a custom layout.
However if you wanna go the hacky way you can achieve an apparent row overlapping in many different ways.
Just as an example, you could create two different row types, one with height 30 and one with height 70, where the first one represents the overlapping part of the row and the second one represents the not-overlapping part of the row. Then use the first type for even rows and the second one for odd rows.
Hope this helps.
p.s. i'm really sorry if my english is not the best
I've a grouped UITableView with several custom cells where I placed text fields. I have defined a property pointing to the text field that is currently active:
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
self.activeTextField = textField;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
self.activeTextField = nil;
}
I need to get the row and section of the cell containing such text field. I tried this:
UITableViewCell *parentCell = (UITableViewCell *)[self.activeTextField superview];
NSIndexPath* cellPath = [self.tableView indexPathForCell:parentCell];
NSInteger cellRow = [cellPath row];
But I always get a 0. It looks like parentCell is not an UITableViewCell actually (but it should be). How can I get those values?
Thanks
If you are adding the text view to cell's container view then you have to call self.activeTextField.superview.superview. If not, self.activeTextField.superview should work.
In order to find out if your text field is added to container view's cell:
If you add the text field from xib/storyboard to a custom cell then it is in contentView
If you do [cell addSubview:] then is in cell's view
If you do [cell.cotentView addSubView:] then is in container view.
Totally unsure if it works but you might try:
UITableViewCell *cell = [self.tableView cellForRowAtPoint:[self.activeTextField center]];
I have a UITextField in a custom cell inside table. I created new class DataCell which is subclass of UITableViewCell. Inside DataCell I created outlets for textfields and I also have method inside implementation file which uses 'editing did end' and I manipulate textField values there.
I am now wondering how to get rowIndex or number of the cell, as each time I click + button new custom cell is loaded on the table. If I get tag I always get same tag number regardless of the cell I selected.
The text field passed to your delegate is a subview of the cell's contentView.
UITableViewCell *cell = (UITableViewCell*) textField.superview.superview;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
You can use this logic when you are not sure of hierarchy between textfield and cell.
UITableViewCell *cell = nil;
UIView *parentView = textField.superview;
while(parentView) {
if([parentView isKindOfClass:[UITableViewCell class]]) {
cell = parentView;
break;
}
parentView = parentView.superview;
}
if(cell)
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
Add tags to the text field in the tableView:cellForRowAtIndexPath: method. In this example, I have a custom cell with a label and a text field:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
RDCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.label1.text = self.theData[indexPath.row];
cell.textField.tag = indexPath.row;
return cell;
}
It sounds like you are maybe handling the end of editing in your custom cell class, but you might want to consider doing it in the table view controller instead, since that gives you easy access to the model, which I presume you are modifying with what the user types in the text field. If you do that, then you should connect the text field's delegate property up to the table view controller in IB.
If we're accepting fragile answers then for the sake of contributing something new to the conversation:
CGRect rectInTableView =
[tableView convertRect:textField.bounds fromView:textField];
NSUInteger indexOfCellContainingTextField =
(NSUInteger)(rectInTableView.y / tableView.rowHeight);
Assumptions that make it fragile: (i) all rows are the same height; (ii) the height is set on the table view. If you haven't implemented UITableViewDelegate -tableView:heightForRowAtIndexPath: then both of those assumptions will hold true. You're also taking advantage of the fact that casting a positive float to an integer rounds down.
I would argue that although still not completely clear of assumptions, this is less fragile than Mundi's solution because it makes assumptions only about things you do directly control (ie, cell sizing) and not about things you don't (ie, the view hierarchy UIKit uses internally to present table views).
I am using group of buttons with different images and tableview with custom cell in my project.every cell having group of components (like imageview,button,label,slider) when pressing each button one cell will be added.
My button image and also added to imageview of the cell.
How can i get the index of each cell when pressing the button again.
cell will be added when i press the button at first time.
when pressing second time i want to increment the label value inside the cell.so i need the indexpath to cell the cell from the tableview.
I'm not sure exactly what you're looking for, maybe you can clarify what you're trying to do.
The method indexPathForCell: is a UITableView method that gives you the index of a certain cell.
Clarify your question a bit and maybe we can help more.
You can call button touch event to get the cell of uitableview -
UIView *cellView = (UIVIew *)[button superview];
UITableViewCell *cell = (UITableViewCell *)[cellView superview];
NSIndexPath *indexPath = [self.tableview indexPathForCell:cell];
Without knowing the structure of your cells, table, or anything like that. this code will work.
-(NSIndexPath *) pathForButton:(UIbutton *) b {
UITableView *tv = nil;
UIView *superView = [b superview];
UIView *cell;
while(superview != nil && ![superview isKindOfClass:[UITableView class]]){
cell = superview;
superview = [superview superview];
}
if(superview != nil && cell != nil){
tv = (UITableView*)superview;
return [tv indexPathForCell:cell];
}
return nil;
}