I have a custom UITableViewCell inside it I have a label that shows the ammount of licks ,
when i drawing the cell everything is fine , however when I'm trying to change the label text it overlaps on its self (and it is very sad tbh).
I tried to remove and add it again , also tried to set needsDisplay and it didn't help me :(
here is my code :
in DrawLabel method
//we will start from the labels: licks and comments
_licksLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 5, self.frame.size.width, 40)];
[_licksLabel setText:[NSString stringWithFormat:#"%# %#",[_dict objectForKey:#"loves"],LocalizedString(#"licks")]];
[_licksLabel setFont:[UIFont fontWithName:FONT_REGULAR size:15]];
[_licksLabel sizeToFit];
[_licksLabel setTextColor:UIColorFromRGB(grey4)];
[self addSubview:_licksLabel];
when I do want to change the label's text here in another method
- (void)meLike
{
if ([[_dict objectForKey:#"self_loved"]integerValue]>0)
{
// return;
}
[_licksLabel removeFromSuperview];
[FeedAPI loveFeedWithId:[_dict objectForKey:#"id"]];
[_dict setValue:[NSNumber numberWithInt:1] forKeyPath:#"self_loved"];
[_dict setValue:[NSNumber numberWithInt:[[_dict objectForKey:#"loves"]integerValue]+1] forKeyPath:#"loves"];
[_licksLabel setText:[NSString stringWithFormat:#"%# %#",[_dict objectForKey:#"loves"],LocalizedString(#"licks")]];
[_licksButton setImage:[UIImage imageNamed:#"ic_lick_on.png"] forState:UIControlStateNormal];
[self setNeedsDisplay];
[super setNeedsDisplay];
[self addSubview:_licksLabel];
}
I find a little mess in your code. I Whould do like the following:
Use interface builder to add a UITableViewCell prototype to your table
assign (via interface builder) a reuse identifier (for example "cell")
inside this UITableViewCell add a UILabel (via interface) and place it wherever you like
assign a tag to the UILabel (for example 1)
After this setup you need to implement the table rows.
Here's an example
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell;
cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
UILabel *name = (UILabel *) [self.tableView viewWithTag:1];
name.text = #"whateveryouwant;
return cell;
}
Okay thanks for help to every1 i have solved the issue
the solution was to reload the tableview data .
Related
I have an UISwitch in my UITableViewCell prototype.
The problem is, when I turn on one of the switches, one of the not shown switches also gets turned on.
This doesn't happen in the iPad version where all cells are shown.
Here is some code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
Notification *notification = [notifications objectAtIndex:indexPath.row];
UILabel *titleLabel = (UILabel *) [cell viewWithTag:100];
UISwitch *newsSwitch = (UISwitch *) [cell viewWithTag:101];
UIImageView *imageView = (UIImageView *) [cell viewWithTag:102];
[titleLabel setText:[notification name]];
[imageView setImage:[UIImage imageNamed:[notification image]]];
BOOL isOn = [storage boolForKey:[NSString stringWithFormat:#"notification_%#", [notification name]]];
[newsSwitch setOn:isOn];
[newsSwitch setTag:indexPath.row];
[newsSwitch addTarget:self action:#selector(didChangeStateForSwitch:) forControlEvents:UIControlEventValueChanged];
return cell;
}
Your problem is that you first query the switch via its tag:
UISwitch *newsSwitch = (UISwitch *) [cell viewWithTag:101];
But later on, you change that tag:
[newsSwitch setTag:indexPath.row];
So when the cell gets reused, the switch won't be found as now its tag isn't 101 any more. Therefor, the switch will be stuck in its old state.
You can easily verify this by adding a NSLog(#"Switch: %#", newsSwitch); after querying the switch. You'll see that it'll output Switch: (null) for those rows where you have "wrong" switch values.
The solution this is to not modify the tag.
Question is, how do you remember which row the switch is for, then? One way would be this:
- (void)didChangeStateForSwitch:(id)sender
{
NSIndexPath *indexPath = [myTableView indexPathForCell:[sender superview]];
...
}
Another possibility is to use associated objects.
First time when you load the cell you are taking the view with tag 101 for switch.Few lines after that you are setting new tag to this switch and next time when you try to take the view with tag 101 it doesn't exist.[newsSwitch setTag:indexPath.row]; delete this line and try again
You can get the index path as #DarkDust suggested
- (void)didChangeStateForSwitch:(id)sender
{
NSIndexPath *indexPath = [myTableView indexPathForCell:[sender superview]];
...
}
In my UITableView Controller I have cells which each contain a UILabel and UISegmentedController. There are about 20 cells in total. The user can select the UISegmentedControl to control whether object is contained in an array or not.
My problem is that as the user scrolls the UISegmentedControl is duplicated and appears as though there are multiple selections for UISegmentedControl. I have a feeling that this is because cells are being reused but unfortunately I don't have a very good understanding of reusing cells.
I have been trying to play around with:
if(cell == nil){}
but I am not sure what should happen there:
Anyway here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"featuresCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSString *feature = [features objectAtIndex:indexPath.row];
UILabel *titleLabel = (UILabel *)[cell viewWithTag:9999];
titleLabel.text = feature;
UISegmentedControl *segmentedControl = [[UISegmentedControl alloc]initWithItems:[NSArray arrayWithObjects:#"Yes", #"No", nil]];
segmentedControl.frame = CGRectMake(215, 17, 85, 28);
[segmentedControl addTarget:self action:#selector(valueChanged:) forControlEvents: UIControlEventValueChanged];
segmentedControl.tag = indexPath.row;
[cell addSubview:segmentedControl];
if ([selectedFeatures containsObject:feature]) {
segmentedControl.selectedSegmentIndex = 0;
}
else{
segmentedControl.selectedSegmentIndex = 1;
}
return cell;
}
Your help will be greatly appreciated,
Thanks for your time.
cell is never nil so that's not going to help you.
The trick is to mark the cell in some way so as to know whether the segmented control has already been added. The best way is to use the segmented control's tag. Unfortunately you seem to be using the tag for some other purpose, so you'll need to stop doing that:
if (![cell viewWithTag:12345]) {
UISegmentedControl *segmentedControl = // ....;
// ....
segmentedControl.tag = 12345;
[cell.contentView addSubview:segmentedControl];
}
And now, you see, you are guaranteed that there is exactly one segmented control in the cell, and you can find it by calling [cell viewWithTag:12345].
The general rule is that each cell subclass should implement - (void)prepareForReuse to make it ready for configuration in - (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath. I think you can solve your problem by subclassing UITableViewCell, creating each cell with the required UISegmentedControl, and providing a method to configure the segmented control as required.
prepareForReuse should reset the segmented control so that each time you configure your custom cell it is in a known, empty state.
The advantage of this approach is that you are not only reusing the table view cell, but also the segmented control and any other decoration you want the cell to contain. Additionally you have moved the configuration of the cell into the cell object.
Briefly, in the cell subclass init method you create the segmented control and add it as a subview of the contentView.
In the cell subclass configuration method you provide you set the segmented control's segments etc.
In the cell subclass prepareForReuse you remove all those segments and leave the segmented control in a state ready for configuration next time you reuse that cell.
To go even further, start using auto layout to position the segmented control in the cell rather than using a set of hard numbers in a frame. If you get those working on one kind of device that's great but as soon as your cells have different dimensions (in a table view on a different device) things will not look so good.
Try this,
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"featuresCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSString *feature = [features objectAtIndex:indexPath.row];
UILabel *titleLabel = (UILabel *)[cell viewWithTag:9999];
titleLabel.text = feature;
UISegmentedControl *segmentedControl;
if ([cell.contentView viewWithTag:kSegmentTag]) { //kSegmentTag a tag
segmentedControl = (UISegmentedControl *)[cell viewWithTag:kSegmentTag];
} else {
segmentedControl = [[UISegmentedControl alloc]initWithItems:[NSArray arrayWithObjects:#"Yes", #"No", nil]];
segmentedControl.frame = CGRectMake(215, 17, 85, 28);
[segmentedControl addTarget:self action:#selector(valueChanged:) forControlEvents: UIControlEventValueChanged];
segmentedControl.tag = kSegmentTag;
[cell.contentView addSubview:segmentedControl];
}
if ([selectedFeatures containsObject:feature]) {
segmentedControl.selectedSegmentIndex = 0;
}
else{
segmentedControl.selectedSegmentIndex = 1;
}
return cell;
}
and in valueChanged:
- (IBAction)valueChanged:(id)sender {
UISegmentedControl *segmentedControl = (UISegmentedControl *)sender;
CGPoint tablePoint = [segmentedControl convertPoint:segmentedControl.bounds.origin toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:tablePoint];
//here indexpath.row give changed row index
}
I am having two labels created manually for displaying it in the tableviewcell named title and detail, code for displaying it are,
dealarray = [[NSMutableArray alloc]initWithObjects:#"1",#"2",#"3",#"4",nil];
detailarray = [[NSMutableArray alloc]initWithObjects:#"oneoneoneoneone oneoneoneoneoneoneoooooooo",#"two",#"three",#"fouronefouronefouronefouronefouronefouronefouron",nil];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
for(UILabel *lbl in [cell.contentView subviews])
{
[lbl removeFromSuperview];
}
cell.accessoryType= UITableViewCellAccessoryNone;
UILabel* title;
title= [[UILabel alloc] initWithFrame:CGRectMake(5,5,300,20)];
[cell.contentView addSubview:title];
[cell.contentView bringSubviewToFront:title];
[title setFont:[UIFont boldSystemFontOfSize:14]];
title.tag = 1001;
title.backgroundColor = [UIColor clearColor];
title.textColor = [UIColor blackColor];
title.text =[dealarray objectAtIndex:indexPath.row];
UILabel* detail;
detail= [[UILabel alloc] initWithFrame:CGRectMake(5,30,300,10)];
[cell.contentView addSubview:detail];
[cell.contentView bringSubviewToFront:detail];
[detail setFont:[UIFont systemFontOfSize:12]];
detail.tag = 1002;
detail.backgroundColor = [UIColor clearColor];
detail.textColor = [UIColor blackColor];
detail.text = [detailarray objectAtIndex:indexPath.row];
return cell
}
No problem in displaying those 2 labels and no problem in hiding all the 'detail' label and displaying the 'title' alone, the problem arises when I try to display the 'detail' label of the resp selective of cells.
Code tried:
// conti of cellforrowatindexpath
detail.numberOfLines = 3;
detail.lineBreakMode = NSLineBreakByWordWrapping;
if (a==-1)// declared 'a' in viewdidload as -1
{
((UILabel*)detail).hidden = YES;
}
else if(a==indexPath.row)
{
((UILabel*)detail).hidden = NO;
}
((UILabel*)detail).hidden = YES;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
a=indexPath.row;
[tableview reloadData];
}
Sorry for posting large amount of codes, it may help any one who is searching for the wholesome data of my doubt.
Whats the mistake am doing, I can't hide the detail label for resp selecting of cells. Can anybody help in this regard?
May I suggest you to use xcode functionnality to design your cell content? (as easy as drag and drop of UILabel on your cell for example) Then you will be able to access them from your code using their respective "tag" id.
A correct way of doing is detail here : http://www.appcoda.com/customize-table-view-cells-for-uitableview/
I think it's a better way than programmatically creating content for your cell, because using xcode interface you will be able to flawlessly define your interface.
Anyway to respond precisely to your question you should try to hide the detail label from "didSelectRowAtIndexPath" :
// Retrieve the corresponding cell
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
// Get the detail label (using its tag) you set it to 1002
UILabel *detail = (UILabel *)[cell viewWithTag:1002];
// Hide it
detail.hidden = true;
Hope this help.
There is an error in your logic. You code as written will always set detail.hidden to YES, the 2 preceding if are ignored, thus this (BTW you don't need the type coercion and extra brackets):
if(a==-1) detail.hidden = YES;
else if (a==indexPath.row) detail.hidden = NO;
else detail.hidden = YES;
I've got 3 uilabels added to a uitableview cell. The problem that I am running into is when I swipe to delete, my UILabel on the right side of the cell doesn't move so the delete button and the UILabel are overlapping each other. I've posted some of my code below.
I developed my layout using storyboard, so from what I am reading, frames aren't going to help.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *baseTableCellIdentifier = #"baseCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:baseTableCellIdentifier];
BaseInfo *infoAtIndex = [[[DataClass getInstance] allItems] objectAtIndex:[indexPath row]];
baseName = (UILabel *)[cell viewWithTag:1];
baseICAO = (UILabel *)[cell viewWithTag:2];
baseTime = (UILabel *)[cell viewWithTag:3];
[cell.contentView addSubview:baseName];
[cell.contentView addSubview:baseICAO];
[cell.contentView addSubview:baseTime];
[baseName setText:[infoAtIndex name]];
[baseICAO setText:[infoAtIndex icao]];
baseTimeZome = [NSTimeZone timeZoneWithName:[infoAtIndex timeZone]];
[baseDate setDateFormat:#"HH:mm"];
[baseDate setTimeZone:baseTimeZome];
NSString *baseTimeString = [baseDate stringFromDate:[NSDate date]];
[baseTime setFont:[UIFont boldSystemFontOfSize:20]];
[baseTime setText:baseTimeString] ;
return cell;
}
-(void)setEditing:(BOOL)editing animated:(BOOL)animated{
if (editing) {
//no idea what to put here
}else{
}
}
In your UITableViewCell subclass override setEditing:animated: and perform there any layout change. As the commenter says, you may not need to do that setting proper values for your label's autoresizingMask.
You should create a cell subclass. Then you can implement setEditing:animated: to modify the subviews frames.
Hi I have a UITableView and I am dymanically inserting cells into it that contain a UIStepper and a UILabel. The UILabel shows the value of the UIStepper.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if(!cell)
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
[cell.textLabel setText:[self.myarray objectAtIndex:indexPath.row]];
UIStepper *stepper = [[UIStepper alloc]init];
UILabel *label = [[UILabel alloc]init];
label.text = [NSString stringWithFormat:#"%.f", stepper.value];
[cell addSubview:stepper];
[cell addSubview:label];
[stepper addTarget:self action:#selector(incrementStepper:) forControlEvents:UIControlEventValueChanged];
return cell;
}
I have removed some of the above lines that do the formatting for clarities sake but this works and everything is ok.
-(void)incrementSkillStepper:(id)sender
{
UIStepper *stepper = (UIStepper*)sender;
//set the label that is in the same cell as the stepper with index stepper.value.
}
When I click the stepper in the specific cell I want the label in the same cell to increment, but my problem is in which the way that addtarget works - I can only send the sender which in this case is the stepper to the event which means it doesnt have access to the dynamically created label. Does anybody know how I can set the text of the label from the incrementStepper delegate method?
Call [sender superview] to get the cell that it's in,
-(void)incrementSkillStepper:(id)sender
{
UIStepper *stepper = (UIStepper*)sender;
UITableViewCell* cell = [stepper superview];
UIView* subview = [[cell subviews] lastObject]; // Make sure your label *is* the last object you added.
if ([subview isKindOfClass:[UILabel class]]) {
// do what you want
}
}
you can also loop thru the [cell.contentView subviews] array, and get the label you need, better give the label a tag value, and use viewWithTag
You can set tag to UIStepper as indexPath.row and set tag to label as indexPath.row+999.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UIStepper *stepper = [[UIStepper alloc]init];
stepper.tag = indexPath.row;
UILabel *label = [[UILabel alloc]init];
label.tag = indexPAth.row + 999;
//......
}
Now in delegate method of UIStepper, you can find label in that cell like this
-(void)incrementSkillStepper:(id)sender
{
UIStepper *stepper = (UIStepper*)sender;
UILabel *label = (UILabel *)[self.view viewWithTag:sender.tag + 999];
//now this is same label as you want. Now you can change the value of label as you want
}
You should update the model with the value of the stepper, and then set the label's value based on that value in the model. You should give the stepper a tag in cellForRowAtIndexPath that's equal to the indexPath.row, and in the stepper's action method, set the value of a property in your model to the stepper's value. Then, reload the row at that same indexPath.
(void)incrementSkillStepper:(UIStepper *)sender {
NSInteger row = sender.tag;
[self.theData[row] setObject:#(sender.value) forKey:#"stepperValue"];
[self.tableView reloadRowsAtIndexPaths: #[row] withRowAnimation:UITableViewRowAnimationNone];
}
In cellForRowAtIndexPath, you would have something like this to populate the label:
UILabel *label = [[UILabel alloc]init];
label.text = [NSString stringWithFormat:#"%#", self.theData[indexPath.row][#"stepperValue"]];
In this example, I'm assuming you have an array of dictionaries (theData) that has all the data you need to populate the cells. One of the dictionary keys, "stepperValue" would be used to store the stepper value as an NSNumber.