How do I make a UIImageView fill a UITableViewCell? - ios

This is my first post, so if there is something that I should do differently, please let me know!
I'm making a photo app, where the I want the photos to fill the UITaleViewCells, and the Cells to expand to fit the images.
I've been struggling to make it work, I've googled everything I can thing of, watched a few youtube videos, but I can't find any that work with UIImageView's. Here is the code I'm working with right now:
override func viewWillAppear(_ animated: Bool) {
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
}
On the UIImageView I have these constraints:
Bottom Margin, Top Margin, Trailing Margin and Leading Margin, when I run my app I get this really weird space at the top and the bottom of my images.
Here is some screenshots:

Well i suggest you to try with UICollectionView for this because for such layouts its better to use Collection View instead Table Views, try this (tempArr means your image array),
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
// create a cell size from the image size, and return the size
let picHeight = tempArr[indexPath.row].valueForKey("imgHeight") as! String
let picWidth = tempArr[indexPath.row].valueForKey("imgWidth") as! String
var picHeightFloat :CGFloat!
var picWidthFloat :CGFloat!
if let n = NSNumberFormatter().numberFromString(picHeight) {
picHeightFloat = CGFloat(n)
}
if let q = NSNumberFormatter().numberFromString(picWidth) {
picWidthFloat = CGFloat(q)
}
let imageSize = CGSize(width: picWidthFloat, height: picHeightFloat)
return imageSize
}

Related

Dynamic cell height in UICollectionView

I'm having a custom UICollectionViewCell which contains many objects but overall, it needs to connect to the database, grab stored image urls and populate a UIImageView by appending each photo (the number of photos is optional - from 1 to 10) to a UIStackView.
Now, I get everything except that the height of the cell is hard coded, so, it's not working. I'm not sure how to get the size though. Here's my code.
This is where the height is hardcoded.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
let width = collectionView.bounds.width
let size = CGSize(width: width, height: 800)
return size
}
This is where the UIImageView is being setup. photoURLs is the array of the downloaded URLs from the database.
if let photoURLString = post?.photoURLs
{
for urlString in photoURLString
{
let photoURL = URL(string: urlString)
let imageView = UIImageView()
imageView.sd_setImage(with: photoURL)
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.widthAnchor.constraint(equalTo: postImagesStackView.widthAnchor)
postImagesStackView.addArrangedSubview(imageView)
postImagesStackView.layoutIfNeeded()
}
}
Also, how to leave a little bit of space between each photo? Like 1 point?
I think that you need to calculate cell height.
In this case, sum images height before you call reload collectionView, or fix imageView height and multiplies images count with height.
Another solution is make cell for each image.
Instead of stackview, you could using section and custom collectionview layout.
Like woosiki indicated, you could try just calculating a value based on image sizes returned. Once the you get the response and compute the full height, then call collectionView.reloadData() and reference that fullHeight property for each backing object. Something like below, though make it safer with if let/where and default/max height.
func collectionView(_ collectionView: UICollectionView, layout
collectionViewLayout: UICollectionViewLayout, sizeForItemAt
indexPath: IndexPath) -> CGSize
{
let imageObject = dataSource.items[indexPath.row]
let width = collectionView.bounds.width
let size = CGSize(width: width, height: imageObject.fullHeight)
return size
}
For a border/spacing, could include slightly larger parent view if you end up including captions or metadata anyway or just try documentation example:
https://developer.apple.com/documentation/uikit/nslayoutanchor

Simply using storyboard, constraints to set size of cell in UICollectionView

Here's a UICollectionView, and the cell in purple:
Quite simply, I want the cells to be 1/2 of the collection view width. (So TBC, it will be a two rows arrangement of cells in the collection view.)
(The collection view is simply fullscreen, so each cell is half the screen width.)
How do you do this in storyboard?
If I try to control-drag in the normal way, it basically doesn't work.
These are simple totally static cells (not dynamic).
For anyone googling here, to save your time: Here's exactly (2016) the simplest way to make a two-across UICollectionView layout; no gaps between the cells.
// Two - two-across UICollectionView
// use a completely standard UIViewController on the storyboard,
// likely change scroll direction to vertical.
// name the cell identifier "cellTwo" on the storyboard
import UIKit
class Two:UICollectionViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
let w = collectionView!.bounds.width / 2.0
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.itemSize = CGSize(width:w,height:w)
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
collectionView!.collectionViewLayout = layout
// Note!! DO NOT!!! register if using a storyboard cell!!
// do NOT do this:
// self.collectionView!.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
}
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int
{ return 1 }
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{ return 5 }
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
return collectionView.dequeueReusableCellWithReuseIdentifier("cellTwo", forIndexPath: indexPath)
}
}
You can't do it in the storyboard. The collection view width is not known until runtime, and collection view cells are not under autolayout, so you cannot express the notion "1/2 the width" of anything else. (If you did know the collection view width in advance, you could use the flow layout in the storyboard to set the cell size absolutely, by dividing in your head; but you don't know it, because the width differs depending on the device.)

Dynamic Height for UICollectionView Cell

