How to use Autolayout and self-sizing cells in iOS8 - objective-c

I have setup the auto-sizing cells with UILabels with no problem in iOS8 following this tutorial:
http://useyourloaf.com/blog/2014/08/07/self-sizing-table-view-cells.html
But I am having problems setting up a UIImageView using auto-sizing. I need the images to be different sizes in Landscape compared to portrait (so they retain the same aspect ratio). But each time I get a broken layout constraints error
"<NSLayoutConstraint:0x14554680 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x14564440(200)]>
As the actual code has quite a few different types of cell (that all work with auto-sizing cells) I have setup a simple example of the issue with a blue image view here:
https://github.com/AdrianMW/ios8autosizingCells
I have setup the constraints to the superview and am setting up the height like so:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ImageTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kImageCell forIndexPath:indexPath];
cell.heightConstraint.constant = [self heightForOrientation:self.interfaceOrientation];
return cell;
}
Solutions Tried
I have tried turning off setTranslatesAutosizingMask: on the cell as per some of the other suggestions but when I do I just get a blank space instead of a image. I have also tried adding the following:
[cell setNeedsDisplay];
[cell layoutIfNeeded];
I have seen mention of overriding sizethatfits: to get it to work on this cell using the old way but that doesn't seem to be called on the cell.
Cheers

In your ImageTableViewCell.xib, try changing the constraint:
Image View.Bottom EQUAL Superview.Bottom
Change the priority from 1000 to 999.

Related

AutoLayout for UICollectionView Cells

I have an uicollectionview in my app which shows the photos from the camera roll (using AssetsLibrary) in small square cells by using an uiimageview. My problem is that it all looks fine on an iPhone5/5S but there is huge spacing between the cells when running it on an iPhone 6. The question is: How could I make the cells resize themselves so that the spacing stays the same on all devices? (And the cells grow instead).
After set auto layout for UIImageView to make sure that UIImageView will fit to cell and make the adjustments to the cell size from there
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
int cellWidth = (SCREEN_WIDTH - (3 * PADDING)) / 4;
return CGSizeMake(cellWidth, cellWidth);
}
Make your collection view controller follow the protocol of UICollectionViewDelegateFlowLayout and override the following method. Then you can size your cells based on the size of your view's bounds.
- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
// Calculate size based on your view's bounds here
}
Note: If you're also dealing with rotation you'll have to invalidate your collection view layout when the device rotates in order for this method to be called again and recalculate the sizes.
[self.collectionViewLayout invalidateLayout];

Asynchronously loading image into autolayout UIImageView

I've searched SO and have not seen an answer specifically for Autolayout.
I have a UITableViewCell using Autolayout. The UIImageView is constrained to have its edges 12 pt from Cell edges and the trailing edge of the Label.
Images are retrieved asynchronously and assigned to the UIImageView's image property.
[self downloadImageWithURL:[NSURL URLWithString:imageUrlString] completionBlock:^(BOOL succeeded, UIImage *image) {
if (succeeded) {
weakSelf.newsImageView.image = image;
// [weakSelf.contentView layoutIfNeeded];
}
}];
If there is a blank placeholder image assigned to the image of the cell in cellForRowAtIndexPath, then the asynchronously retrieved image is loaded and visible. If the blank placeholder assignment is commented out, the asynchronously retrieved image never appears.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
SCTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:tableCellIdentifier];
// cell.newsImageView.image = _blankImage;
return cell;
}
I tried layoutIfNeeded (commented out above) and updateConstraintsIfNeeded to see if the image would appear but it did not work. So for now I've been putting a blank image there first, which seems like a hack, and wouldn't be flexible if relying on intrinsic content size of images who's sizes could be different. Are the constraints being optimized out of existence when the first layout occurs? Anyone know what's going on and how to resolve it?
Try calling: invalidateIntrinsicContentSize on your UIImageView in the completion handler. This should force the auto layout system to recalculate the view intrinsic size. Possibly, you could also need calling updateConstraintsIfNeeded after that (but try first without).
If this does not work and your UIImageViews have a known size, you could try subclassing UIImageView and override the -intrinsicContentSize method. Return the known size from it.
Turns out the nib somehow had two outlets referring to the same UIImageView element. One outlet didn't exist in code, yet still linked in the nib UI. Must have been a mishap in refactoring, but removing it fixes it.

Have access to UITableViewCell from heightForRow

