I'm create UICollectionView programmatically to uiview (i use single view). like this
UICollectionViewFlowLayout *CATLayout=[[UICollectionViewFlowLayout alloc]init];
CATLayout.minimumInteritemSpacing = 2.0f;
CATLayout.minimumLineSpacing = 2.0f;
CATLayout.scrollDirection = UICollectionViewScrollDirectionVertical;
CATLayout.sectionInset = UIEdgeInsetsMake(1.0f, 1.0f, 1.0f, 1.0f);
self.ColStickersListView=[[UICollectionView alloc]initWithFrame:CGRectMake(0, 0, StickersListView.frame.size.width, StickersListView.frame.size.height) collectionViewLayout:CATLayout];
self.ColStickersListView.delegate=self;
self.ColStickersListView.dataSource=self;
self.ColStickersListView.tag=2;
[self.ColStickersListView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"CollStickersList"];
[self.ColStickersListView setBackgroundColor:[UIColor clearColor]];
[StickersListView addSubview:ColStickersListView];
and
UICollectionViewCell *cell = [ColStickersListView dequeueReusableCellWithReuseIdentifier:#"CollStickersList" forIndexPath:indexPath];
// for background selected
NSString *imageName=anObject;
NSString *filename=[NSString stringWithFormat:#"%#/Stickers/List/%#",[appDel DocsPath],imageName];
NSLog(#"%#",filename);
cell.backgroundColor=[UIColor clearColor];
UIImage *image=[UIImage imageNamed:filename];
UIImageView *photoView=[[UIImageView alloc]initWithFrame:CGRectMake(StickersListPadding,StickersListPadding,StickersListThumbSize-(StickersListPadding*2),StickersListThumbSize-(StickersListPadding*2))];
photoView.image=image;
photoView.contentMode=UIViewContentModeScaleAspectFit;
photoView.userInteractionEnabled = YES;
[cell.contentView addSubview:photoView];
return cell;
it's work perfect for display image to cell.
Problem !!
if scroll page to bottom and scroll return to top again that image in cell it's overlap.
How to fix it.!!! (Programmatically only with single view)
I suspect because you aren't setting the frame on the UIImageView it is deriving its size from the image that is being set on it. So in some cases it might overflow its parent view -> the cell in this case.
I suggest you setup some constraints on the UIImageView, this can be done programmatically.
So try to set left, right, top and bottom constraints on the UIIMageView so that it stays within the bounds of the cell
During scroll
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath is beeing called repeatively..
the means your are adding UIImageView *photoView repeatedly to cell.contentView
UIImageView *photoView=[[UIImageView alloc]initWithFrame:CGRectMake(StickersListPadding,StickersListPadding,StickersListThumbSize-(StickersListPadding*2),StickersListThumbSize-(StickersListPadding*2))];
//codes..
[cell.contentView addSubview:photoView];
doing something like this prevent that:
UICollectionViewCell *cell = [ColStickersListView dequeueReusableCellWithReuseIdentifier:#"CollStickersList" forIndexPath:indexPath];
if (cell == nil)
{
UIImageView *photoView=[[UIImageView alloc]initWithFrame:CGRectMake(StickersListPadding,StickersListPadding,StickersListThumbSize-(StickersListPadding*2),StickersListThumbSize-(StickersListPadding*2))];
// codes...
[cell.contentView addSubview:photoView];
}
Another way of solving this is by creating a custom collection cell:
// CustomCollectionCell.h
#interface YourCollectionCell : UICollectionViewCell
#property (nonatomic) UIImageView *photoView;
#end
// CustomCollectionCell.m
#implementation YourCollectionCell
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// and setting up you imageview here
self.photoView=[[UIImageView alloc]initWithFrame:YourRect];
self.photoView.contentMode=UIViewContentModeScaleAspectFit;
self.photoView.userInteractionEnabled = YES;
[self.contentView addSubview:self.photoView];
}
return self;
}
and using it like:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CustomCollectionCell *cell = (CustomCollectionCell *)[ColStickersListView dequeueReusableCellWithReuseIdentifier:#"CollStickersList" forIndexPath:indexPath];
cell.photoView.image=image;
return cell;
}
Hope i've helped you, happy coding.. Cheers & Good night!
Related
I have UICollectionViewCell
#import "DBPhotoCollectionViewCell.h"
#define IMAGEVIEW_BORDER_LENGTH 5
#implementation DBPhotoCollectionViewCell
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self setup];
}
return self;
}
/* Need to implement the initWithCoder method since this class will be created from the storyboard */
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self){
[self setup];
}
return self;
}
/* Create the UIImageView and add it to the cell's contentView in code. */
-(void)setup
{
self.imageView = [[UIImageView alloc] initWithFrame:CGRectInset(self.bounds, IMAGEVIEW_BORDER_LENGTH, IMAGEVIEW_BORDER_LENGTH)];
[self.contentView addSubview:self.imageView];
}
#end
And call this Cell from CollectionViewController class
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Photo Cell";
DBPhotoCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
cell.backgroundColor = [UIColor whiteColor];
cell.imageView.image = [UIImage imageNamed:#"Astronaut.jpg"];
// Configure the cell
return cell;
}
But I have one problem when I start my application my picture is showed with small size. What I missed?
http://screencast.com/t/ia8tE05P6yc
http://screencast.com/t/33N4AhT7
cell.imageView, imageView object is not same as which you have added on cell of Interface Builder. UICollectionViewCell has default size UIImageView which are accessing, you can access your customView imageView in cellForIndexPath method of collectionView by linking imageView of subClass DBPhotoCollectionViewCell in interface builder you don't need to add [self.contentView addSubview:self.imageView]; in setup method.
Second approach would be assign tag value to imageView when you adding to cell contentView
/* Create the UIImageView and add it to the cell's contentView in code. */
-(void)setup
{
self.imageView = [[UIImageView alloc] initWithFrame:CGRectInset(self.bounds, IMAGEVIEW_BORDER_LENGTH, IMAGEVIEW_BORDER_LENGTH)];
self.imageView.tag=1;
[self.contentView addSubview:self.imageView];
}
//And access imagView in cellForIndexPath
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Photo Cell";
DBPhotoCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
cell.backgroundColor = [UIColor whiteColor];
UIImageView *imageView=[cell.contentView viewWithTag:1];
imageView.image = [UIImage imageNamed:#"Astronaut.jpg"];
// Configure the cell
return cell;
}
I have a collection view with the layout set to horizontal scrolling. Each header section contains a label which needs to be rotated 90 degrees
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
UICollectionReusableView *reusableview = nil;
if (kind == UICollectionElementKindSectionHeader)
{
Collection *collection=self.collectionArray[indexPath.section];
UICollectionReusableView *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"HeaderView" forIndexPath:indexPath];
headerView.tag=indexPath.section;
headerView.backgroundColor=[Settings getInstance].textColor;
UIImageView* expandView=(UIImageView*)[headerView viewWithTag:kExpandImage];
expandView.image=[UIImage imageNamed:[NSString stringWithFormat:#"expand_%#.png",[Settings getInstance].appBrand]];
UILabel* label=(UILabel*)[headerView viewWithTag:kCollectionLabel];
label.text=[NSString stringWithFormat:#"%#",collection.name];
label.backgroundColor=[Settings getInstance].textColor;
label.textColor=[Settings getInstance].backColor;
label.font=[UIFont fontWithName:#"HelveticaNeue-UltraLight" size:50];
label.transform = CGAffineTransformMakeRotation(-M_PI_2);
CGRect frame=label.frame;
frame.size.height=headerView.layer.frame.size.height;
frame.origin.y=headerView.layer.frame.origin.y;
label.frame=frame;
//add tap gesture to detect touch
HeaderTapRecognizer *singleTapRecogniser = [[HeaderTapRecognizer alloc] initWithTarget:self action:#selector(sectionTapped:)];
singleTapRecogniser.sectionIndexPath=indexPath;
singleTapRecogniser.delegate=self;
singleTapRecogniser.numberOfTouchesRequired = 1;
singleTapRecogniser.numberOfTapsRequired = 1;
[headerView addGestureRecognizer:singleTapRecogniser];
reusableview= headerView;
}
if (kind == UICollectionElementKindSectionFooter) {
UICollectionReusableView *footerview = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:#"FooterView" forIndexPath:indexPath];
reusableview = footerview;
}
return reusableview;
}
When I run this through Instruments, I'm getting an increased memory allocation under GSEventRunModal. The problem is resolved by taking out this line of code
label.transform = CGAffineTransformMakeRotation(-M_PI_2);
All examples I've seen all use this method to rotate a label so unsure where I'm going wrong.
Any ideas are most welcome, thanks.
One thing to try would be applying the transformation BEFORE you add the text, background color, text color and font. That way you are only rotating an empty label, then subsequently filling it.
i want to load my images i've got from flickr into uicollectionsview
i followed allot of guides but they all fall apart when i have to do this
cell.imageView.image
it basically says it can't find imageView in object of uicollectionsviewcell
i also tried a method like this, but it didn't work
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"flickerCell";
UICollectionViewCell *flickerCell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
flickerCell.backgroundColor = [UIColor whiteColor];
UIImageView *recipeImageView = (UIImageView *)[flickerCell viewWithTag:100];
recipeImageView.image = [UIImage imageNamed:[photoURLs objectAtIndex:indexPath.row]];
[flickerCell addSubview:recipeImageView];
return flickerCell;
}
recipieImageView is something i got some a tutorial.
my cell is named flickerCell in the storyboard and photoURLs has 33 objects i can view in code . so it's all there. also photoURLs is a NSMutablearray
how ever. in this method my recipeImageView returns nill ???
i have a cell.h which has the imageView
#import <UIKit/UIKit.h>
#interface Cell : UICollectionViewCell
#property (strong, nonatomic) IBOutlet UIImageView *imageView;
#end
so if anyone know a way i can display all the images that photoURLs has in a uicollectionview it would be very helpful
UPDATE
i now tried a new way but still not working. imageView returns nil
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"flickerCell";
Cell *flickerCell = (Cell*) [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
flickerCell.backgroundColor = [UIColor whiteColor];
imageView.image = [UIImage imageNamed:[photoURLs objectAtIndex:indexPath.row]];
return flickerCell;
}
UPDATE AGAIN
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"flickerCell";
Cell *flickerCell = (Cell*) [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
NSLog(#"flickercell = %#", flickerCell);
flickerCell.backgroundColor = [UIColor whiteColor];
flickerCell.imageView.image = [UIImage imageNamed:[photoURLs objectAtIndex:indexPath.row]];
return flickerCell;
}
with this in viewdidload
[self.collectionView registerClass:[Cell class] forCellWithReuseIdentifier:#"flickerCell"];
NSLOG of flickerCell returns this :
flickercell = <Cell: 0x9dec2f0; baseClass = UICollectionViewCell; frame = (0 0; 120 115); layer = <CALayer: 0x9dec430>>
As I get it you are using a custom UICollectionViewCell. However in the method you pasted you create a pointer to UICollectionViewCell which has no built-in UIImageView. So that's why you get the error in the first try.
About the second - did you register your custom UICollectionViewCell class with the CollectionView?
======
Edit: after a long chat with the poster we solved all the problems.
The first one was that the registered class was UICollectionViewCell, not the inherited custom one.
The second problem was that the view was actually in a xib file, so registerNib:forCellWithReuseIdentifier: should be used instead of registerClass:forCellWithReuseIdentifier:
The third one was that the custom cell was configured in a storyboard xib instead of its own xib file that is wired with the custom class ("Cell" in our case)
And fourth main problem was that UIImage was incorrectly created with imageNamed: which should be used only for images that are embedded in the project. The correct way to create the UIImage in our case is: flickerCell.imageView.image = [UIImage imageNamed:[photoURLs objectAtIndex:indexPath.row]];
It may help you.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"flickerCell";
Cell *flickerCell = (Cell*) [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
flickerCell.backgroundColor = [UIColor whiteColor];
flickerCell.imageView.image = [UIImage imageNamed:[photoURLs objectAtIndex:indexPath.row]];
return flickerCell;
}
You can easily create a custom UICollectionViewCell class with custom the animation while tap the UICollectionViewCell.
So, You don't need to add any specific method/action for the animation.
In storyboard - Add a UICollectionViewCell with cell identifier, UIImageView and UILabel.
Assign both UIImageView and UILabel property to Storyboard cell.
Import the 'UviCollectionCell.h' in your view controller.
In YourViewController.h
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UviCollectionCell *cell = (UviCollectionCell *) [cv dequeueReusableCellWithReuseIdentifier:#"collection_cell" forIndexPath:indexPath];
[cell.cellImageView setBackgroundColor: [UIColor randomColor]];
// You can add your image here.
cell.cellImageView.image = [UIImage imageNamed:[photoURLs objectAtIndex:indexPath.row]];
[cell.cellTitle setText:[NSString stringWithFormat:#"S %ld - R %ld",(long)indexPath.section,(long)indexPath.row]];
//cell.backgroundColor = [UIColor randomColor];
return cell;
}
UviCollectionCell.h
#import <UIKit/UIKit.h>
#interface UviCollectionCell : UICollectionViewCell<UIGestureRecognizerDelegate>
#property (strong, nonatomic) IBOutlet UIImageView *cellImageView;
#property (strong, nonatomic) IBOutlet UILabel *cellTitle;
#end
UviCollectionCell.m
#import "UviCollectionCell.h"
#implementation UviCollectionCell
// Initialize the collectiion cell based on the frame and add the tap gesture recognizer for custom animation
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
// Custom animation while tap the CollectionCell view.
UITapGestureRecognizer *tapReconizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(selectedCellAnimation)];
[tapReconizer setNumberOfTouchesRequired : 1];
[tapReconizer setDelegate:self];
[self addGestureRecognizer:tapReconizer];
return self;
}
// Initialize the collection cell based on the nscoder and add the tap gesture recognizer for custom animation
-(id) initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
// Custom animation while tap the CollectionCell view.
UITapGestureRecognizer *tapReconizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(selectedCellAnimation)];
[tapReconizer setNumberOfTouchesRequired : 1];
[tapReconizer setDelegate:self];
[self addGestureRecognizer:tapReconizer];
return self;
}
// Initialize the collectiion cell and add the tap gesture recognizer for custom animation
-(id) init {
self = [super init];
// Custom animation while tap the CollectionCell view.
UITapGestureRecognizer *tapReconizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(selectedCellAnimation)];
[tapReconizer setNumberOfTouchesRequired : 1];
[tapReconizer setDelegate:self];
[self addGestureRecognizer:tapReconizer];
return self;
}
// Customize the animation while tap the UICollectionViewCell with custom animation duration.
- (void)selectedCellAnimation {
[UIView animateWithDuration:0.4 delay:0 options:(UIViewAnimationOptionCurveEaseIn) animations:^{
CALayer *layer = self.layer;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, 25.0f * M_PI / 180.0f, 1.0f, 0.0f, 0.0f);
layer.transform = rotationAndPerspectiveTransform;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3 delay:0 options:(UIViewAnimationOptionCurveEaseOut) animations:^{
CALayer *layer = self.layer;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m24 = 0;
rotationAndPerspectiveTransform =CATransform3DRotate(rotationAndPerspectiveTransform, 0.0f * M_PI / 180.0f, 1.0f, 0.0f, 0.0f);
layer.transform = rotationAndPerspectiveTransform;
} completion:nil];
}];
}
#end
In your code
imageView.image = [UIImage imageNamed:[photoURLs objectAtIndex:indexPath.row]];
what does [photoURLs objectAtIndex:indexPath.row] return?
For use with imageNamed: it MUST return a NSString object which is the name of a resource located in the main bundle. It won't work with a URL, even less if this URL is pointing to a remote URL.
Anyway, you shouldn't use imageNamed. This method caches the image in the system's cache, which is not optimal/required in a collection/table view. Instead use an alternative method to create the UIImage object.
A solution to your problem depends whether you need to load your image data from a remote server or if these resources are located on the device.
In the first case, you need an asynchronous approach in conjunction with a "placeholder image" which represents an image while being loaded when it is displayed in the cell.
In the second case, you may use a simple synchronous approach to load images (similar to your existing solution, but using imageWithContentsOfFile: for example), unless the images are large, in which case you need an asynchronous approach too.
Note: your code has a few other issues, too - but first and foremost you need to tell where your images come from.
A possible (but not optimal) solution for loading images from the disk on the device:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"flickerCell";
FlickerCell* flickerCell = (FlickerCell*) [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
flickerCell.backgroundColor = [UIColor whiteColor];
NSURL* url = [self.photoURLs objectAtIndex:indexPath.row]; // file URL
flickerCell.imageView.image = [UIImage imageWithContentsOfFile:[url path]];
return flickerCell;
}
A more sophisticated solution would load and decode images ahead in a background thread - before they are actually displayed. That way, when they are actually displayed, the images are possibly already encoded and will render much faster. This also requires a cache.
I make a custom UICollectionViewCell and add a subView to its contentView:
BooksCell.h
#interface BooksCell : UICollectionViewCell
#property (strong, nonatomic) UIImageView *certifyImageView;
#end
BooksCell.m
- (id)initWithFrame:(CGRect)frame {
self= [super initWithFrame:frame];
if(self){
_coverImageView = [[UIImageView alloc] initWithFrame:CGRectMake(15, 15, 88, 117)];
_coverImageView.userInteractionEnabled = YES;
[self.contentView addSubview:_coverImageView];
UIImage *certifyImage = [UIImage imageNamed:#"13-6.png"];
_certifyImageView = [[UIImageView alloc] initWithFrame:CGRectMake(17.5, _coverImageView.frame.size.height-3, certifyImage.size.width, certifyImage.size.height)];
_certifyImageView.image = certifyImage;
_certifyImageView.hidden = YES;
}
return self;
}
ViewController
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
BooksCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"BooksCell" forIndexPath:indexPath];
cell.coverImageView.image = [UIImage imageNamed:#"13-5.png"];
// **Here I want some cell display certifyImageView and some not.**
if(indexPath.row%2==0){
cell.certifyImageView.hidden = NO;
}
return cell;
}
I add the collectionView as subView to the Viewcontroller, and set it's frame correctly, now the collectionView displaied coverImageView and certifyImageView normally, but when I scroll the collectionView, certifyImageView displaied on the wrong cell,I guess it maybe caused by the Reuse Cell, and how to sole it?
I think because it is reusing the cell that already set the certifyImageView.hidden to NO so you have to set it back to YES
Maybe try this
if(indexPath.row%2==0){
cell.certifyImageView.hidden = NO;
}
else{
cell.certifyImageView.hidden = YES;
}
This way you will make sure that the certifyImageView will be set to hidden if thats what you want.
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];
}