So this handy feature that I used so much just don't work with Autolayout ? Or how it is ? I cannot find any info anywhere about this, but for me it seems that it is not working with UITableViewCell because its content view is not resizing. And I have to check if I have correct layout at runtime and before with "springs and struts" a was able to move window in editor and see if it sizes layout correctly.
Best is not to use UITableViewCell in UI designer at all, design there view using autolayout and the add it to content view of cell. This code uses some custom code...
UITableViewCell *cell = [self dequeueReusableCell:[UITableViewCell className]];
if (!cell) {
cell = [UITableViewCell.alloc initWithStyle:UITableViewCellStyleDefault reuseIdentifier:[UITableViewCell className]];
UIView *view = viewClass.create;
view.width = [cell width];
view.height = [cell height];
[cell.contentView addSubview:view];
}
Related
I've project which is based on storyboard and on one of the views I've placed UITableView with UIViewTableViewCell (style Right Detail).
In Xcode everything looks OK, but when I launch application on simulator or device, the the table cell looks too wide.
Do you have any suggestions what could be reason of this strange behavior? What should I change to get cell be full visible?
EDIT:
I'm attaching cellforRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"MyTableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
Have you put the same width for your custom cell, as the width of a UITableView?
Also, have you managed your "Delete" Label size properly (using autolayout or autoresizing).
With new iPhone 6, we have to pay attention to widtch as well, just like with different heights, when iPhone 5 was introduced.
And I can see from those two screenshots you posted that you develop using iPhone6 sized ViewController and running it on iPhone5.
Im prety sure you didn't manage Autolayout / Autoresizing as you should.
Try doing this, for your "Delete" label:
I had the same problem where the content on the right hand side of my tableView was being cut off.
The reason being, I had set the Autolayout constraints correctly for the views inside my custom tableViewCell. However, I had not set the AutoLayoutConstraints for the tableView itself. Therefore the UITableView wasn't being constrained correctly and in my case was too large.
I'm trying to implement a custom UICollectionView that shows all elements from the asset library. While displaying the items is not that big of a deal, I run into a weird bug which I think has to do something with the layout I'm using.
Let's start with some screenshots to indicate what's going on. I'll just post the links to these images to be easy on your scroll wheels :-)
1. Initial
https://dl.dropboxusercontent.com/u/607872/stackoverflow/01-uicollectionview-start.png
Everything ok.
2. Scrolled down
https://dl.dropboxusercontent.com/u/607872/stackoverflow/02-uicollectionview-scrolleddown.png
The images from the first row (which isn't visible anymore) repeat behind the images in the last row. The first one is invisible because it is entirely overlapped.
3. Scrolled up again
https://dl.dropboxusercontent.com/u/607872/stackoverflow/03-uicollectionview-scrollup.png
The first image (in the background) is from the same row, but should be the third. The 2nd and 3rd image are from the last row.
Here's some code. I hope I didn't miss any relevant stuff - if so, please let me know.
MultiImagePickerController.m (:UICollectionViewController, protocols: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout)
- (void)loadView {
[...] // layout stuff
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
[layout setScrollDirection:UICollectionViewScrollDirectionVertical];
_collectionView = [[UICollectionView alloc] initWithFrame:collectionRect collectionViewLayout:layout];
[_collectionView setDataSource:self];
[_collectionView setDelegate:self];
[_collectionView registerClass:[MultiImagePickerCell class] forCellWithReuseIdentifier:#"MultiImagePickerCellIdentifier"];
[_collectionView setBackgroundColor:[UIColor whiteColor]];
[self.view addSubview:_collectionView];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MultiImagePickerCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"MultiImagePickerCellIdentifier" forIndexPath:indexPath];
if ( !cell ) {
CGSize sizeCell = [MultiImagePickerCell defaultSize];
cell = [[MultiImagePickerCell alloc] initWithFrame:CGRectMake( 0, 0, sizeCell.width, sizeCell.height )];
}
NSString *groupname = [self getKeyFromMutableDictionary:assetList forIndex:indexPath.section];
[cell addImageUsingAsset:[[assetList objectForKey:groupname] objectAtIndex:indexPath.row]];
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGSize sizeCell = [MultiImagePickerCell defaultSize];
return CGSizeMake( sizeCell.width + 10.0, sizeCell.height + 10.0 );
}
MultiImagePickerCell - (void)addImageUsingAsset:(ALAsset *)asset;
This method adds a UIImageView and a UIImage to the cell to show the asset. Nothing special, in my opinion. When I set breakpoints in cellForItemAtIndexPath, I can see that the correct asset is being read from the assetList and the views are calculated correctly. The drawRect method of the cell is NOT implemented.
I think I experienced the same problem a few years back when I tried to programmatically create a UITableViewController, but I can't remember how I solved it.
Any suggestions?
Got it - I had to remove the subviews from my cells.
I am currently working on a UICollectionView with a lot of images. However, it sometimes crashes in this view with memory warning. I am using AFNetworking and UIImageView+AFNetworking category to set image through setImageWithURL: method. One issue can be caching. I am not sure if AFNetworking deals with image caching. Anyway, is there a way to optimize this code in terms of memory management? Or if I am to implement didReceiveMemoryWarning method in this view controller, what can be put in this method? I attach the code for cellForItemAtIndexPath for this collection view.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"RecipeCell" forIndexPath:indexPath];
// setting the image view for the cell using AFNetworking. Does this do caching automatically?
UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:6];
if (PRODUCTION) {
[recipeImageView setImageWithURL:[[self.recipes objectAtIndex:indexPath.row] objectForKey:#"recipe_image"] placeholderImage:[UIImage imageNamed:#"default_recipe_picture.png"]];
} else {
[recipeImageView setImageWithURL:[NSString stringWithFormat:#"http://localhost:5000/%#", [[self.recipes objectAtIndex:indexPath.row] objectForKey:#"recipe_image"]] placeholderImage:[UIImage imageNamed:#"default_recipe_picture.png"]];
}
// configure the back of the cell. fill all the info.
UITextView *recipeNameView = (UITextView *)[cell viewWithTag:8];
recipeNameView.text = [NSString stringWithFormat:#"%#", [[self.recipes objectAtIndex:indexPath.row] objectForKey:#"recipe_name"]];
UILabel *recipeNameLabel = (UILabel *)[cell viewWithTag:2];
recipeNameLabel.text = [NSString stringWithFormat:#"%#", [[self.recipes objectAtIndex:indexPath.row] objectForKey:#"recipe_name"]];
NSDictionary *user = [[self.recipes objectAtIndex:indexPath.row] objectForKey:#"user"];
UIButton *chefNameButton = (UIButton *)[cell viewWithTag:3];
[chefNameButton setTitle:[NSString stringWithFormat:#"%# %#", [user objectForKey:#"first_name"], [user objectForKey:#"last_name"]] forState:UIControlStateNormal];
NSMutableArray *missingIngredientsStringArray = [[NSMutableArray alloc] init];
NSArray *missingIngredients = [[self.recipes objectAtIndex:indexPath.row] objectForKey:#"missing_ingredients"];
for (NSDictionary *missingIngredient in missingIngredients) {
[missingIngredientsStringArray addObject:[missingIngredient objectForKey:#"name"]];
}
NSString *missingIngredientsString = [missingIngredientsStringArray componentsJoinedByString:#","];
UITextView *missingIngredientsView = (UITextView *)[cell viewWithTag:4];
missingIngredientsView.text = [NSString stringWithFormat:#"%u Missing Ingredients: %#", missingIngredients.count, missingIngredientsString];
// configure the front of the cell. chef name button and missing ingredients and likes on front view
UIButton *frontNameButton = (UIButton *)[cell viewWithTag:11];
[frontNameButton setTitle:[NSString stringWithFormat:#"%# %#", [user objectForKey:#"first_name"], [user objectForKey:#"last_name"]] forState:UIControlStateNormal];
[frontNameButton sizeToFit];
frontNameButton.frame = CGRectMake(160 - [frontNameButton.titleLabel.text sizeWithFont:[UIFont boldSystemFontOfSize:13]].width - 7, frontNameButton.frame.origin.y, frontNameButton.frame.size.width, frontNameButton.frame.size.height);
UILabel *likesLabel = (UILabel *)[cell viewWithTag:9];
likesLabel.text = [NSString stringWithFormat:#"%# likes", [[self.recipes objectAtIndex:indexPath.row] objectForKey:#"likes"]];
UIButton *missingIngredientsButton = (UIButton *)[cell viewWithTag:12];
[missingIngredientsButton setBackgroundImage:[UIImage imageNamed:#"badge_green.png"] forState:UIControlStateSelected];
if (missingIngredients.count == 0) {
missingIngredientsButton.selected = YES;
[missingIngredientsButton setTitle:#"" forState:UIControlStateNormal];
} else {
missingIngredientsButton.selected = NO;
[missingIngredientsButton setTitle:[NSString stringWithFormat:#"%u", missingIngredients.count] forState:UIControlStateNormal];
}
// make back view invisible.
UIView *backView = [cell viewWithTag:1];
UIView *frontView = [cell viewWithTag:5];
frontView.alpha = 1.0;
backView.alpha = 0;
// adding flip gesture recognizers
UIView *flipView1 = [cell viewWithTag:12];
UIView *flipView2 = [cell viewWithTag:1];
UITapGestureRecognizer *flipGestureRecognizer1 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(flipCell:)];
UITapGestureRecognizer *flipGestureRecognizer2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(flipCell:)];
[flipView1 addGestureRecognizer:flipGestureRecognizer1];
[flipView2 addGestureRecognizer:flipGestureRecognizer2];
return cell;
}
[Edit] I attach a screenshot of my Instruments run.
You can see that memory allocation increases as I just push segue and press back button repeatedly. Things that just keep increasing are CFData, CALayer, CABackingStore, UITableView. I doubt these are things that are created after segue, and they are not being released... Please help!
You're probably going to want some sort of image caching strategy to avoid re-downloading images. And UIImageView+AFNetworking category does cache images for you. But you may also have the responses being cached in the in-memory URL cache, which in this case is somewhat redundant.
So you might consider reducing or turning off the in-memory URL cache. I had the issue you're describing and the following reduced my memory issues quite a bit:
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil];
[NSURLCache setSharedURLCache:sharedCache];
AFNetworking automatically stores images in an NSCache collection, which automatically removes some or all of the images from memory on a low memory warning. AFNetworking is probably not your issue.
In fact, I don't think displaying images is your issue unless you're downloading lots of very large images and displaying them simultaneously. (If this is the case, you should try optimizing your images for display on the device so they don't need to be resized.)
One issue I see is that you are adding a gesture recognizer to the cell every time it comes into the view, but cells are reused, so when a cell comes in again you are adding unnecessary gesture recognizers to it. You could resolve this by subclassing UITableViewCell and assigning the gesture recognizers as properties. You could also resolve this by checking flipView1 and flipView2 to see if they have gesture recognizers attached before adding them. (I'm not sure if this is enough to cause a memory warning though.)
I'd recommend going to Build -> Profile and selecting the Allocations instrument. On the left, select Objective C only, and hide system calls. Then, scroll through your collection view and look at the instrument to see what's taking up all the memory.
UPDATE
Here's a screenshot of the Allocations tool:
I custom NSTableView just like what I do in UITableView. I implement the datasource and delegate. In the code, I do like this:
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
ZJCellView *cellView = [tableView makeViewWithIdentifier:cellIdentifier owner:self];
//this is a custom view
if (cellView == nil)
{
CGRect rect = CGRectMake(0, 0, tableView.frame.size.width, kTableViewCellHeight);
cellView = [[[ZJCellView alloc] initWithFrame:NSRectFromCGRect(rect)] autorelease];
cellView.identifier = cellIdentifier;
// some other controls
}
return cellView;
I do the custom just like what I do in iOS.
The problem is there is a line in both left and right border, like:
I tried change the frame of the cell view and it seems to be of no use.
As it is the right way to custom the cell in iOS, I wonder where is it wrong in Cocoa.
Can any body help me?
thanks.
Finally, I find it is the Cell Space which can be set in the nib.
In iOS:
Tableviewcells have same with with Tableview. They are autoresized. If you set frame for a cell in cellForRowAtIndexPath: method, changes nothing.
For width you need to change the width of the tableview.
For height you need to implement delegate method heightForRowAtIndexPath: of the tableview or set rowHeight property.
But I don't know how is it for NSTableView, I think it is similar.
I have a custom UITableViewCell that consists of a UIImageView and a UILabel. The cell is 320x104px and the imageView takes up the whole area with the label in front. There are only 8 cells.
in ViewDidLoad I am creating all needed images up front and caching them in a dictionary at the correct dimensions.
When I scroll the UITableView there is a noticable lag every time a new cell is encountered. This makes no sense to me as the image it is using is already created and cached. All that I'm asking of the cell is for its UIImageView to render the image.
I am using a custom cell with its view in a xib and configuring my UITableView to use it with:
[self.tableView registerNib:[UINib nibWithNibName:#"ActsCell" bundle:nil]
forCellReuseIdentifier:myIdentifier];
Cell creation and configuration:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString* reuseIdentifier = #"ActsCell";
ActsCell* cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
// Configure the cell...
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(ActsCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
Act* act = [self.acts objectAtIndex:indexPath.row];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.title.text = act.name;
cell.imageView.image = [self.imageCache objectForKey:act.uid];
}
What could be causing the lag? There would seem to be no benefit in trying to do anything Async as all the time-intensive work is done.
Do you load images from local files by any chance?
By using Instruments I've found that there is some lazy loading mechanism in UIImage - the real image data was decompressed from PNG only at the stage of rendering it on the main thread which caused the lag during scrolling.
So, after the loading of UIImage with -initWithContentsOfFile: method, I've added code to render the contents of this image to offscreen context, saved that context as new UIImage and used it for UIImageView in UITableViewCell, this made the scroll to be smooth and pleasant to the eye again.
In case of reference there is the simple code I'm using to force reading of image contents in separate thread (using ARC):
UIImage *productImage = [[UIImage alloc] initWithContentsOfFile:path];
CGSize imageSize = productImage.size;
UIGraphicsBeginImageContext(imageSize);
[productImage drawInRect:CGRectMake(0, 0, imageSize.width, imageSize.height)];
productImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
And I think the surface of UIImage created in such a way will be in the supported format suited for rendering, which will also offload work needed to render it on main thread.
EDIT: The docs for UIGraphicsGetImageFromCurrentImageContext() say that it should be only used from main thread, but searching on the net or SO shows that starting from iOS 4 the UIGraphics.. methods became thread safe.