I have the following code:
- (CGFloat)tableView:(UITableView *)_tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
PostTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
return cell.imageView.frame.size.height + 40;
}
What I want to do is be able to access the cell's properties somehow. I want the cell's height to be equal to the height of the image in the cell's image view. I don't think this is working.
Don't try to access the cell. Your table's data source or some other object besides your cell should be managing that image. Ask that object for the image and take its size.
Treating your cell as a data container is tangling up your model, view, and controller layers and is likely to make changes more difficult down the line.

How to change UITableViewCell appearance if cellForRowAtIndexPath is not calling

I have a table view in my app. The table view cells has background with pattern image. Table view content is changing and sometimes there are only two or three cells with content info. And table view automatically add other cells to bottom of screen. The problem is the background of these cells is clear but i want to make background same as other cells (with pattern image). Usually i change cells appearance in cellForRowAtIndexPath. But table view don'call this method for cells that created automatically. So, is there any solution?
You could do this in viewDidLoad()
self.searchDisplayController.searchResultsTableView.backgroundColor = [UIColor redColor]; // set your color for tableview here This should color the search results table cells to the color of your preference. Another delegate you could investigate using is: - (void)tableView:(UITableView*)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
cell.backgroundColor = [UIColor redColor]; // your color here
}Hope this helps.
Did you try to subclass your tableview cells and set their background in their initializer?

UITableView: load all cells

Is it possible to load all cells of an UITableView when the view is loaded so that they are not loaded when I'm scrolling?
(I would show a loading screen while doing this)
Please, it's the only way at my project (sorry too complicate to explain why ^^)
EDIT:
Okay let me explain you, what I'm definite doing:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifier = [NSString stringWithFormat:#"Identifier %i/%i", indexPath.row, indexPath.section];
CustomTableCell *cell = (CustomTableCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
NSDictionary *currentReading;
if (cell == nil)
{
cell = [[[CustomTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
UILabel *label;
UIView *separator;
if(indexPath.row == 0)
{
// Here I'm creating the title bar of my "table" for each section
}
else
{
int iPr = 1;
do
{
currentReading = [listData objectAtIndex:iPr-1];
iPr++;
} while (![[currentReading valueForKey:#"DeviceNo"] isEqualToString:[devicesArr objectAtIndex:indexPath.section]] ||
[readingresultsArr containsObject:[currentReading valueForKey:#"ReadingResultId"]]);
[readingresultsArr addObject:[currentReading valueForKey:#"ReadingResultId"]];
//
// ...
//
}
}
return cell;
}
My error happens in the do-while-loop:
"listData" is an array with multiple dictionaries in it.
My problem ist that when I’m scrolling my table slowly down, all is fine, but when I’m scrolling quickly to the end of the view and then I’m scrolling to the middle, I get the error that iPr is out of the array’s range. So the problem is, that the last row of the first section has already been added to the "readingresultsArr", but has not been loaded or wants to be loaded again.
That’s the reason why I want to load all cells at once.
You can cause all of the cells to be pre-allocated simply by calling:
[self tableView: self.tableView cellForRowAtIndexPath: indexPath];
for every row in your table. Put the above line in an appropriate for-loop and execute this code in viewDidAppear.
The problem however is that the tableView will not retain all of these cells. It will discard them when they are not needed.
You can get around that problem by adding an NSMutableArray to your UIViewController and then cache all the cells as they are created in cellForRowAtIndexPath. If there are dynamic updates (insertions/deletions) to your table over its lifetime, you will have to update the cache array as well.
put a uitableview on a uiscrollview
for example , you expect the height of the full list uitableview is 1000
then set the uiscrollview contentsize is 320X1000
and set the uitableview height is 1000
then all cell load their content even not visible in screen
In my case it was that I used automaticDimension for cells height and put estimatedRowHeight to small that is why tableview loaded all cells.
Some of the answers here and here suggest using automaticDimension for cells height and put mytable.estimatedRowHeight to a very low value (such as 1).
Starting with iOS 15 this approach seems not to work anymore. Hence, another way to achieve the table to "load" all cells could be by automatically scrolling to the last cell. Depending on the tables height and how many rows it can show some cells are discarded but each cell would be loaded and shown at least once.
mytable.scrollEnabled = YES;
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:cellCount - 1 inSection:0];
[mytable scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
mytable.scrollEnabled = NO;
If you want to scroll up again just scroll to the top as outlined here.
Following the comment that was made by juancazalla, I found that if you have a tableView that is using automaticDimension, loading all the cells at once can be best achieved by setting estimatedRowHeight to a low value (such as 1).