iOS UICollectionView memory leak - ios

I've created UICollectionView through storyboard.
My cell is custom cell class that have 3 buttons with images.
My images are available as part of class GalleryItemInfo. I have an array of those objects
[GalleryDataProvider sharedInstance].itemInfo
There is code for cellForItemAtIndexPath (in one cell there are three buttons for three items in array):
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CollectionViewCellPreviewTriple *cell;
if (indexPath.row % 2 == 0 && !is_iPhone) {
cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cellOrangeRed" forIndexPath:indexPath];
if (is_Fingerprint_Version) {
cell.imageViewRope.image = [UIImage imageNamed:#"image-rope-1.png"];
}
} else {
cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cellGreenBlue" forIndexPath:indexPath];
if (is_Fingerprint_Version) {
cell.imageViewRope.image = [UIImage imageNamed:#"image-rope-2.png"];
}
}
cell.previewCellDelegate = self;
cell.tag = indexPath.row;
NSInteger leftPreviedId = [cell firstPreviewId];
self.leftPreviewedID = leftPreviedId;
UIImage *image1 = ((GalleryItemInfo *)[[GalleryDataProvider sharedInstance].itemInfo objectAtIndex:leftPreviedId]).slotPreviewImage;
UIImage *image2;
UIImage *image3;
if (leftPreviedId + 1 < [[GalleryDataProvider sharedInstance].itemInfo count])
image2 = ((GalleryItemInfo *)[[GalleryDataProvider sharedInstance].itemInfo objectAtIndex:leftPreviedId + 1]).slotPreviewImage;
if (leftPreviedId + 2 < [[GalleryDataProvider sharedInstance].itemInfo count])
image3 = ((GalleryItemInfo *)[[GalleryDataProvider sharedInstance].itemInfo objectAtIndex:leftPreviedId + 2]).slotPreviewImage;
[cell setupWithImage1:image1 image2:image2 image3:image3];
if (self.isEditModeEnabled) {
[cell showRemoveButtons];
} else {
[cell hideRemoveButtons];
}
return cell;
}
Trouble: when I scroll my collection memory usage increases every swipe from right to left on about 1 megabyte.
Why memory is not released?
Update:
CollectionViewCellPreviewTriple code (created through storyboard):
#import <UIKit/UIKit.h>
#protocol UICollectionViewPreviewCellDelegate;
#interface CollectionViewCellPreviewTriple : UICollectionViewCell
#property (weak, nonatomic) IBOutlet UIButton *buttonSlot1;
#property (weak, nonatomic) IBOutlet UIButton *buttonSlot2;
#property (weak, nonatomic) IBOutlet UIButton *buttonSlot3;
#property (weak, nonatomic) IBOutlet UIButton *buttonRemove1;
#property (weak, nonatomic) IBOutlet UIButton *buttonRemove2;
#property (weak, nonatomic) IBOutlet UIButton *buttonRemove3;
#property (weak, nonatomic) IBOutlet UIImageView *imageViewRope;
#property (nonatomic, weak) id<UICollectionViewPreviewCellDelegate> previewCellDelegate;
- (void)setupWithImage1:(UIImage *)image1 image2:(UIImage *)image2 image3:(UIImage *)image3;
- (void)showRemoveButtons;
- (void)hideRemoveButtons;
- (NSInteger)firstPreviewId;
#end
#protocol UICollectionViewPreviewCellDelegate
- (void)collectionViewPreviewCell:(CollectionViewCellPreviewTriple *)collectionViewCell didSelectSubitemWithIndex:(NSInteger)subitemIndex;
- (void)collectionViewPreviewCell:(CollectionViewCellPreviewTriple *)collectionViewCell didEditModeRequestWithStatus:(BOOL)status;
- (void)collectionViewPreviewCell:(CollectionViewCellPreviewTriple *)collectionViewCell didRemoveRequestWithIndex:(NSInteger)subitemIndex;
- (void)slotButtonRequestsShadow:(UIButton *)slotButton;
#end
Update:
- (void)setupWithImage1:(UIImage *)image1 image2:(UIImage *)image2 image3:(UIImage *)image3
{
[self.buttonSlot1 setBackgroundImage:image1 forState:UIControlStateNormal];
[self.buttonSlot1 setBackgroundImage:image1 forState:UIControlStateHighlighted];
//if (image2) {
[self.buttonSlot2 setBackgroundImage:image2 forState:UIControlStateNormal];
[self.buttonSlot2 setBackgroundImage:image2 forState:UIControlStateHighlighted];
[self.buttonSlot2 setHidden:(image2 == nil)];
//}
//if (image3) {
[self.buttonSlot3 setBackgroundImage:image3 forState:UIControlStateNormal];
[self.buttonSlot3 setBackgroundImage:image3 forState:UIControlStateHighlighted];
[self.buttonSlot3 setHidden:(image3 == nil)];
//}
}
Profiling
link for screen

This may be related to the issue that many people have been suffering with where the cell's are not reused.
To test this, you should override the method prepareForReuse and in it write a very simple log:
NSLog(#"%# is being called as expected.", NSStringFromSelector(_cmd));
You should then run your app, scroll the collection view, and check the console to see if this log appears.
If this log does not appear, you may want to check this answer for help as to how to proceed. In my app cells are not reused in the simulator, but are reused on devices. It's odd.

Related

UITableViewCell and its ViewModel persistency in memory

I noticed that my iOS app memory consumption is increasing while I'm scrolling table view. Using Allocations instrument I found out that some of table view cells are not retained and corresponding view models are not retained at all. What can be causing it? Memory leaks instrument didn't find any problems and I don't see any strong reference to the VM that can hold it into memory.
Here is code from tableView delegate/datasource:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
id<STReactiveView> cell = [tableView dequeueReusableCellWithIdentifier:_cellId];
STContact *contact;
if(!_searchController.active) {
contact = [[_arrangedData objectForKey:[_sectionTitles objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];
}
else
contact = [_filteredData objectAtIndex:indexPath.row];
STContactsListItemViewModel *cellVM = [[STContactsListItemViewModel alloc] initWithContact:contact services:_services];
[cell bindViewModel:cellVM];
UITableViewCell *returnCell = (UITableViewCell *)cell;
return returnCell;
}
And from cell class:
#interface STContactViewCell()
#property (strong, nonatomic) IBOutlet UILabel *nameLabel;
#property (strong, nonatomic) IBOutlet UIButton *phoneButton;
#property (strong, nonatomic) IBOutlet UIButton *messageButton;
#property (strong, nonatomic) IBOutlet UILabel *statusLabel;
#property (strong, nonatomic) IBOutlet UIImageView *photoView;
#property (strong, nonatomic) IBOutlet UIImageView *statusImage;
#property (strong, nonatomic) STContactsListItemViewModel *viewModel;
#end
#implementation STContactViewCell
-(void)bindViewModel:(id)viewModel {
_viewModel = viewModel;
[self initialize];
}
-(void)initialize {
self.nameLabel.text = _viewModel.contact.name;
self.statusLabel.text = _viewModel.contact.statusText;
self.photoView.contentMode = UIViewContentModeScaleToFill;
if(_viewModel.contact.photo)
[self.photoView setImage:_viewModel.contact.photo];
else {
if(_viewModel.contact.url.length)
[self.photoView sd_setImageWithURL:[NSURL URLWithString:_viewModel.contact.url]];
else
[self.photoView setImage:[UIImage imageNamed:#"ContactPlaceholder"]];
}
[_statusImage setImage:[UIImage imageNamed:[NSString stringWithFormat:#"Status%#",_viewModel.contact.status]]];
[_phoneButton setImage:[UIImage imageNamed:[NSString stringWithFormat:#"Phone%#",_viewModel.contact.status]]forState:UIControlStateNormal];
[_phoneButton setImage:[UIImage imageNamed:[NSString stringWithFormat:#"Phone%#Dim",_viewModel.contact.status]]forState:UIControlStateHighlighted];
[_messageButton setImage:[UIImage imageNamed:[NSString stringWithFormat:#"Message%#",_viewModel.contact.status]]forState:UIControlStateNormal];
[_messageButton setImage:[UIImage imageNamed:[NSString stringWithFormat:#"Message%#Dim",_viewModel.contact.status]]forState:UIControlStateHighlighted];
_photoView.layer.cornerRadius = _photoView.frame.size.width/2;
_photoView.layer.masksToBounds = YES;
[_photoView setContentMode:UIViewContentModeScaleAspectFill];
_phoneButton.rac_command = _viewModel.executePhoneCall;
_messageButton.rac_command = _viewModel.executeMessageSend;
if([_viewModel.contact.status isEqualToString:#"Unavailable"]) {
[_phoneButton setEnabled:NO];
[_messageButton setEnabled:NO];
}
}
And last but not the least, screenshot of allocations:

Objective-c UITableView Cell image superimpose

i'm working on UITableView who display entry with a price range value for each.
i'm using a loop to display the price range. and i searching where is my mystake because, when i'm scrolling up and down and each cells who have been display,hidden and redisplay again, superimpose the image of the "dollar" and it ugly, see the picture.
how i set init my cell :
PoiAllTableCell *cell = (PoiAllTableCell *)[tableView dequeueReusableCellWithIdentifier:#"PoiAllCell" forIndexPath:indexPath];
i'm using this code to display dollar imageview :
for (int i =0; i < 4;) {
PriceRangeImageView *img = [PriceRangeImageView new];
if (i < _currentPoi.priceRange) {
[img initWithXMultiple:i withColor:_appDelegate.mainColor];
}
else{
[img initWithXMultiple:i withColor:_appDelegate.blackColor];
}
[cell.priceRangeView addSubview:img];
i++;
}
how i display the image :
#import "PriceRangeImageView.h"
#implementation PriceRangeImageView
- (void)initWithXMultiple:(int)xMultiple withColor:(UIColor *)color{
if (self.image == nil) {
self.frame = CGRectMake(xMultiple*16,0,16,16);
self.image = [UIImage imageNamed:#"dollar_icon"];
self.image = [self.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
self.tintColor = color;
}
}
#end
here is my cell:
#import <UIKit/UIKit.h>
#import "PriceRangeView.h"
#interface PoiAllTableCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel *poiTitleLabel;
#property (weak,nonatomic) IBOutlet UIImageView *poiImageView;
#property (weak, nonatomic) IBOutlet UILabel *distanceLabel;
#property (weak, nonatomic) IBOutlet UILabel *kmStaticLabel;
#property (weak, nonatomic) IBOutlet UILabel *separatorLabel;
#property (weak, nonatomic) IBOutlet UILabel *categoriesLabel;
#property (weak, nonatomic) IBOutlet UIView *priceRangeView;
#property (nonatomic) CGSize distanceLabelSize;
#property (nonatomic) CGFloat distanceLabelWidth;
#end
i'm thinking than at every scroll cell add dollar imageview on the cell, i want to know how to avoid this. if somebody can suggest me a better way ?
because you using
UITableViewCell *cell = [tableView dequeueReusableHeaderFooterViewWithIdentifier:identifier];
So the cell is reusing and it have already an imageview. So use this code to add image view
for (int i =0; i < 4;) {
[[cell.priceRangeView viewWithTag:i] removeFromSuperview];
PriceRangeImageView *img = [PriceRangeImageView new];
img.tag = i;
if (i < _currentPoi.priceRange) {
[img initWithXMultiple:i withColor:_appDelegate.mainColor];
}
else{
[img initWithXMultiple:i withColor:_appDelegate.blackColor];
}
[cell.priceRangeView addSubview:img];
i++;
}
You should not be init a new img every time you are adding one on top of the other. You should make a property for image and call a method to set or set in the setter if the initWith.. code is not too long. But, don't create a new instance overtime it goes off screen.
#property(strong,nonatomic)PriceRangeImageView* img;
-(PriceRangeImageView*)img{
if(!_img){
_img = PriceRangeImageView *img = [PriceRangeImageView new];
}
//do what you need to do to prepare it here.. omg = some image or whatever
return img;
}
then just use self.img as your variable
i found this solution and it works good, i juste remove all subview on my priceRangeView before adding new imageView.
[cell.priceRangeView.subviews makeObjectsPerformSelector:#selector(removeFromSuperview)];
//affichage priceRange
for (int i =0; i < 4;) {
PriceRangeImageView *img = [PriceRangeImageView new];
if (i < _currentPoi.priceRange) {
[img initWithXMultiple:i withColor:_appDelegate.mainColor];
}
else{
[img initWithXMultiple:i withColor:_appDelegate.blackColor];
}
[cell.priceRangeView addSubview:img];
i++;
}

UIButton Label Resets When Clicked Inside A TableViewCell

I have a custom tableview cell, when I click the "quantity button" inside the cell, the value of the cell itself resets to the value defined the prototype cell on storyboard (which is "1"). So imagine I click the button under the image of the first cell, the label of the button changes from "5" to "1". I commented out all the code when button is pressed, so assume nothing ishapening there. Why is this?
My Custom Cell (.h)
#interface BLCartItemCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UIImageView *imgView;
#property (weak, nonatomic) IBOutlet UILabel *title;
#property (weak, nonatomic) IBOutlet UIButton *quantityBtn;
#property (weak, nonatomic) IBOutlet UIButton *removeBtn;
#property (weak, nonatomic) IBOutlet UILabel *price;
#end
My Custom Cell (.m)
#implementation BLCartItemCell
#end
In my view controller:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
// Section 0: Cart cells
static NSString *cartCellIdentifier = #"cartCell";
BLCartItemCell *cell = [tableView dequeueReusableCellWithIdentifier:cartCellIdentifier];
if (cell == nil) {
cell = [[BLCartItemCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"cartCell"];
// [cell.removeBtn addTarget:self action:#selector(removeBtnPressed:) forControlEvents:UIControlEventTouchUpInside];
// [cell.quantityBtn addTarget:self action:#selector(quantityBtnPressed:) forControlEvents:UIControlEventTouchUpInside];
}
// Grab the cart item from cart and set the cell properties
BLCartItem *cartItem = [self.cart.items objectAtIndex:indexPath.row];
cell.title.text = cartItem.title;
cell.quantityBtn.titleLabel.text = [NSString stringWithFormat:#"%i", cartItem.quantity];
cell.price.text = [NSString stringWithFormat:#"$ %.2f", [cartItem totalPrice]];
NSData *image_data = [[NSData alloc] initWithContentsOfURL:cartItem.image];
cell.imgView.image = [UIImage imageWithData:image_data];
// Buttons will keep track of cell index
cell.quantityBtn.tag = indexPath.row;
cell.removeBtn.tag = indexPath.row;
return cell;
I removed the IBActions associated with the button, problem still happens.
I think it has problem when you set buttonTitle like that
cell.quantityBtn.titleLabel.text = [NSString stringWithFormat:#"%i", cartItem.quantity];
try :
[cell.quantityBtn setTitle: [NSString stringWithFormat:#"%i", cartItem.quantity] forState: UIControlStateNormal]];
Read more
Text change on UIButton doesn't stick
Apple Document

how can i add multiple views depending on an array size?

im making an app to sell tickets, there are different ticket types, for each ticket type i would like to ass a:
nameLabel UIStepper amountLabel priceLabel.
so those 4 views * ticket types.. added to the viewcontroller during runtime.
can i do this without doing it in a tableviewcontroller?
cant seem to add them dynamicly below eachother, any hints?
Im assuming your ticket is a UITableViewCell since you are mentioning "tableviewcontroller".
If thats the case your should make a subclass of UITableViewCell and add those 4 views to the subclassed cell.
So that your subclassed cell header would look something like:
#Import <UIKit/UIKit.h>
#interface TicketCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel *nameLabel;
#property (weak, nonatomic) IBOutlet UIStepper *stepper;
#property (weak, nonatomic) IBOutlet UILabel *ammountLabel;
#property (weak, nonatomic) IBOutlet UILabel *priceLabel;
#end
Then you should set this class as your UITableViews prototype cell class in Interface Builder and then drag and drop the UILabels and the UIStepper into the prototype cell. After that you need to connect the outlets to the right UILabels and UIStepper.
And in your uitableviewcontroller you want to reuse this prototype
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellId= #"PrototypeCellStoryboardIdentifier";
TicketCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if (cell == nil) {
cell = [[TicketCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellId];
}
return cell;
}
and finally you want to set the number of rows in your table to the number of objects in your "number-of-ticket-types-array" like this:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [ticketTypes count];
}
if you don't want to use tableview you can do this by using UIScollView as follows
import "ViewController.h"
#interface ViewController ()
{
float y;
}
#property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
- (IBAction)newTicket:(UIButton *)sender;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
y=50.0;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)newTicket:(UIButton *)sender
{
UILabel * nameLabel = [[UILabel alloc]initWithFrame:CGRectMake(10.0,y,50, 30)];
UILabel * priceLabel = [[UILabel alloc]initWithFrame:CGRectMake(10.0,y+32, 50, 30)];
UILabel * amountLabel = [[UILabel alloc]initWithFrame:CGRectMake(10.0,y+64, 50, 30)];
UIStepper * stepper = [[UIStepper alloc]initWithFrame:CGRectMake(10.0,y+96,50,30)];
nameLabel.text = #"name";
priceLabel.text = #"price";
amountLabel.text = #"amount";
[self.scrollView addSubview:nameLabel];
[self.scrollView addSubview:priceLabel];
[self.scrollView addSubview:amountLabel];
[self.scrollView addSubview:stepper];
y = y +130.0;
if (y>=self.view.frame.size.height)
{
self.scrollView.contentSize =CGSizeMake(320.0, y+120.0);
}
}
#end

Changing Image on selected state of UITablView cell

I have a created a custom cell and added one label and Image, I have 4 rows in my table each row has a different image and each row opens a different view controller, so, now what I need is on click of a particular row I want the image to change to do that I tried this, but its not working, so please help me out.
if(indexPath.row == 0)
{
if(cell.selected == true)
{
UIImage *cellImage = [UIImage imageNamed:#"abc.png"];
cell.icon.image = cellImage;
}
else
{
UIImage *cellImage = [UIImage imageNamed:#"abc.png"];
cell.icon.image = cellImage;
}
}
Regards
Ranjit
Try to do following when creating your cell or in tableView:willDisplayCell:forRowAtIndexPath: method:
cell.imageView.image = [UIImage imageNamed:#"abc.png"];
cell.imageView.highlightedImage = [UIImage imageNamed:#"abc.png"];
It will work for your icon property too if it is UIImageView
First create a property in your custom cell for uiImageview and synthesize it..
and the in didSelectRowAtIndexPath delegate method of UITabeView access the property and change the image something like :-
yourCell.yourImageView.image=[UIImage imageNamed:#"yourImage"]
For Sample I am giving my Code :-
#import <UIKit/UIKit.h>
#interface CustomizedCellProductDetails : UITableViewCell {
UILabel *sNO;
UILabel *abcWine;
UILabel *redWine;
UILabel *two;
UILabel *hundred;
UILabel *fourTwo;
UILabel *twoOne;
UIImageView *imgView;
UILabel *itemNo;
UILabel *itemName;
UILabel *itemDesc;
UILabel *department;
UILabel *qtyAvailable;
UIButton *check;
}
#property (nonatomic , retain) UILabel *sNO;
#property (nonatomic , retain) UILabel *abcWine;
#property (nonatomic , retain) UILabel *redWine;
#property (nonatomic , retain) UILabel *two;
#property (nonatomic , retain) UILabel *hundred;
#property (nonatomic , retain) UILabel *fourTwo;
#property (nonatomic , retain) UILabel *twoOne;
#property (nonatomic , retain) UIImageView *imgView;
#property (nonatomic , retain) UILabel *itemNo;
#property (nonatomic , retain) UILabel *itemName;
#property (nonatomic , retain) UILabel *itemDesc;
#property (nonatomic , retain) UILabel *department;
#property (nonatomic , retain) UILabel *qtyAvailable;
#property (nonatomic , retain) UIButton *check;
-(void) clicked;
#end
.m file synthesize it:-
#import "CustomizedCellProductDetails.h"
#implementation CustomizedCellProductDetails
#synthesize sNO,abcWine,redWine,two,hundred,fourTwo,twoOne,imgView,itemNo,itemName,itemDesc,department,qtyAvailable,check;
in tableview delegate :-
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here. Create and push another view controller.
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
*/
CustomizedCellProductDetails * cell = (CustomizedCellProductDetails )[tableView cellForRowAtIndexPath:indexPath];
[cell.imgView setImage:[UIImage imageNamed:#"wine.png"]];
}
Try to do following when creating your cell ...
In Implementation Block...
#implementation ABC
{
NSMutableArray *imageArray;
NSMutableArray *imageSelectedArray;
}
Do this In the below method
(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
[cell.imageView setImage:[UIImage imageNamed:[imageArray objectAtIndex:indexPath.row]]];
[cell.imageView setHighlightedImage:[UIImage imageNamed:[imageSelectedArray objectAtIndex:indexPath.row]]];
}

Resources