How to change UIImageView frame on UICollectionView cell when the UICollectionView loads? - ios

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!

Related

Adding subview into UICollectionView programmatically

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*/
}

Adding a subview to a UICollectionViewCell that takes up entire cell frame

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

UICollectionViewCell : Reused Cell with UIImageView item not refreshed after image and size changes

I've started developing an app with a pinterest-like layout with UICollectionView with different cell heights and contents. I've defined a template cell (label + UIImageView) which is used throught the cellForItemAtIndexPath callback.
In that specific function (cellForItemAtIndexPath) I'm modifying the content (UIImage) and the size of the UIImageView element to respect the ratio of the new image and use the full width of the current Cell.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
FRGWaterfallCollectionViewCell *waterfallCell = [collectionView dequeueReusableCellWithReuseIdentifier:WaterfallCellIdentifier forIndexPath:indexPath];
[waterfallCell.layer setMasksToBounds:YES];
[waterfallCell.layer setCornerRadius:10.0];
[waterfallCell.layer setBorderWidth:1.0];
[waterfallCell.layer setBorderColor:[[UIColor darkGrayColor] CGColor]];
[waterfallCell.layer setNeedsDisplayOnBoundsChange:YES];
// some variables init
.....
// Get the new image
UIImage *image = [UIImage imageNamed: [NSString stringWithFormat: #"photo_%d.jpg", no_image]];
// Compute the image ratio
CGFloat largeur_img = image.size.width;
CGFloat hauteur_img = image.size.height;
CGFloat viewRatio = hauteur_img / largeur_img;
// Modify the UIImageView frame
CGRect cadre = waterfallCell.img_doc.frame;
<b>waterfallCell.img_doc.frame = CGRectMake(cadre.origin.x,cadre.origin.y,cadre.size.width, ceil(cadre.size.width * viewRatio));</b>
// We set the image of the UIImageView
<b>[waterfallCell.img_doc setImage:image];</b>
// Force to redraw the cell
<b>[waterfallCell setNeedsDisplay];</b>
return waterfallCell;
}
However, this Cell is not refresh or redrawn...except when I scroll down the UICollectionView and go back to the top, then my UIImageView for each cell are in a correct shape. I've searched on the web, seen many posts regarding setNeedsDisplay but here it seems that the Cell use the CALayer so it is not concerned.. I've understand that the refresh in on the main thread but I do not know how to fire this refresh in order to get at the first display, the correct cells with correct sub-UIImageView with their right dimensions.
Thanks for your help,
Regards
Nicolas
The problem is right here :
FRGWaterfallCollectionViewCell *waterfallCell = [collectionView dequeueReusableCellWithReuseIdentifier:WaterfallCellIdentifier forIndexPath:indexPath];
On the first time you open the controller, there are no cells to be dequeued, so waterfallCell will be nil. ( if you want you can add a log to verify this). Only when you will scroll, the cells will be dequeued, that's why only when you scroll you can see the diferences.
What I do, when I use customised collection view cells :
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"catalogOneRowCell";
LACatalogOneRowCell *catalogCell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
if (catalogCell == nil) {
catalogCell = [LACatalogOneRowCell getLACatalogOneRowCell];
}
Here is the LaCatalogOneRowCell.m
+ (LACatalogOneRowCell *)getLACatalogOneRowCell {
NSArray *xib = [[NSBundle mainBundle] loadNibNamed:#"LACatalogOneRowCell" owner:nil options:nil];
for (NSObject *obj in xib) {
if ([obj isKindOfClass:[LACatalogOneRowCell class]]) {
return (LACatalogOneRowCell *)obj;
}
}
return nil;
}
And very important, in your viewDidLoad, register the nib of your cell:
UINib *nib = [UINib nibWithNibName:#"LACatalogOneRowCell" bundle:nil];
[catalogCollectionView registerNib:nib forCellWithReuseIdentifier:#"catalogOneRowCell"];
Also, i do not recommed alocing in cellforRow:
UIImage *image = [UIImage imageNamed:...
is the same as
UIImage *image = [[[UIImage alloc] initWithImage:.... ] autorelease];
It is bad for performance to alloc objects in cellForRow, especially objects like UIImage
In my opinion, you should dequeue based on ratio. This will save you from a lot of pain trying to put everything together. Whatever happens your items will have a limit set of ratios anyway. Plus I'm not sure you can change the frame of a cell that's already been used without kicking off a large reload of the view which would be quite costly in terms of CPU.

UICollectionViewCell outlet frame change animation

I have a UICollectionView which uses a waterfall style layout to show different sized cells. The height calculation for each cell works just fine.
The problem I have is that I have to change the size of two outlets (UIImage and UILabel) in each cell and I don´t know where to change the frame size in order prevent the UICollectionView from animating said frame size change. I tried it to do in the layoutSubviews method of each cell and in the cellForItemAtIndexPath without results. Every time the viewcontroller containing the UICollectionView appears the frame change for the two outlets in each cell is animated.
Example setting the frame for just the image inside cellForItemAtIndexPath:
- (UICollectionViewCell *) collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CVCNewspaper *cell = (CVCNewspaper *)[collectionView dequeueReusableCellWithReuseIdentifier:CELL_IDENTIFIER_NEWSPAPER forIndexPath:indexPath];
BONewspaper *newspaper = [newspapers objectAtIndex:indexPath.row];
CGFloat width = cell.frame.size.width;
// Calculate rect for imageView
CGFloat objectWidth = newspaper.coverNews.imageWidth ? newspaper.coverNews.imageWidth : 180;
CGFloat objectHeight = newspaper.coverNews.imageHeight ? newspaper.coverNews.imageHeight : 100;
CGFloat scaledHeight = floorf(objectHeight / (objectWidth / width));
cell.imageViewCover.frame = CGRectMake(MARGINVIEW, HEADERHEIGHT + 5, width, scaledHeight);
[cell fillCellWithNewspaper:newspaper];
return cell;
}

How to make a textfield as big as a UICollectionViewCell?

I downloaded the Collection View example and I replaced the ImageView with textfield.
I want to make the textfield as big as the UICollectionViewCell (which is a square shape). But the shape of the textfield didn't change.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"Cell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
// height and width in the parent cell
double height = cell.frame.size.height;
double width = cell.frame.size.width;
// get the child textfield
UITextField *textField = (UITextField *)[cell viewWithTag:100];
// make the textfield as big as the cell
CGRect frame = CGRectMake(0, 0, width, height);
textField.frame =frame;
textField.text = #"testing";
return cell;
}
Do you have set the Tag of your UITextfield to 100 in XCode Designer? You have to set the Tag to 100 before calling your code, to ensure the UITextfield to be found.
Changing Border Style is not necessary, because you can do it with every Borderstyle.... I have done it already...
Go to your cellItem Nib in XCode Designer, select the UITextField and in the right properties, change the tag to 100... This will solve it
You can do that by changing the border style of UITextField.
See this Stackoverflow post: How to set border style of a UITextfield
I have found the root cause of this problem. The root cause is the auto layout, it resizes the width, height and the space between the cell and the textfield at the runtime. Once I removed the auto layout, the code work fine.... And the border style is not a matter.
I am looking for another way to solve this problem.
just try this..
NSInteger myCustomHeight = 237;
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 200, myCustomHeight)];

Resources