Follow this answer on SO I be able to create UICollectionView programmatically. But I can't find any better solution when I try to add subview into UICollectionViewCell. Here is how most answer on SO achieve
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:#"cellIdentifier" forIndexPath:indexPath];
cell.backgroundColor = [UIColor redColor];
UIImageView *image = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,100,100)];
image.image = /*some image*/
[cell addSubview:image];
return cell;
}
Maybe i'm wrong but the purpose of using UICollectionView wasn't because of recycle and reuse to optimize the performance? If using the code above when the user scrolling wasn't it add more and more UIImageView into UICollectionViewCell subview when dequeueReusableCellWithReuseIdentifier get trigger?
So what is the better way to do this? I can't use UITableView for this because I need the horizontal scrolling technique.
NOTE: I need to create it programmatically without using xib.
Yes, It will add imageView everytime when your collection view will get scrolled! And it is not good approach. Now, what you can do is, Take one image view in your collectionview cell, I mean in interface builder(xib or storyboard whatever you have used) and in cellForItemAtIndexPath just show or hide that image view as per need or change image of it as per requirement!
Update :
If you have create collection view programmatically then you can set some flag in your cellForItemAtIndexPath! Take one flag and set it after adding imageview. and you can declare imageview and flag as global variable. Something like,
if (!isImageAdded) {
UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:#"cellIdentifier" forIndexPath:indexPath];
cell.backgroundColor = [UIColor redColor];
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,100,100)];
imageView.image = /*some image*/
[cell addSubview:image];
isImageAdded = YES;
}
else{
imageView.image = /*some image*/
}
Related
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!
MY OBJECTIVE: I need to load an image on the UICollectionView, but changing the Y position of it on the UIImageView frame, because the loaded image is taller than the cell display window, and I need to display a specific part of it.
SETTINGS: The UICollectionView and the UIImageView were manually added to the storyboard (not programmatically). The UIImageView view mode is set to Aspect Fill on the Attributes Inspector.
THE PROBLEM: I've tried changing the UIImageView frame after (and also before) loading the image, but the change only takes effect after the UICollectionView is scrolled up and down, and I need the image position updated when it first loads.
PS: I've also tried all the different types of view mode on the UIImageView, but it doesn't seem to make any difference.
Here's the coding I'm using for it:
- (UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
PhotoCell *cell = (PhotoCell *)[collectionView dequeueReusableCellWithReuseIdentifier:#"PhotoCell" forIndexPath:indexPath];
cell.photoImageView.image = [UIImage imageNamed:_stringImageURL];
CGRect oldFrame = cell.photoImageView.frame;
CGRect newFrame = CGRectMake(oldFrame.origin.x, -45, oldFrame.size.width, oldFrame.size.height);
cell.photoImageView.frame = newFrame;
cell.backgroundColor = [UIColor redColor];
return cell;
}
Any help would be greatly appreciated! Thanks in advance!
I am trying to simply add a UIView to a UICollectionViewCell that takes up the entire cell's frame. The code I have now only displays a single cell in the top left corner (I suspect each cell is getting layer out on top of each other). How exactly can I do this?
I plan on subclassing UIView later so as to customize the view that I am adding to the cell. I don't want to subclass UICollectionViewCell however based on the way I will be using it.
#pragma mark - Collection view data source
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return 6;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
UIView *menuItem = [[UIView alloc] init];
menuItem.frame = cell.frame;
menuItem.backgroundColor = [UIColor greenColor];
[cell addSubview:menuItem];
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath; {
return CGSizeMake(60, 60);
}
You'll need to use bounds, not frame. If you don't know the difference between the two or why this matters, you should read up on this and get comfortable with it before going further:
menuItem.frame = cell.bounds;
You'll also want to set an autoresizing mask, since the cell won't initially be at the correct size:
menuItem.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
And, really, you should be adding this to the cell's contentView property:
[cell.contentView addSubview:menuItem];
menuItem.frame = cell.contentView.bounds;
That said, if you plan on adding lots of subviews to menuItem, I recommend subclassing UICollectionViewCell instead of trying to build it in your cellForItemAtIndexPath: method. It will be much easier to control if the layout and setup is encapsulated in a different class, and you can respond to height / width changes by overriding layoutSubviews.
You should only interact with the cell's contentView in this case.
UIView *menuItem = [UIView new];
menuItem.frame = cell.contentView.bounds;
menuItem.backgroundColor = [UIColor greenColor];
[cell.contentView addSubview:menuItem];
It's Swift version.
let menuItem = UIView.init()
menuItem.frame = cell.bounds
menuItem.backgroundColor = .green
cell.contentView.addSubview(menuItem)
menuItem.frame = cell.contentView.bounds
Swift 5 Apple doc:
var contentView: UIView
The main view to which you add your cell’s custom content.
When configuring a cell, you add any custom views representing your cell’s content to this view. The cell object places the content in
this view in front of any background views.
var backgroundView: UIView?
The view that is displayed behind the cell’s other content.
Use this property to assign a custom background view to the cell. The background view is placed behind the content view and its frame
is automatically adjusted so that it fills the bounds of the cell.
Example:
let template = UIView()
self.backgroundView = template
template.layer.cornerRadius = 10
template.backgroundColor = .white
I have a UICollectionView which contains custom UICollectionViewCell. The collection view has a background image.
I would show the background image, but the collection view (or its cells) has always a white background which prevents my background image to show.
Here is the code:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
AgendaCollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:agendaCellIdentifier forIndexPath:indexPath];
cell.delegate = self;
cell.layer.borderWidth=0.0f;
cell.layer.backgroundColor = [UIColor clearColor].CGColor;
cell.contentView.layer.backgroundColor = [UIColor blackColor].CGColor;
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
[cell setCurrentDateAtIndex:([indexPath row]) date:currentMonth events:events];
return cell;
}
The AgendaCollectionCell is very simple: I did a cell by using IB and set all background images to clearColor. Also the collection view use a very basic custom layout.
An excertp of what is shown in the pic:
The collection view show cells with numbers. Beneath the collection view there is a background which is not shown because of the white color of the cells.
Any hint appreciated. Thank you.
SOLVED.
The problem was in the rendering of the custom cell. I used a method (a lost method...) which set the background color of the cell. For non custom cell, the code in my question is correct.
you can also background color for your custom UICollectionView by setting backgroundview for UICollectionView.
UIView *blueView = [[UIView alloc]init];
blueView.backgroundColor = [UIColor blueColor];
self.collectionView.backgroundView = blueView;
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];
}