Newbie question.
I have a custom UICollectionView cells with UIImage and UIButton in every cell. I need to have array of UIImages that will be filled after cell's checkbox will be tapped to "checked" state (custom UIButton with appropriate checked/unchecked images connected to IBAction with changing internal cell's BOOL and checkbox state image). I can't use -didSelectItemAtIndexPath: for marking because I'm using it for previewing a cell photo. First idea was to add cell image to NSMutableArray by action of tapping checkbox to "checked" state, but if user will want to uncheck checkbox I can't track what exactly UIImage I need to delete from array. What is most painful method to do this?
PS: it is a listing of my method connected to the checkbox
- (void)theCheckboxTapped:(UIButton*)sender
{
cell = (CustomCell *)sender.superview.superview;
if (cell)
{
if ([cell isSelected] == NO)
{
UIImage *img1 = [UIImage imageNamed:#"checked.png"];
[[(CustomCell *)cell checkboxSign] setImage:img1 forState:UIControlStateNormal];
}
if ([cell isSelected] == YES)
{
UIImage *img2 = [UIImage imageNamed:#"unchecked.png"];
[[(CustomCell *)cell checkboxSign] setImage:img2 forState:UIControlStateNormal];
}
cell.isSelected =! cell.isSelected;
}
}
First idea was to made array of cells and check does BOOl of this cell is equal to YES or NO and add appropriate cell images to array
For solving this there are many solutions. But the one which I am explaining, is easy but this consumes some little bit more memory than other methods.
Steps:-
Create two array with capacity cell count. add all images in this array and add only bool value false initially when no cell is select. For this you can add 0.
NSMutableArray *imageArray = [[NSMutableArray alloc]init];
NSMutableArray *boolArray = [[NSMutableArray alloc]init];
When you checked any cell button then replace 0 with 1 in boolArray array, and vice versa.
And reload table. During reloading if value in second array for that position is 0 then no need to pick image from second and if value is 1 then pick image.
for changing value you can replaceObjectAtIndex method of nsmutablearray.
Edit:
No need to do like above.
one simpler solution is according to your code here.
As I am seeing that you are able to get cell.
So you can set tag for imageView and depending to tag you can also get cell's imageview in which you want to add image.
For this create imageArray and all images in this. Now add image like:
if cell.selected = True;
cell.imageView.image = [imageArray objectAtIndex:#"Cell's tage here or buttons tag here which is equal to indexPath.row"];
else
cell.imageView.image = #"Default image or no image here".
i have solved in a simple way.
Only after load 50-60 image uiCollectionView became very slow, so i have write a method to remove all imageView from the existing cell.
-(void)cleanCache{
NSMutableArray *visibleThumbViews = [NSMutableArray arrayWithCapacity:self.collectionView.visibleCells.count];
for (ThumbCell *t in self.collectionView.visibleCells) {
[visibleThumbViews addObject:t.thumbView];
}
for (ThumbView *t in self.thumbsViews) {
if (![visibleThumbViews containsObject:t]) {
[t unloadImage];
[t removeFromSuperview];
}
}
}
this method is called here:
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row % 30 == 0) {
[self cleanCache];
NSLog(#"clean");
}
ThumbCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"ThumbCellID" forIndexPath:indexPath];
cell.thumbView = [self.thumbsViews objectAtIndex:indexPath.row];
cell.thumbView.frame = cell.bounds;
return cell;
}
it's work very fine :P
sorry for bad english
UPDATE:
i had missed this delegate method:
-(void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath{
ThumbView *t = [self.thumbsViews objectAtIndex:indexPath.row];
[t unloadImage];
[t removeFromSuperview];
}
Related
I am trying to display an array of images in a UICollectionView. The cells are being displayed, but the images are not.
Here is the cell being built:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"Cell";
UICollectionViewCell *cell =
[collectionView dequeueReusableCellWithReuseIdentifier:identifier
forIndexPath:indexPath];
UIImageView * catagoryImage = [[UIImageView alloc] initWithFrame:CGRectMake((cell.frame.origin.x),(cell.frame.origin.y), cell.frame.size.width, cell.frame.size.height)];
[catagoryImage setImage:[UIImage imageNamed:[Apps objectAtIndex:indexPath.row]]];
[cell.layer setCornerRadius:4];
[cell addSubview:catagoryImage];
cell.clipsToBounds = NO;
return cell;
}
And here is the array being declared:
Apps = [NSArray arrayWithObjects:#"Appstore.jpg", #"2.gif", #"3.gif", #"4.gif", #"5.gif", #"7.gif", #"8.gif",#"9.gif", nil];
Any ideas?
You have a few options, but I'll name 2:
This. It's a bit hacky. but it works
Subclass UICollectionViewCell
There are two problems with the code. The first, bigger one, is the idea of creating an image view on every invocation of cellForItemAtIndexPath.
This method is called every time a cell is scrolled into view, and the cells are reused, so your cells will end up with piles and piles of UIImageViews on top of one another as the user scrolls around.
Problem two is the coordinate system of the imageView frame. It should be placed relative to the cell's bounds, not the cell's frame which is in the parent view's (collection view's) coordinate space, so...
// see if this cell has an image view already
UIImageView *categoryImageView = (UIImageView *)[cell viewWithTag:128];
if (!categoryImageView) {
// only create one if we don't have one
CGRect frame = cell.bounds; // this is zero based, relative to the cell
categoryImageView = [[UIImageView alloc] initWithFrame:frame];
categoryImageView.tag = 128; // so we can find it later
[cell addSubview:categoryImageView];
}
// add it conditionally, but configure it unconditionally
categoryImageView.image = [UIImage imageNamed:[Apps objectAtIndex:indexPath.row]]];
Another possible problem with the code is access to the Apps objectAtIndex:. Apparently Apps is an unconventionally named instance of an NSArray. This is only okay if your numberOfItemsInSection datasource method returns Apps.count, and if all elements in the array are names of images in your app bundle.
Sussed it.
The images were outside the keyboard scheme and were in the container package.
I modified a subview table cell using addsubview to include an additional image on the right (besides the one on the left). I had it working great until I added a search bar at the top. Somehow in doing that, the right image stopped displaying. I tried removing the search bar but have been unable to get the image to display. I have a feeling I made a typo inadvertently or somehow messed up the code.
Here is the method. It works except that the image on the right no longer displays. Would appreciate it if anyone can spot my error or suggest what is going wrong.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *protoCell = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:protoCell];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:protoCell];
}
// IDModel * item;
IDModel * item = nil;
// if no search
//item = [self.tableItems objectAtIndex:indexPath.row];
//following for search
if (tableView == self.searchDisplayController.searchResultsTableView) {
item = [searchResults objectAtIndex:indexPath.row];
} else {
item = [self.tableItems objectAtIndex:indexPath.row];
}
// Configure the cell...
cell.textLabel.text = item.name;
cell.detailTextLabel.text = item.sub;
cell.imageView.image = [UIImage imageNamed:item.pic];
//following crops and rounds image
//add image to right
UIImageView *rightimage;
rightimage = [[UIImageView alloc] initWithFrame:CGRectMake(225.0,0.0,80.0,45.0)];
rightimage.autoresizingMask=UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
// rightimage.image = [UIImage imageNamed:#"pic.jpg"];
[cell.contentView addSubview:rightimage];
rightimage.image = [UIImage imageNamed:item.pic];
return cell;
}
Thanks in advance for any suggestions.
One concern here is that you shouldn't add views to a UITableViewCell from the view controller. Table view cells are reused, so each time this cell gets reused you'll be adding a new view to it without first removing the old one.
Instead, create a UITableViewCell subclass, and add rightimage (which I would rename rightImageView to be consistent with Apple's naming conventions) in the -initWithStyle:reuseIdentifier: method. Then set the rightView's frame in the -layoutSubviews method, or set up the auto layout constraints in the -initWithStyle:reuseIdentifier: right after creating the rightImageView.
Make rightImageView a public property (put it in the .h file) so you can assign the image from the view controller.
I get images asynchronously with parse, and i insert them into an array with some other stuff.
I return this array to fill a tableView, and the rows has an UIImageView, but some images that i get asynchronously doest get loaded when i fill the table with the array in cellForRowAtIndexPath, so when i first look the tableView, i get the rows with an empty image in the cell, till i move the scroll in the tableView so cellForRowAtIndexPath is called again and those images i was set in the array are already loaded.
I get the images also if i do a IBAction with [self.tableView reloadData]; i get the images.
The problem is that i return the cell inmediatelly and the image is not loaded.
I'm using a custom TableViewCell.
I found something about "AsyncImageView" to load the image, but i dont know how.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Get the game for the row
GameModel *game = [self.currentMatches objectAtIndex:indexPath.row]; //HERE i load the images
MatchCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"GameCell" forIndexPath:indexPath];
cell.userImage.image = game.avatarp1; //I set the image to the cell but isnt loaded yet
//If i do this WORKS but i get 2 times the same image (on the game and now) (its a function that i created, that seems is calling tableView or so when assigns the image to the cell, i dont understand why it works
[ParseModel getAvatarWithUser:game.p1 completionBlock:^(UIImage *avatar) {
cell.userImage.image = avatar;
}];
What if you set to nil the image when you get the dequeueReusableCell, and then let the block set the image?
Just change this:
cell.userImage.image = game.avatarp1;
For this:
cell.userImage.image = nil;
And then let this code works (I have understood by your answer that this works well)
[ParseModel getAvatarWithUser:game.p1 completionBlock:^(UIImage *avatar) {
cell.userImage.image = avatar;
}];
Hope it helps!
You can use "SDWebImage" as an alternative to "AsyncImageView" that you mentioned in your question. It is very easy to implement. I put here an example of my own code, where I download an image from a URL in a cell in the same way you are trying. (Change my "view" for your cell.userImage)
This method puts a temp image (placeholder and when the image is fully downloaded, it automaticlly changes (without any reload or scroll).
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view
{
if (view == nil)
{
view = [[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 238.0f, 289.0f)] autorelease];
//here is where you put a placeholder and download in the background the image with an URL. Items is an array of URLs
[(UIImageView *)view setImageWithURL:[items objectAtIndex:index]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
view.contentMode = UIViewContentModeScaleAspectFit;
}
return view;
}
When you have added SDWebImage to your project, dont forget to add into your .m: #import < SDWebImage/UIImageView+WebCache.h >
I have a UITableViewCell, it is scroll-disabled and with fixed sections and rows.
There are two sections, with 1 row in section 0, and several rows in section 1.
The tableView is for users to make some choices.
So, the first section (with only one row) is going to display the result of users' choices,
and no doubt the second section (with several rows) is for choosing.
Now I want to put an image in the cell of the only row of the first section,
and this image will change according to users' choose.
It is very easy to judge which png image should be displaying, but I have trouble update it.
I tried use the cell's imageView, and manually alloc a UIImageView or UIView there to display those images.
But all of them won't work, I mean they just keep what they are displaying at the beginning and never changes it, even if I set the view's background or its image to a new png.
I tried some method like
[myImage setNeedsDisplay] for the manually alloced view,
or
[thatCell setNeedsDiaplsy] & [self.tableView reloadData] for the imageView of that cell,
but in vain.
So I wonder how can I achieve this function that dynamically display an image in a UITableViewCell in different situations?
Thanks a lot!
_____update line_____
I'm sorry that I didn't provide my code.
and here they are.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *inOutTableCellIdentifier = #"DealTableViewIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:inOutTableCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:inOutTableCellIdentifier] autorelease];
cell.textLabel.font = [UIFont fontWithName:cMyFont size:[UIFont systemFontSize]];
cell.detailTextLabel.font = [UIFont fontWithName:cMyFont size:[UIFont smallSystemFontSize]];
// I tried using both imageView and UIView here, but won't work
if (indexPath.section == 0) {
//cell.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"moneyCell.png"]];
cell.backgroundColor = [UIColor cyanColor];
// cell.imageView.image = [UIImage imageNamed:#"dealDone2.png"];
self.undoneIcon = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)] autorelease];
//self.undoneIcon.image = [UIImage imageNamed:#"dealUndone2.png"];
self.undoneIcon.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"dealUndone2.png"]];
[cell.contentView addSubview:self.undoneIcon];
.... // deal with other rows in section 1
return cell;
}
// and this is the method that update the image, the images are named "dealDoneX.png",
// where X is an integer from 0 to 4.
- (void)checkUndoneDegree { // here I also tried 2 ways corresponding to UIView or a cell's imageView, but neither works well.
int i = 0;
if (self._date)
i++;
if (self.moneyTextField.text)
i++;
if (self._incomingAccount)
i++;
if (self._expensingAccount)
i++;
if (_dealType != kTransferView)
if (self._type)
i++;
NSLog(#"undone degree: %d",i);
NSString *imageName = [#"dealUndone" stringByAppendingFormat:#"%d",i];
self.undoneIcon.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:imageName]];
[self.undoneIcon setNeedsDisplay];
// NSUInteger p[2] = {0,0};
// UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathWithIndexes:p length:2]];
// [cell setNeedsDisplay];
}
and everytime I update the table's data, like changing some text of some cell,
I would call [self checkUndoneDegree] and then call [self.tableView reloadData],
But the picture is never updated, at least from the screen.
I even tried to put the codes that decide which png to set in the
(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
method, but it can only make the view displaying the first png, without any updating.
Thanks for helping!
Make your undoneDegree (represented by i variable in your code) an ivar of your view controller, so it is accessible in all of it's methods, also in the UITableView delegate and data source methods.
Forget about setNeedsDisplay method. Since you are using UITableView to display your content, you need to play by its rules. This means you should use reloadSections:withRowAnimation: method.
Check again if you need self.undoneIcon. I'm pretty sure that imageView property should do. That is if you really want to display an image in the cell. If you want to create some kind of progress bar by manipulating cell's background, then use backgroundView property, or just place UIProgressView in your cell. This is what I think your want to do, after looking at your code.
I've created a custom UITableViewCell class, and used the layoutSubviews method to add a custom label. Like this:
- (void)layoutSubviews
{
[super layoutSubviews];
if (statusLabel == nil)
{
statusLabel = [[UILabel alloc]initWithFrame:CGRectMake(430.0, 10.0, 100.0, 20.0)];
[statusLabel setTextAlignment:UITextAlignmentRight];
[statusLabel setText:#"Status, set in code"];
statusLabel.tag = 1;
[self.contentView addSubview:statusLabel];
}
}
As you can see, I have set the initial text of the label to "Status, set in code".
In the table view controller I set the text for this custom label in the cellForRowAtIndexPath method, like so:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
int index = [indexPath row];
NSString *introducerString =[introducers objectAtIndex:index];
NSArray *parts = [introducerString componentsSeparatedByString:#","];
static NSString *MyIdentifier = #"Requester";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[[DanceCardCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:MyIdentifier] autorelease];
}
UIImage *image = [UIImage imageNamed:#"1.jpg"];
[cell.imageView setImage:image];
cell.textLabel.text = [parts objectAtIndex:1];
cell.detailTextLabel.text = #"Some text";
UILabel *statusLabel = (UILabel *)[cell viewWithTag:1];
statusLabel.text = #"Did it!";
return cell;
}
I'm using one table view to display two lists, depending on which of two buttons is pressed. When a button is pressed the appropriate table view controller is attached to the table view, and the reloadData method is called to trigger display of the new data. The new data does display, but the custom label text, which should read "Did it!" reads "Status, set in code ...", until I switch lists again twice.
How can I get the new text for the custom label to update straight away? I have checked the official documentation and cannot find any reference to refreshing a cell's display after updating its custom content.
Here is a screen shot to demonstrate what happens: http://www.dsbsystems.co.uk/images/xcode1.png
You're initializing the cell and immediately attempting to find the statusLabel with tag 1 inside it. layoutSubviews hasn't had the opportunity to be called yet, and so the label hasn't been created and added. (I suggest overriding the designated initializer method on your table view cell and creating the label there.)
Because of this, when you try to pull out statusLabel, it becomes nil because there's no such view, and messaging (calling a method on) nil simply does nothing (actually, it returns nil). You will need to watch out for this going forward if you're used to things blowing up with e.g. null reference exceptions.
When the cell is requested again, a new cell isn't needed because it's available from the reuse queue, and the label will be found correctly.