I have an error with my custom UITableViewCell image not showing up until the UITableView is scrolled. It dosnt happen to all of the cells only the next one comming on or the most recent one comming off (for instane if you see half of the cell on the bottom you cannot see the image until you scroll it all the way into view)
So I have decided that maybe I need to do a better job of relating my UIImages to the correct custom cell.
I have created a UIImageView called "firstImageView" I then go into my interface builder and relate that to the correct UIImageView.
Then inside tableview:cellForRowAtIndexPath: I try to set it like this
myCustomCell.firstImageView.image = [UIImage imageNamed:#"SMILE.png"];
However I get this error
Property 'firstImageView' not found on object of type 'UITableViewCell *'
Any help would be appreciated
To address your error with upcoming images, here is my answer: When you get your UITableViewCell like this
MyCustomCell *myCustomCell = [self.tableView dequeueReusableCellWithIdentifier:#"myCustomCellIdentifier"];
you can implement -(void)prepareForReuse method of your override of UITableViewCell. Otherwise it is also possible to set the custom content in your -(UITableViewCell *)tableView:cellForRowAtIndexPath: method after dequeuing your cell.
You can get further information here:
How to use prepareForUse-Method and Apple Documentation of UITableViewCell
To address the other problem with unknown class attribute: What type is your myCustomCell? In the interface builder you have to assign the correct type and your myCustomCell also has to be of the correct type.
here 122,111 are THE frames for image view in table view cell replace it with yours ``
UIGraphicsBeginImageContextWithOptions(itemSize, NO,UIScreen.mainScreen.scale); ˚˚CGSize itemSize = CGSizeMake(122,111);
CGRect imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height);
[imageView.image drawInRect:imageRect];
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
When dequeueing the cell are you casting it as your custom cell class?
MyCustomCellClass *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
It looks like you created a custom UITableViewCell subclass but you don't dequeue it using the Identifier you assigned in IB ("reuse identifier")..
Related
I was using awakeFromNib to set an image with this code
self.cellImage01 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"mushroom_risotto.jpg"]];
self.cellImage01.frame = CGRectMake(0.0, 0.0, self.frame.size.width, self.frame.size.height);
self.cellImage01.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
self.cellImage01.contentMode = UIViewContentModeScaleAspectFit;
Then I needed indexPath to get the image info from it's data source so I moved the code from awakeFromNib to cellForItemAtIndexPath. Now it displays differently
it doesn't have the same scaleAspectFit
going to landscape changes it to a small slice of the pic.
The small slice image only happens when the cell goes out of view, then back into view.
I'm using cell reuse and that seems to be where things are changing, but I can't access the code for that to see what's going on.
Does anyone know the call stack it goes thru for cell reuse?
Why would the aspect work in awakeFromNib and not in cellForItemAtIndexPath?
The common element here is that cellForItemAtIndexPath calls this first:
ArticleCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"ArticleCollectionViewCell" forIndexPath:indexPath];
Edit: I should have noted that it displays properly when called in awakeFromNib.
I have used a simple prototype cell from storyboard for adding header view to my table view. I added some labels in cell and give specific tag to every label, but while accessing it returns me nil value for [cell viewWithTag :] method.(I have not created a custom class for cell) I am using following method
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UITableViewCell * headerView = [tableView dequeueReusableCellWithIdentifier:#"HeaderView"];
UILabel * lblVenueName = (UILabel *)[headerView viewWithTag:100];
lblVenueName.text = #"Test Venue";
return headerView;
}
for above code the value for lblVenueName always return nil.
I ran into this problem in XCode 7.0 beta 5 (7A176x). I suppose it's a bug. I've checked in debugger and turns out that cell contentView does not have any subviews at runtime.
If you are designing for some specific screen size then this workaround may help you. Switch to the base values like this:
Then in attributes inspector check that both "installed" options are checked on all views that you need and their superviews (for example for all views in prototype cell, table view and it's superview):
After that you can return to your specific screen size and viewWithTag() will return correct values.
(id)dequeueReusableCellWithIdentifier:(NSString *)identifier
Return Value
A UITableViewCell object with the associated identifier or nil if no such object exists in the reusable-cell queue.
So I'm guessing that method returns nil for you :) Try a version that takes a indexPath :)
How can I display icons to the left of the UITableViewCell in UITableView?
The Mail app does this, and I'm wondering if this is a native feature that comes with iOS, or one that need a custom implementation.
Thanks!
If you have Custom UITableViewCell then you can implement layoutSubviews as follows:
- (void)layoutSubviews
{
[super layoutSubviews];
self.imageView.bounds = CGRectMake(5, 5, 15, 15);
self.imageView.frame = CGRectMake(5, 5, 15, 15);
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
}
I have used this one. It works. Hope this helps you.
You need to extend UITableViewCell with a class of your own.
In this new class of yours you can have your own layout and complete control of what you want the cell to look like. On interface builder for this cell you can have the image and just add a method to display/hide such image.
When implementing your UITableView you will need to specify your cell identifier for your new class so it loads the right nib.
static NSString *cellIdentifier = #"MyCustomCellClass";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
The credit should really go to #rdelmar, but as he gave the solution in in a comment rather than an answer, I'm posting this for other people:
That image is not "left of the cell", it's part of the cell. With a
custom cell, you can put your content where you want, as well as inset
the separator so it looks like the cell doesn't go all the way to the
left edge of the screen
I'm having an issue,
I have a simple UICollectionView with a static 200 cells that load images from Flickr.
my CellForItemAtIndexPath looks like this:
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"FlickrCell" forIndexPath:indexPath];
cell.backgroundColor = [self generateRandomUIColor];
if(![[cell.subviews objectAtIndex:0] isKindOfClass:[PFImageView class]])
{
NSURL *staticPhotoURL = [self.context photoSourceURLFromDictionary:[self.photos objectAtIndex:indexPath.row] size:OFFlickrSmallSize];
PFImageView *imageView = [[PFImageView alloc] initWithFrame:CGRectMake(0, 0, cell.frame.size.height, cell.frame.size.width) andImageURL:staticPhotoURL andOwningCell:cell];
[cell addSubview:imageView];
}
return cell;
}
PFImageView is a subclass of UIImageView that loads a Flickr photo URL on a background thread and then updates it's own image on the main thread - this works fine.
The logic is really simple - I create a cell if there isn't one dequeueable.
If the cell (which I'm expecting to be dequeued and already have a PFImageView) doesn't have a PFImageView, I alloc and init an imageView for the cell and add it as a subview of the cell.
Thus I expect if the cell has been dequeued it should already have a PFImageView as a subview and as we should not get into the if statement to create a new imageView and kick off a new photo download request
Instead what I see is that the cells at the top and bottom of the UICollectionView that 'go off screen' momentarily - when they come back on screen they are not being reused and seemingly a new cell is created and the picture refreshed.
1) How can I achieve a static image once the cell has been created (i.e. not refreshing when the cell goes slightly off screen.
2) Why are the cells not being reused?
Many thanks for your time.
John
UICollectionView will reuse cells for maximum efficiency. It does not guarantee any particular reuse or population strategies. Anecdotally, it seems to place and remove cells based on integer power of two regions — e.g. on a non-retina iPad it might divide your scroll area up into regions of 1024x1024 and then populate and depopulate each of those regions as they transition into and out of the visible area. However you should not predicate any expectations on its exact behaviour.
In addition, your use of collection view cells is incorrect. See the documentation. A cell explicitly has at least two subviews — backgroundView and contentView. So if you add a subview it will be at index 2 at the absolute least and, in reality, the index will be undefined. In any case you should add subviews to contentView, not to the cell itself.
The most normal way of doing what you're doing would be to create a custom UICollectionView subclass that inherently has a PFImageView within it.
I see several potential issues:
You are looking specifically at index 0 of the cell for the child class that you are adding. The UICollectionViewCell may have other views as children, so you can't just assume that the only (or first) child is the one you added.
I don't see that you are calling registerClass:forCellWithReuseIdentifier: or registerNib:forCellWithReuseIdentifier:, one of which is required for proper use of dequeue (https://developer.apple.com/library/ios/documentation/uikit/reference/UICollectionViewCell_class/Reference/Reference.html).
You are only setting the URL of the PFImageView in the case that you have to construct the PFImageView. The idea with dequeuing reusable views is that you will only construct a small subset of the views needed, and the UITableView will recycle them as they move offscreen. You need to reset the value for the indexPath that is being requested, even when you don't construct the new content.
If your case is as simple as you describe, you can probably get away with adding your PFImageView to the contentView property of your dequeued UICollectionView.
In your controller:
// solve problem 2
[self.collectionView registerClass:[UICollectionViewCell class] forReuseIdentifer:#"FlickrCell"];
In collectionView:cellForItemAtIndexPath
UICollectionViewCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"FlickrCell" forIndexPath:indexPath];
cell.backgroundColor = [self generateRandomUIColor];
// solve problem 1 by looking in the contentView for your subview (and looping instead of assuming at 0)
PFImageView *pfImageView = nil;
for (UIView *subview in cell.contentView.subviews)
{
if ([subview isKindOfClass:[PFImageView class]])
{
pfImageView = (PFImageView *)subview;
break;
}
}
NSURL *staticPhotoURL = [self.context photoSourceURLFromDictionary:[self.photos objectAtIndex:indexPath.row] size:OFFlickrSmallSize];
if (pfImageView == nil)
{
// No PFImageView, create one
// note the use of contentView!
pfImageView = [[PFImageView alloc] initWithFrame:CGRectMake(0, 0, cell.contentView.frame.size.height, cell.frame.size.width) andImageURL:staticPhotoURL andOwningCell:cell.contentView];
[cell.contentView addSubview:pfImageView];
}
else
{
// Already have recycled view.
// need to reset the url for the pfImageView. (Problem 3)
// not sure what PFImageView looks like so this is an e.g. I'd probably remove the
// URL loading from the ctr above and instead have a function that loads the
// image. Then, you could do this outside of the if, regardless of whether you had
// to alloc the child view or not.
[pfImageView loadImageWithUrl:staticPhotoURL];
// if you really only have 200 static images, you might consider caching all of them
}
return cell;
For less simple cases (e.g. where I want to visually lay out the cell, or where I have multiple children in the content), I typically customize my UICollectionViewCell's using Interface Builder.
Create a subclass of UICollectionViewCell in the project (In your case, call it PFImageCell).
Add an IBOutlet property to that subclass for the view I want to change in initialization (In your case, a UIImageView).
#property (nonatomic, assign) IBOutlet UIImageView *image;
In Interface Builder, create a prototype cell for the UITableView.
In the properties sheet for that prototype cell, identify the UICollectionViewCell subclass as the class.
Give the prototype cell an identifier (the reuse identifier) in the property sheet.
Add the view child in interface builder to the prototype cell (here, a UIImageView).
Use IB to map the IBOutlet property to the added UIImageView
Then, on dequeue in cellForRowAtIndexPath, cast the dequeued result to the subclass (PFImageCell) and set the value of the IBOutlet property instance. Here, you'd load the proper image for your UIImageView.
I am not sure if the cell is being re-used or not. It may be being reused but the subview may not be there. My suggestion would be to create a PFImageViewCollectionViewCell Class (sub class of UICollectionViewCell) and register it as the CollectionView Cell and try. That's how I do and would do if I need a subview inside a cell.
Try adding a tag on this particular UIImageView
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static int photoViewTag = 54353532;
UICollectionViewCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"FlickrCell" forIndexPath:indexPath];
cell.backgroundColor = [self generateRandomUIColor];
PFImageView *photoView = [cell.contentView viewWithTag:photoViewTag];
// Create a view
//
if (!photoView) {
photoView = [[PFImageView alloc] initWithFrame:CGRectMake(0, 0, cell.frame.size.height, cell.frame.size.width) andImageURL:staticPhotoURL andOwningCell:cell];
imageView.tag = photoViewTag;
[cell.contentView addSubview:imageView];
}
// Update the current view
//
else {
NSURL *staticPhotoURL = [self.context photoSourceURLFromDictionary:[self.photos objectAtIndex:indexPath.row] size:OFFlickrSmallSize];
photoView.imageURL = staticPhotoURL;
}
return cell;
}
I would really recommend to create your own UICollectionViewCell subclass though.
EDIT: Also, note that I used the contentView property instead of adding it directly to the cell.
I have a custom UICollectionViewCell that has a custom background view which is drawn using one of several colour schemes. The colour scheme for the background view is set in my custom initializer -(id)initWithFrame:andColourPalette: for the View.
I have a similar custom initialiser in my UICustomViewCell subclass but I can't figure out how to call this initialiser when I am setting up the cell in cellForItemAtIndexPath:
Can anyone help me do this? Or offer alternative solution for passing this Dictionary of colours into the Cell to pass on to the subView?
EDIT to show more detail:
This is what I have in my UICollectionView VC:
In ViewWillAppear:
[self.collectionView registerClass:[OPOLawCollectionViewCell class] forCellWithReuseIdentifier:CELL_ID];
self.colourPalette = [OPOColourPalette greenyColourPalette];
In cellForItemAtIndexPath:
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CELL_ID forIndexPath:indexPath];
OPOLawCollectionViewCell *lawCell = (OPOLawCollectionViewCell *)cell;
MainLevel *level = self.collectionData[indexPath.row];
lawCell.delegate = self;
lawCell.colourPalette = self.colourPalette;
In my Custom UICollectionViewCell
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// get background view
OPOLawBook *lawBookView = [[OPOLawBook alloc]initWithFrame:CGRectMake(0, 0, 200, 265) andColourPalette:self.colourPalette];
But that doesn't work - I guess because the propertys are not set up.
If I change the last line to this, then it works fine:
OPOLawBook *lawBookView = [[OPOLawBook alloc]initWithFrame:CGRectMake(0, 0, 200, 265) andColourPalette:[OPOColorPalette greenyColorPalette]];
So i guess I need to use a custom intialiser here but I cant figure out how to call it , or from where...
Thanks
Yuo have to register your customCells in collectionView:
[self.collectionView_ registerClass:[YourCustomClass class]
forCellWithReuseIdentifier:#"CustomCell"];
And then in your method cellForItemAtIndexPath:
YourCustomClass *cell = (YourCustomClass *)[collectionView
dequeueReusableCellWithReuseIdentifier:#"CustomCell" forIndexPath:indexPath];
It is done because collectionView might have 1000 cells and 10 visible. You don't keep all of them initialized and reuse when possible.
EDIT
You should set colorPaletter after you deque the reusable cell. Think of it as a container which can hold any color. You need to determine (by indexpath) what color to paint.
You shouldn't do below if your custom cell is in the Storyboard,
[self.collectionView registerClass:[OPOLawCollectionViewCell class] forCellWithReuseIdentifier:CELL_ID];
Because Storyboard take responsibility to register Cell_ID own.
Now, It will conflict to be generated invalid Cell if you use both.
Way off, every answer. The questioner is looking for a way to uniquely identify each cell upon initialization, which happens prior to dequeuing a cell, and prior to a cell's access to its index path property.
The only way to do this is to assign a unique reuse identifier to every cell based on what the index path value will be (assuming you will know what that will be—and, in your case, you will); then, when dequeuing the cell, use the index path to find the cell with the corresponding reuse identifier.
Does this negates the purpose of reuse identifiers? Absolutely not. You'll be reusing that cell every time you need to use it again. Reuse identifiers were not meant to limit you to a cookie-cutter cell for every cell in your collection view; they are also intended to be "unique use" identifiers.