I have tableview and I want to show all images from one folder inside directory and I have path to that directory. I am loading image to cell like:
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
for (UIView * view in cell.contentView.subviews){
if ([view isKindOfClass:[UIButton class]]) {
[view removeFromSuperview];
}
}
cell.imageView.image = nil;
cell.accessoryType = UITableViewCellAccessoryNone;
NSData *imgData = [NSData dataWithContentsOfFile:absoluteImagePath];
UIImage *thumbNail = [[UIImage alloc] initWithData:imgData];
UIImageView *imageView = [[UIImageView alloc] initWithImage:thumbNail];
[cell insertSubview:imageView atIndex:0];
}
but when I scroll up and down I get image over image inside cell (old image is visible below new, when I scroll on for example first position I see image for index 0 below other image ).
Does anyone know what is a problem, I am new to this iOS programming ?
You should
subclass your cell,
create an outlet for the image, and
design the layout in storyboard.
The benefits:
No need to check for (cell==nil)
You will have to write less code
Cleaner solution without redundant view creation errors
When the cell is reused, [cell insertSubview:imageView atIndex:0]; adds an additional UIImageView to the cell. Suggestion is to create a custom UITableViewCell and in the prepareForReuse method, remove the UIImageView. An easy way to identify UIImageView to remove is to add a tag to it. E.g.,
imageView.tag = 1000;
Then you can find the image view with
UIImageView *imageView = [self.contentView viewWithTag:1000];
if (imageView) {
[imageView removeFromSuperview];
}
Declare the UIImageView where you are initializing the cell.
For example
if (cell==nil)
{
UIImageView *image = [[UIImageView alloc]initWithFrame:yourFrame];
[cell addSubView:image];
}
image.image = yourImage;
Alternatively, you can remove all subviews from the cell before adding new UIImageView as subview.
Related
This question already has answers here:
UITableView Separator line
(14 answers)
Closed 9 years ago.
In my table view, I need to set the cell separator as an image that I have with me. How can I do this?
A simple solution is you can set a pattern image as cell seperator.
[tableView setSeparatorColor:[UIColor colorWithPatternImage:[UIImage imageNamed:...]]];
Or you can simply add a UIImageView in the cell content view
UIImageView *imagView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"seprater.png"]];
imgView.frame = CGRectMake(0,cell.contentView.frame.size.height - 1.0, cell.contentView.frame.size.width, 1);
[customCell.contentView addSubview:imageView];
Your best bet is probably to set the table's separatorStyle to UITableViewCellSeparatorStyleNone.
And manually adding/drawing a line (perhaps in tableView:cellForRowAtIndexPath:) when you want it.
In tableView:cellForRowAtIndexPath:
...
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
// Drawing our own separatorLine here because I need to turn it off for the
// last row. I can only do that on the tableView and on on specific cells.
// The y position below has to be 1 less than the cell height to keep it from
// disappearing when the tableView is scrolled.
UIImageView *separatorLine = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, cell.frame.size.height - 1.0f, cell.frame.size.width, 1.0f)];
separatorLine.image = [[UIImage imageNamed:#"grayDot"] stretchableImageWithLeftCapWidth:1 topCapHeight:0];
separatorLine.tag = 4;
[cell.contentView addSubview:separatorLine];
[separatorLine release];
}
// Setup default cell settings.
...
UIImageView *separatorLine = (UIImageView *)[cell viewWithTag:4];
separatorLine.hidden = NO;
...
// In the cell I want to hide the line, I just hide it.
seperatorLine.hidden = YES;
...
In viewDidLoad:
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
to active this put an image view at the bottom of your cell and assign an image to it .also please make sure you have set cellSeperatorStyle as None
That should work
{ [self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UIImageView *imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"<image name>"]];
imgView.frame = CGRectMake(0, cellHeight, cellWidth, 1);
[customCell.contentView addSubview:imgView];
return customCell;
}
}
First of all you have to make SeparatorStyle to none by following code
[self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
after that add image too the end of every cell
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
.....
UIImageView *imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"seprater.png"]];
[imgView sizeToFit];
[Cell.contentView addSubview:imgView];
.....
}
My UITableView is pulling data from an array. Items in this array have a property called IsSelected. I am trying to put a UIImageView in the cell contentView for each item that is selected.
The UITableView however when reusing the cell is causing my image to be reused on cells that it shouldn't. I cannot figure out for the life of me how I should be doing it differently. I have attached a screen shot showing the issue. If I keep scrolling up and down the image goes all over the place:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *SchoolCellIdentifer = #"SchoolCellIdentifier";
SchoolInfoItem *item = [self.schoolsArray objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SchoolCellIdentifer];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SchoolCellIdentifer];
cell.contentView.backgroundColor = [BVColors WebDarkBlue];
}
cell.textLabel.text = item.Name;
if ([item.selected isEqualToString:#"1"])
{
cell.contentView.backgroundColor = [BVColors WebBlue];
UIImageView *selectedItemCheckMarkIcon = [[UIImageView alloc] initWithFrame:CGRectMake(300, 13, 17, 17.5)];
[selectedItemCheckMarkIcon setImage:[UIImage imageNamed:#"check-mark.png"]];
[cell.contentView addSubview:selectedItemCheckMarkIcon];
}
else
{
cell.contentView.backgroundColor = [BVColors WebDarkBlue];
}
return cell;
}
You need to make sure the UIImageView is getting removed from the cell content view. It looks like in your code when a cell gets dequeued that imageview is still in the cells view hierarchy.
Best solution is to have your cell hold onto a reference to the image view and remove it when necessary.
Take the following:
if ([item.selected isEqualToString:#"1"])
{
cell.contentView.backgroundColor = [BVColors WebBlue];
cell.myImageView = [[UIImageView alloc] initWithFrame:CGRectMake(300, 13, 17, 17.5)];
[selectedItemCheckMarkIcon setImage:[UIImage imageNamed:#"check-mark.png"]];
[cell.contentView addSubview:cell.myImageView];
}
else
{
[cell.myImageView removeFromSuperview];
cell.myImageView = nil;
cell.contentView.backgroundColor = [BVColors WebDarkBlue];
}
Note in the else case the removal of the imageview.
You keep adding a the UIImageView as a subview on the cell's contentView. This isn't removed when the table view is reusing the cell. You'll need to remove the subview if it shouldn't appear.
You should make selectedItemCheckMarkIcon a property on your UITableViewCell subclass. And then have a method in your subclass where you set the image or visibility of the image accordingly.
You could also use the accessoryView property on the UITableView and set the imageView as that:
if ([item.selected isEqualToString:#"1"]) {
cell.contentView.backgroundColor = [BVColors WebBlue];
UIImageView *selectedItemCheckMarkIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"check-mark.png"]];
cell.accessoryView = selectedItemCheckMarkIcon;
} else {
cell.accessoryView = nil;
cell.contentView.backgroundColor = [BVColors WebDarkBlue];
}
Note that you don't need to set a frame in this case because the system will automatically set the frame correctly for the accessoryView.
I encountered a weird behavior of UIScrollView when inside a UICollectionViewCell. In my project I have a collectionView and each cell has a UIScrollView that contains 1 or more images. Here's some sample code for purpose of this question:
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CustomCollectionCell * cell = [cv dequeueReusableCellWithReuseIdentifier:#"CustomCell" forIndexPath:indexPath];
UIImageView * imageView = [[UIImageView alloc] initWithFrame:CGRectMake(20, 20, 150, 150)];
imageView.image = [UIImage imageNamed:#"someImage.png"];
[cell.scrollView addSubview:imageView];
return cell;
}
Of course in my project it's not the same image but different ones that are fed from a database.
The first collection cell is added with no problem. When I add the second cell, the scrollView seems to duplicate itself and place another instance of itself on top. I know it sounds weird, but you can see how it looks in the image below:
Notice how the image in the scrollView on the left has darkened, I assume it's because the scrollView get duplicated.
So, I'm guessing that the cell is reused in a wrong way or something.
Thanks for your help!
A new UIImageView is being added to the reused cell each time collectionView: cellForItemAtIndexPath: is invoked. To solve this, in the custom UICollectionViewCell, implement the prepareForReuse method. In that method, remove the image view.
To accomplish this, you could set a tag on the image view. E.g.,:
(UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CustomCollectionCell * cell = [cv dequeueReusableCellWithReuseIdentifier:#"CustomCell" forIndexPath:indexPath];
UIImageView * imageView = [[UIImageView alloc] initWithFrame:CGRectMake(20, 20, 150, 150)];
imageView.image = [UIImage imageNamed:#"someImage.png"];
[cell.scrollView addSubview:imageView];
// The following line of code is new
cell.contentView.tag = 100;
return cell;
}
And to remove the image view from the cell, add/update the prepareForReuse method in the custom cell:
-(void)prepareForReuse{
UIView *myImageView = [self.contentView viewWithTag:100];
[myImageView removeFromSuperview];
}
i have a TableView with custom table cells. I add programmatically borders at the bottom of each cell to keep the screendesign layout. Everything is fine, when the App loads the first time. But after scrolling (and scrolling back to the top) multiple border lines are displayed all over the screen.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{ static NSString *CellIdentifier = #"CellProgramm";
ProgrammTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
...
if([object.cellMessageArray[1] isEqualToString:#"wrapper"] || [object.cellMessageArray[1] isEqualToString:#"keynote"] || [object.cellMessageArray[1] isEqualToString:#"break"]) {
UIImageView *lineSeparator = [[UIImageView alloc] initWithFrame:CGRectMake(0, cell.bounds.size.height, 1024, 5)];
lineSeparator.image = [UIImage imageNamed:[NSString stringWithFormat:#"blind.png" ]];
lineSeparator.backgroundColor = [UIColor whiteColor];
[cell.contentView addSubview:lineSeparator];
}
else if([object.cellMessageArray[1] isEqualToString:#"standard"]) {
UIImageView *lineSeparator = [[UIImageView alloc] initWithFrame:CGRectMake(60, cell.bounds.size.height+4, 1024, 1)];
lineSeparator.image = [UIImage imageNamed:[NSString stringWithFormat:#"blind.png" ]];
lineSeparator.backgroundColor = [UIColor pxColorWithHexValue:#"eeeeee"];
[cell.contentView addSubview:lineSeparator];
}
}
Has anyone an idea?
When you scroll a tableview, the cells are reused (dequeueReusableCellWithIdentifier) to optimize performance. In the code above, a lineSeparator image view is added to the cell each time the cellForRowAtIndexPath method is invoked. If the cell is used 5 times, it will have 5 image views added.
One way address this is to remove the lineSeparator image view from the cell before it is reused. This is typically done in the cell's prepareForReuse method.
In the cellForRowAtIndexPath, add a tag to the lineSeparator image view (e.g., lineSeparator.tag = 100;
In your cell's class, implement the prepareForReuse method. E.g.:
-(void)prepareForReuse{
UIView *lineSeparatorView = [self.contentView viewWithTag:100];
[lineSeparatorView removeFromSuperview];
}
I've got a UITableViewCell and I need to randomly put interstitial images into some of them. This is working, but due to the caching of the cells, the images are repeating themselves further down the table. Let's say I've pre-decided to put images in cells 1, 12, and 25, those cells will have their images, but after cell 1, cells 4, 8, etc. might also contain that first image.
I'm guessing what I need to do is specifically tell cellForRowAtIndexPath clear the cache and remove any images if it doesn't meet the criteria to add an image. How would I go about doing that? I've already tried an else on the if statement that makes the decision and assigns the image. In that else, I've tried setting the UIImageView's image to nil, but that hasn't made any difference.
Is there any other way I can clear the cache to prevent these images from spamming their way down the page at regular intervals? Here's my cellForRowAtIndexPath method, and the lines currently in that last else aren't having an effect on the images, so they'll (I think) need replacing with something that works!
- (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath
{
// Make and allocate the cell if necessary.
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier: #"Cell"];
if (cell == nil) {
cell = [[CustomCell alloc] initWithStyle: UITableViewCellStyleValue1 reuseIdentifier: #"Cell"];
}
cell.lineLabel.text = [[self.fetchedResultsController objectAtIndexPath: indexPath] line];
cell.actorLabel.text = [[self.fetchedResultsController objectAtIndexPath: indexPath] actor];
[cell.lineLabel sizeToFitMultipleLines];
// Get the size each label needs to be when constrained to set width and very long height (8000).
CGSize labelSize = [cell.lineLabel.text sizeWithFont: cell.lineLabel.font constrainedToSize: CGSizeMake(265, 8000)];
if ([[cellsToContainInterstitialImages allKeys] containsObject: [thisLine.lineID stringValue]]) {
UIImageView *interstitialImage = [[UIImageView alloc] init];
NSNumber *cellTypeNumber = [cellsToContainInterstitialImages valueForKey: [thisLine.lineID stringValue]];
kInterstitialCellType cellType = [cellTypeNumber intValue];
if (cellType == kInterstitialCellTypeFirst) {
interstitialImage = [[UIImageView alloc] initWithImage: [UIImage imageNamed: #"man.png"]];
}
else if (cellType == kInterstitialCellTypeSecond) {
interstitialImage = [[UIImageView alloc] initWithImage: [UIImage imageNamed: #"woman.png"]];
}
else {
interstitialImage = [[UIImageView alloc] initWithImage: [UIImage imageNamed: #"child.png"]];
}
[interstitialImage setFrame: CGRectMake(10, cell.lineLabel.frame.size.height + cell.actorLabel.frame.size.height + 10, 150, 150)];
[cell addSubview: interstitialImage];
} else {
UIImageView *interstitialImage = [[UIImageView alloc] init];
[interstitialImage setImage: nil];
}
return cell;
}
The else branch of your code does not add the interstitialImage image to the cell. You should change your code to add the UIImageView in the constructor of the cell, so that in the cellForRowAtIndexPath you'd have to set the image, rather than adding subviews.
Add the UIImageView to your CustomCell, make it private, and provide a property for the cellForRowAtIndexPath to use:
#interface CustomCell : UITableViewCell {
UIImageView *_interstitialImage;
}
#property (nonatomic,readonly) UIImageView *interstitialImage;
#end
#implementation CustomCell
-(id)initWithStyle:(UITableViewCellStyle) style reuseIdentifier:(NSString*)reuseId {
if (self = [super initWithStyle:style reuseIdentifier:reuseId]) {
_interstitialImage = [[UIImageView alloc] init];
[_interstitialImage setImage: nil];
[_interstitialImage setFrame: CGRectMake(10, cell.lineLabel.frame.size.height + cell.actorLabel.frame.size.height + 10, 150, 150)];
[cell addSubview: _interstitialImage];
}
return self;
}
-(UIImageView*)interstitialImage {
return _interstitialImage;
}
#end
Now your cellForRowAtIndexPath would use cell.interstitialImage instead of allocating the new UIImageView all the time. This would avoid adding the image multiple times:
if ([[cellsToContainInterstitialImages allKeys] containsObject: [thisLine.lineID stringValue]]) {
NSNumber *cellTypeNumber = [cellsToContainInterstitialImages valueForKey: [thisLine.lineID stringValue]];
kInterstitialCellType cellType = [cellTypeNumber intValue];
if (cellType == kInterstitialCellTypeFirst) {
[cell.interstitialImage setImage:[UIImage imageNamed: #"man.png"]];
} else if (cellType == kInterstitialCellTypeSecond) {
[cell.interstitialImage setImage:[UIImage imageNamed: #"woman.png"]];
} else {
[cell.interstitialImage setImage: [UIImage imageNamed: #"child.png"]];
}
} else {
[cell.interstitialImage setImage: nil];
}
The easiest solution should be to use – tableView:willDisplayCell:forRowAtIndexPath: method of UITableViewDelegate Protocol. Clean your cells in this method.
The problem is that in the if part you add an image view. This means as cells are reused you keep adding more and more images views.
In the else part you create an dummy image view and set its image to nil. This code does not do what you intended - to clear any existing image.
What you need to do is to ensure you never add a second image view to a cell. And when you need to clear the image, you get a reference to the existing image view, not create a new one.