This is my first attempt at creating a collectionView programmatically. I used a couple tutorials to get where I am now which is a Facebook/LinkedIn-esque layout with a textView and imageView. They look great when there is an image but I have no idea how to make the cell dynamically smaller in height when there is no image. I know that I currently have a fixed height of 500 but it being my first attempt, I am not sure how to set it to dynamically adjust.
I've seen a lot of Obj-C examples but nothing (that I can understand) for Swift that I know how to implement.
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
if let textContent = dataSource.posts[indexPath.item].textContent {
let rect = NSString(string: textContent).boundingRectWithSize(CGSizeMake(view.frame.width, 1000), options: NSStringDrawingOptions.UsesFontLeading.union(NSStringDrawingOptions.UsesLineFragmentOrigin), attributes: [NSFontAttributeName:UIFont.systemFontOfSize(14)], context: nil)
return CGSizeMake(view.frame.width, rect.height + 370)
}
return CGSizeMake(view.frame.width, 500)
}
}
My vertical visual layout constraints (created with an extension):
addContstraintsWithFormat("V:|-8-[v0(44)]-4-[v1]-4-[v2][v3(44)][v4(1)][v5(44)]|", views: profileImg, postTextView, postImageView, likesCommentsLabel, dividerLineView, likeButton)
sizeForItemAtIndexPath is one of the collection view delegate function in which you can set the height of each cell. In this delegate just add a condition.. where you can check if the cell has an image or not and based on that set the height using CGSizeMake

Resize images automatically in CollectionViewCell in CollectionView with Auto Layout

I have a collectionView with horizontal UICollectionViewFlowLayout.
I am trying to achieve:
If a device orientation is portrait, UIImageView width will be qual to view.width and let the height be calculated automatically (like it usually happens with Auto Layout). And the same for the landscape mode. Example - standard photo app on the Iphone.
Unfortunately i don't see how to achieve it with autoLayout. I set constraints on UIImageView for it to be equal in size to the cell. But looks like the sell itself cannot be pinned to the Parent View.
After reading similar questions looks like cells must be resized programmatically using
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSize(width: width, height: height)
}
And here i am stuck because i know the width of the screen but don't know how to calculate the height dynamically.
About image height:
I have my image declared like this:
var pageImages = [UIImage]()
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: ImageDetailViewCell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! ImageDetailViewCell
let curr = indexPath.row
let imgName = data[albumNumber][curr]["image"]
cell.DetailImageView.image = UIImage(named: imgName!)
return cell
}
If you use the proper UIImageView resize setting (say, aspect fit/fill), then you just need to set the cell's height to your collectionView's (you get a pointer to it as one of the ...sizeForItemAtIndexPath... method parameters) height. You also should call the - layoutIfNeeded method on your cell afterwards.
You can use sizeForItemAtIndexPath: to change the size of collection view cell.
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
var numberOfCellInRow : Int = 3
var padding : Int = 5
var collectionCellWidth : CGFloat = (self.view.frame.size.width/CGFloat(numberOfCellInRow)) - CGFloat(padding)
return CGSize(width: collectionCellWidth , height: collectionCellWidth)
}
You can get the size of cell via :
((UICollectionViewFlowLayout) self.collectionViewName).itemSize.height)
You can get the image size via :
let sizeOfImage = image.size
let height = image.height
If you want to change the height then change it manually by return CGSize(width: collectionCellWidth , height: cellheight)

How would I properly use the Photos framework in iOS 8 with UICollectionView, preserving image aspect ratios, like in Messages.app?

I want to create a similar look to what Apple has in Messages.app, where you have a horizontal sliding view of images.
I understand in iOS 8 the best way to do this would be using the new Photos framework, and likely alongside a horizontal UICollectionView.
I have my UICollectionView pinned to a set height in my view controller storyboard, and positioned from the top, and then in viewDidLoad I write:
let assetCollectionsResult = PHAssetCollection.fetchAssetCollectionsWithType(.SmartAlbum, subtype: .SmartAlbumRecentlyAdded, options: nil)
let recentlyAddedCollection = assetCollectionsResult.firstObject as! PHAssetCollection
recentlyAddedFetchResult = PHAsset.fetchAssetsInAssetCollection(recentlyAddedCollection, options: nil)
Where recentlyAddedFetchResult is an instance variable on the view controller.
I then created a custom UICollectionViewCell subclass that simply has a UIImageView pinned with Auto Layout to the cell's edges.
In the UICollectionViewDataSource methods I have:
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return recentlyAddedFetchResult.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("ImageCell", forIndexPath: indexPath) as! ImageCollectionViewCell
let asset = recentlyAddedFetchResult[indexPath.row] as! PHAsset
PHImageManager.defaultManager().requestImageForAsset(asset, targetSize: CGSize(width: 300.0, height: 300.0), contentMode: .AspectFill, options: nil) { (result, info) in
cell.imageView.image = result
}
return cell
}
However, I understand now I'd have to implement sizeForItem, but I'm confused how to handle that. The best way I could come up with was performing the above fetch again, but with a smaller image size as I just need the ratio, then returning a size based on that. But the fetch is asynchronous, so I'd have to save it then return it later, right?
I tried that as follows:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
if let cellSize = cellSizeCache[indexPath] {
return cellSize
} else {
let asset = recentlyAddedFetchResult[indexPath.row] as! PHAsset
PHImageManager.defaultManager().requestImageForAsset(asset, targetSize: CGSize(width: 20.0, height: 20.0), contentMode: .AspectFill, options: nil) { (result, info) in
let imageRatio = result.size.width / result.size.height
let width = 140 * imageRatio
self.cellSizeCache[indexPath] = CGSize(width: width, height: 140.0)
collectionView.reloadItemsAtIndexPaths([indexPath])
}
return CGSize(width: 140, height: 140)
}
}
But the console just gets flooded with an infinite loop of this message:
the item height must be less than the height of the UICollectionView minus the section insets top and bottom values.
But it is. The collection view is 150 tall with no section insets. Even if I remove the caching so it just returns 140x140 every time it still complains about that. Why? If I turn it down to around 70x70 it works fine, but I haven't a clue why.
On top of that, the first two images in the scroller rarely show up on the first load, only once I scroll do they.
What am I doing wrong here? There's very few tutorials on the new Photos framework, so despite my best efforts I cannot figure out what I'm doing wrong.

Resources