I add UIView in UITableviewcell. The first occurrence of all appears normal. But every time when you scroll all the strays. UILabel overlap with different values. Below is the code to create Cell. What am I doing wrong?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
float low_bound = 0;
float high_bound = 7;
float rndValue = (((float)arc4random()/0x100000000)*(high_bound-low_bound)+low_bound);
int dayStart = rndValue;
int dayMonth = 30;
int maxI = (dayStart+(7-dayStart))*(indexPath.row+1);
int Pos;
if (indexPath.row == 0) {
dayNumber = dayStart;
Pos = 0;
} else {
Pos = dayNumber;
}
for (int i=dayNumber; i < maxI; i++) {
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(4, 4, 40, 40)];
if (i > 0) {
CGRect frame = myView.frame;
frame.origin.x = (44.0*(i- Pos-1));
myView.frame = frame;
}
UILabel *yourLabel = [[UILabel alloc] initWithFrame:CGRectMake(24, 0, 40, 40)];
[yourLabel setText:[NSString stringWithFormat:#"%i", i-dayStart+1]];
[myView addSubview:yourLabel];
[cell addSubview:myView];
dayNumber++;
}
return cell;
}
THe cells you are using are being reused by the system, and every tieme that happens, you just slap another label on top of all the others. You need to either remove the labels before reusing the cell by doing something like
//this needs to be placd before the for-loop
for (UIView *view in cell.contentView.subViews){
[view removeFromSuperview];
}
or you can keep references to the labels and just assign new values to them.
I would advise you to do the latter, but it will probably mean that you have to subclass UITableViewCell.
Right now, the code in your forloop keeps creating new labels and just stacks them up like a lasagna.
Related
I am writing a geometry based app. At one point in the app, there will be a UITableView with some custom cells. These cells contain UILabels. Amid the text of some these labels, I want to insert symbols that look these two triangles:
(source: wiley.com)
However, since I cannot find these symbols in any Apple fonts, is there a way to insert an image into the string in place of a symbol?
Here is a (very) rough idea of what I'm going for (the actual table will not be static):
Ok, I get what you're trying to do. The key, I think, is to just keep adding controls to your cell, calculating the width as you go along.
First, I'd suggest a data structure to hold your cell contents. A simple array will do the job. I generally do this stuff as an ivar:
#interface LabelWithImagesViewController ()
{
NSMutableArray *_cells;
}
#end
Then fill this array with the text and images you want. I'm doing a single row, but you can repeat for every row you need.
- (void)viewDidLoad
{
[super viewDidLoad];
_cells = [[NSMutableArray alloc] init];
[_cells addObject:[[NSArray alloc] initWithObjects:
[UIImage imageNamed:#"triangle.png"],
#"CAT",
[UIImage imageNamed:#"semiequal.png"],
[UIImage imageNamed:#"triangle.png"],
#"DOG",
#" If",
[UIImage imageNamed:#"triangle1.png"],
#"then",
[UIImage imageNamed:#"triangle2.png"],
nil]];
}
And then, you need to create your cell:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return _cells.count;
}
#define kEquationTag 100
#define kCellHeight 44
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"equationCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UIView *equationContainer;
if (cell == nil)
{
// if we don't have a cell create it, including the frame to hold our custom stuff
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
equationContainer = [[UIView alloc] initWithFrame:cell.contentView.bounds];
equationContainer.tag = kEquationTag;
[cell.contentView addSubview:equationContainer];
}
else
{
// if we are dequeing one that already exists, let's get rid of the old custom stuff
equationContainer = [cell.contentView viewWithTag:kEquationTag];
for (UIView *view in equationContainer.subviews)
{
[view removeFromSuperview];
}
}
// Configure the cell...
NSArray *cellContents = [_cells objectAtIndex:indexPath.row];
NSUInteger x = 0;
UIFont *font = [UIFont systemFontOfSize:12.0];
for (NSObject *obj in cellContents)
{
if ([obj isKindOfClass:[NSString class]])
{
NSString *text = (NSString *)obj;
CGSize size = [text sizeWithFont:font];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(x, (kCellHeight - size.height)/2.0, size.width, size.height)];
label.text = text;
label.font = font;
[equationContainer addSubview:label];
x += size.width;
}
else if ([obj isKindOfClass:[UIImage class]])
{
UIImage *image = (UIImage *)obj;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(x, (kCellHeight - image.size.height) / 2.0, image.size.width, image.size.height)];
imageView.image = image;
[equationContainer addSubview:imageView];
x += image.size.width;
}
}
return cell;
}
This yields:
Make a UILabel and use instances of that UILabel for each letter.
Use some geometry logic about the rect of the image view to place the letters regardless of size...such as
UILabel *aLetterLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 35, 35)];
aLetterLabel.text = #"A";
//centered top
aLetterLabel.frame = CGRectMake((shapeImage.frame.origin.x + (shapeImage.frame.size.width/2)), shapeImage.frame.origin.y, 35, 35);
//centered
aLetterLabel.frame = CGRectMake((shapeImage.frame.origin.x + (shapeImage.frame.size.width/2)), (shapeImage.frame.origin.y + (shapeImage.frame.size.height/2)), 35, 35);
//cenetered bottom
aLetterLabel.frame = CGRectMake((shapeImage.frame.origin.x + (shapeImage.frame.size.width/2)), (shapeImage.frame.origin.y+(shapeImage.frame.size.height-35)), 35, 35);
//left center align
aLetterLabel.frame = CGRectMake(shapeImage.frame.origin.x, (shapeImage.frame.origin.y + (shapeImage.frame.size.height/2)), 35, 35);
Wrote these up really fast as a proof of concept...feel free to revise, etc.
You can create your own font with the exact symbols you need. Try this:
http://glyphdesigner.71squared.com
You can't insert an image in a UILabel.
But you can add a UIImageView in the custom cells of your UITableView and then put a UIImage inside.
I'm trying to implement a UIScrollView in each cell of `UITableView'. I wrote my code based on Apple's custom Scrollview project: https://developer.apple.com/library/ios/#samplecode/Scrolling/Listings/ReadMe_txt.html
Each table cell's UIScrollView scrolls through a set of labels, which are initialized in the cellForRowAtIndexPath method. Each label's text is set after initialization to equal a string from an array.
Everything works correctly until I scroll down to the 4th table cell. The UIScrollView in this cell has the same exact labels as the first cell. The same first 3 set of labels or the first 3 scrollViews keep repeating after the first 3 cells. The strange part is, when I log the label.text after it is set, the output shows the correct label.text of what should be displaying in the respective cell.
- (void)layoutScrollLabels: (float)arrayCount
{
UIView *view = nil;
NSArray *subviews = [cell.popularLinkScrollView subviews];
// reposition all image subviews in a horizontal serial fashion
CGFloat curXLoc = 0;
for (view in subviews)
{
if ([view isKindOfClass:[UILabel class]] && view.tag >= 0)
{
CGRect frame = view.frame;
frame.origin = CGPointMake(curXLoc, 0);
view.frame = frame;
curXLoc += (kScrollObjWidth);
}
}
[cell.popularLinkScrollView setContentSize:CGSizeMake((arrayCount * kScrollObjWidth), [cell.popularLinkScrollView bounds].size.height)];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Custom Cell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSDictionary *dictionary = [parseDataArray objectAtIndex:indexPath.row];
NSArray *popularLinkTitleArray = [dictionary objectForKey:#"popularLinkTitleArray"];
cell.popularLinkScrollView.clipsToBounds = YES;
kScrollObjHeight = cell.popularLinkScrollView.frame.size.height;
kScrollObjWidth = cell.popularLinkScrollView.frame.size.width;
NSUInteger i;
for (i = 0; i < popularLinkTitleArray.count; i++)
{
NSString *string = [NSString stringWithFormat:#"%#", [popularLinkTitleArray objectAtIndex: i]];
UILabel *label = [[UILabel alloc] init];
label.text = [NSString stringWithFormat:#"%#", string];
label.backgroundColor = [UIColor clearColor];
label.numberOfLines = 5;
NSLog(#"%#", label.text);
// setup each frame to a default height and width, it will be properly placed when we call "updateScrollList"
CGRect rect = label.frame;
rect.size.height = kScrollObjHeight;
rect.size.width = kScrollObjWidth;
label.frame = rect;
label.tag = i; // tag our images for later use when we place them in serial fashion
[cell.popularLinkScrollView addSubview:label];
}
[self layoutScrollLabels:popularLinkTitleArray.count];
}
return cell;
}
You should not add label as a subview to popularLinkScrollView in cellForRowAtIndexPath. If the count of labels is required to be dynamic, try this code before your for loop in cellForRowAtIndexPath.
for (UIView* subView in cell.popularLinkScrollView.subviews)
[subView removeFromSuperview];
I have a UITableView and a UITableCell subclass. Each table cell has two scrollviews that each rotate a dynamic amount of labels, which are created in my table cell implementation. I have poor scrolling performance and I believe it is from memory leaks. I'm referencing this stackoverflow correct answer to fix my problem: cellForRowAtIndexPath memory management.
I can't figure out how to tweak my code so I don't have to allocate memory every time I create labels.
ViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Custom Cell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSDictionary *dictionary = [parseDataArray objectAtIndex: indexPath.row];
NSArray *popularLinkTitleArray = [dictionary objectForKey:#"popularLinkTitleArray"];
NSString *soundCloudLink = [dictionary objectForKey:#"soundCloudLink"];
NSArray *soundCloudTrackTitleArray = [dictionary objectForKey:#"soundCloudTrackTitleArray"];
cell.artistNameLabel.text = artistName;
[cell createPopularLinkLabels: popularLinkTitleArray];
return cell;
}
CustomCell.m
- (void)layoutScrollLabelsForPopularLinkScrollView: (float)arrayCount
{
UIView *view = nil;
NSArray *subviews = [popularLinkScrollView subviews];
// reposition all image subviews in a horizontal serial fashion
CGFloat curXLoc = 0;
for (view in subviews)
{
if ([view isKindOfClass:[UILabel class]] && view.tag >= 0)
{
CGRect frame = view.frame;
frame.origin = CGPointMake(curXLoc, 0);
view.frame = frame;
curXLoc += (kScrollObjWidth);
}
}
[popularLinkScrollView setContentSize:CGSizeMake((arrayCount * kScrollObjWidth), [popularLinkScrollView bounds].size.height)];
}
-(void) createPopularLinkLabels:(NSArray *) popularLinkTitleArray
{
popularLinkScrollView.clipsToBounds = YES;
kScrollObjHeight = popularLinkScrollView.frame.size.height;
kScrollObjWidth = popularLinkScrollView.frame.size.width;
for (UIView* subView in popularLinkScrollView.subviews)
[subView removeFromSuperview];
NSUInteger i;
for (i = 0; i < popularLinkTitleArray.count; i++)
{
NSString *string = [NSString stringWithFormat:#"%#", [popularLinkTitleArray objectAtIndex: i]];
UILabel *label = [[UILabel alloc] init];
label.text = [NSString stringWithFormat:#"%#", string];
label.backgroundColor = [UIColor clearColor];
label.numberOfLines = 5;
[label setFont:[UIFont fontWithName:#"Calibri" size:18]];
// setup each frame to a default height and width, it will be properly placed when we call "updateScrollList"
CGRect rect = label.frame;
rect.size.height = kScrollObjHeight;
rect.size.width = kScrollObjWidth;
label.frame = rect;
label.tag = i; // tag our images for later use when we place them in serial fashion
[popularLinkScrollView addSubview:label];
}
[self layoutScrollLabelsForPopularLinkScrollView:popularLinkTitleArray.count];
}
Are you using ARC? If not, you should autorelease to
cell = [[[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]
Cell objects are reused and you do not need to re-add subviews each time you change the contents. Just change the data and update the label frames if needed.
If you have distinctly different cells, consider using separate custom cell classes. You will need to use a separate value for the reuseIdentifier.
Also consider not creating a custom cell but just adding elements to the standard UITableViewCell. Read this about styling a cell.
The first thing you could do is try to reuse UILabels in createPopularLinkLabels. Right now, you are just removing them before adding more.
But if the number of popular links is not the same in all the cells, then you will have to remove the remaining ones or hide them and there will be no way to avoid memory allocations.
A better solution would be to insert a custom UIView inside your scroll view and draw all the texts manually instead of creating tons of subviews.
Hope this helps,
Thomas
I have made a prototype cell in Interface Builder. This prototype contains an imageview which should change size depending on the length of a string.
Therefore I have written a method in my subclass of UITableViewCell to calculate the width of the text string with a given font and set this width to the width of the imageview.
This works fine until I push to another view controller and then go back to my table view. Then the view has been repositioned about 20 pixels to the right.
Does anyone know why this happens? This even happens if I set the x-position to something static, e.g. 200.
Update 1:
In my - (UITableViewCell *)tableView:cellForRowAtIndexPath: I use my subclass of UITableViewCell, DFolderCell. This calls the method - (void)updateIndicatorsCount:andStarred which sets the value of the count and the starred label and makes those labels and the images countIcon and starredIcon the width to the width of _count and _starred with a given font.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"folderCell";
DFolderCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell = [[DFolderCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
int index = indexPath.row;
Folder *folder = (Folder *) [folders objectAtIndex:index];
FolderIcon icon = (FolderIcon) folder.icon.intValue;
cell.name.text = folder.name;
cell.icon.image = [[DFolders sharedFolders] imageForFolderIcon:icon];
int count = [[DDrafts sharedDrafts] amountOfDraftsInFolder:folder withType:DraftLoadTypeRecent];
int starred = [[DDrafts sharedDrafts] amountOfDraftsInFolder:folder withType:DraftLoadTypeStarred];
[cell updateIndicatorsCount:count andStarred:starred];
return cell;
}
The method below is part of my subclass DFolderCell
/* Update size of count and starred indicators */
- (void)updateIndicatorsCount:(int)_count andStarred:(int)_starred
{
UIFont *badgeFont = [UIFont systemFontOfSize:11.0];
UIImage *background;
_count = 1000; // Test
if (_count > 0)
{
self.count.text = [NSString stringWithFormat:#"%i", _count];
CGSize size = [self.count.text sizeWithFont:badgeFont];
CGRect frame = self.countIcon.frame;
frame.size.width = size.width;
frame.origin.x = 320 - 20 - size.width;
self.countIcon.frame = frame;
self.count.frame = frame;
background = [UIImage imageNamed:#"Folders_Badge_Count.png"];
self.countIcon.image = [background stretchableImageWithLeftCapWidth:2 topCapHeight:2];
self.count.hidden = NO;
self.countIcon.hidden = NO;
}
/* Code for _starred is similar to code for _count */
}
This is the result.
This is how it should always look (and how it looks before selecting a cell)
When a cell has been selected, a new viewcontroller has been pushed onto the navigation stack and I have popped this view controller by going back, this is how it looks.
Update 2:
I have kept the imageview in the cell prototype but commented out the code setting its size and its image to a stretchable image. I have also changed the background color of the label to a solid red to see the exact size of the label. The size seems to be correct.
This is my - (void)updateIndicatorsCount:andStarred: method now.
- (void)updateIndicatorsCount:(int)_count andStarred:(int)_starred
{
UIFont *badgeFont = [UIFont systemFontOfSize:11.0];
// UIImage *background;
_count = 1000;
if (_count > 0)
{
self.count.text = [NSString stringWithFormat:#"%i", _count];
CGSize size = [self.count.text sizeWithFont:badgeFont];
CGRect frame = self.countIcon.frame;
frame.size.width = size.width;
frame.origin.x = 320 - 30 - size.width;
self.count.frame = frame;
self.count.hidden = NO;
self.count.backgroundColor = [UIColor redColor];
/* DON'T DO ANYTHING TO THE IMAGE */
/*
background = [UIImage imageNamed:#"Folders_Badge_Count.png"];
self.countIcon.hidden = NO;
self.countIcon.image = [background stretchableImageWithLeftCapWidth:2 topCapHeight:2];
self.countIcon.frame = frame;
*/
}
}
Before pushing to another view controller:
After going back from the view controller:
Use this code in your cellForRowatindexpath it will solve your issue
[[[cell contentView] subviews] makeObjectsPerformSelector: #selector(removeFromSuperview)];
im having trouble loading images into a uitable view from an xml file, i have everything loading correctly, the problem is in the uitableview instead of loading the images 0-9 it loads only 0-7 then images 0 and 1 ?
attached is the project, any help would be appreciated
thanks
http://jptarry.com/iPhoneTest/iphoneGrid.zip
I hope i'm right. I don't have a mac right now to test this, but I've seen some errors in the code.
In MainViewController when you are reusing the cell, you simply return the old cell without any change.
I think this is why you get 0-7 then 0-1 again. Most probably there are 7 cells visible on the screen and when you start scrolling you are simply returning the dequeued cells (which are 0 and 1 because you are scrolling down and those cells are out of the screen).
When reusing a cell, you should reinitialize the asyncImageView with the image for the current index. I'll copy-paste some code but this is more as a guideline because i can't compile/test it:
Also, you are attaching the same tag to multiple asyncImageViews you create in the same cell. You won't be able to get all of them back. Attach ids based on the index or something.
EDIT:
This is how i got it working. However, I really don't understand the logic behing your code where you arrange the buttons in a cell.
Also, there is still an issue with AsyncImageView. While it is loading the new image, it is displaying the old one - this is why you will see that when you scroll the old images first appear with a activity indicator, then the new images appear. You should have a loading state for AsyncImageView where it displays something neutral.
Also, I'm really happy with the tagging method, but it works :)
Ok, enough talk, here's the code i changed:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
AsyncImageView *asyncImageView = nil;
int section = indexPath.section;
NSMutableArray *sectionItems = [sections objectAtIndex:section];
int n = ([sectionItems count]) ;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
// Create a new cell
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryNone;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
int i=0;
int f=0;
while (i < [sectionItems count])
{
int yy = 4 +f*74;
//number of items per section 2
for(int j=0; j<[sectionItems count]; j++)
{
Item *item = [sectionItems objectAtIndex:j];
int buttonX = 10;
if (j != 0)
buttonX = 203;
NSLog(#"+++++++++++++++++++++++++++++++");
NSLog(#"image :: %d :: %#", j, item.image);
NSLog(#"+++++++++++++++++++++++++++++++");
CGRect frame;
frame.origin.x = buttonX;
frame.origin.y = yy;
frame.size.width = 107;
frame.size.height = 107;
asyncImageView = [[[AsyncImageView alloc] initWithFrame:frame] autorelease];
asyncImageView.backgroundColor = [UIColor clearColor];
asyncImageView.tag = ASYNC_IMAGE_TAG + f * 100 + j;
//asyncImageView.opaque = YES;
[cell.contentView addSubview:asyncImageView];
i++;
}
f = f+1;
}
}
else
{
NSLog(#"cell != nill %#", cell);
}
int i=0;
int f=0;
while (i < [sectionItems count])
{
//number of items per section 2
for(int j=0; j<[sectionItems count]; j++)
{
Item *item = [sectionItems objectAtIndex:j];
asyncImageView = (AsyncImageView *) [cell.contentView viewWithTag:ASYNC_IMAGE_TAG + f * 100 + j];
[asyncImageView loadImageFromURL:[NSURL URLWithString:item.image]];
i++;
}
f = f+1;
}
return cell;
}
EDIT2: I made a quick fix to AsyncImageView to remove the old image when loading a new one. I'll just post some code excerpts with the changes:
-(void)loadImageFromURL:(NSURL*)url {
// ADD THE FOLLOWING LINES
// Remove the old image (tagged with 1234)
[[self viewWithTag:1234] removeFromSuperview];
// END ADD
if (connection != nil) {
NSLog(#"connection != nil");
[connection cancel];
[connection release];
A little lower in the same method, where you create the image from cache, add those lines:
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.autoresizingMask =
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
// ADD THE NEXT LINE
imageView.tag = 1234; // add this line
// END ADD
[self addSubview:imageView];
And in another function, - (void)connectionDidFinishLoading:(NSURLConnection *)aConnection where you create the new image, just add the tag again:
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.autoresizingMask =
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
// ADD THE NEXT LINE
imageView.tag = 1234;
// END ADD
[self addSubview:imageView];
imageView.frame = self.bounds;