Fix the aspect ratio of the first row in UITableView - ios

I'm creating a screen, imagine the facebook profile screen.. A tableview and the first cell is an image. The goal is that this image maintains an aspect ratio, so with different screen sizes I don't have to worry about sizing it..
I can't get this to work with any view, even if I set specific constraints, the cell won't set the correct height..
I have a little project showing constraints, in this link..
My VC Code:
class ViewController: UITableViewController{
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.dataSource = self
self.tableView.delegate = self
self.tableView.estimatedRowHeight = 100
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.reloadData()
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
return cell
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
}
My constrains:

If the height of the table view cell is basically a function of the table view's width, it might be easier to implement the heightForRowAtIndexPath delegate method to return the width of the table view multiplied by the ratio you want. That would take constraints out of the picture entirely, and probably be more efficient.

This is what you should do:
Insert a table view header view
Update the frame height keep the desired aspect ratio in the viewWillLayoutSubviews function.
class ViewController: UITableViewController {
#IBOutlet weak var headerView: UIView!
override func viewWillLayoutSubviews() {
var frame = headerView.frame;
// Check and see if the aspect ratio of the frame
// of the header view is the desired aspect ratio.
if frame.size.height != frame.width * 0.5 {
// If it is not, update the frame
frame.size.height = frame.width * 0.5;
headerView.frame = frame;
// reset the header view
self.tableView.tableHeaderView = headerView
}
}
}

Related

dynamic tableHeaderView does not work properly

I have a tableHeaderView that should be with dynamic height according to its content.
I tried use the systemLayoutSizeFitting & sizeToFit method in order to set new height for the table view, Unfortunately It's seems to be work well but not as I want (one of the dynamic UI get cropped). I tried to set the content compression resistance priority of the UIs that i want to be dynamic to (1000) but its dose not work as well.. every time at least one UI cropped.
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var podView: PodView!
#IBOutlet weak var postCaption: UILabel!
var pod: Pod!
override func viewDidLoad() {
super.viewDidLoad()
//set header view
podView.setPod(image: pod.image, title: pod.title, description: pod.description, viewWidth: UIScreen.main.bounds.width)
podView.sizeToFit()
postCaption.text = pod.description
postCaption.sizeToFit()
let height = tableView.tableHeaderView!.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
tableView.tableHeaderView!.frame.size.height = height
}
edit: constraint:
view constraint
label Constraint
For having TableHeaderView with dynamic height. Add following code to your viewController. It will work like charm.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let headerView = tableView.tableHeaderView {
let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
var headerFrame = headerView.frame
//Comparison necessary to avoid infinite loop
if height != headerFrame.size.height {
headerFrame.size.height = height
headerView.frame = headerFrame
tableView.tableHeaderView = headerView
}
}
}
Note: Make sure your tableHeaderView is having proper AutoLayout Constraints to get proper height.
Try this steps for creating dynamic tableview header cell :-
1 - Add one UItableViewCell on tableview from storyboard
2 - Create tableView header UI as per your requirement.
3 - Create class as TableViewHeaderCell something according to your requirement what you want to show in header cell.
4 - Then in a ViewController class implement headerview delegate method.
/**
Tableview header method
*/
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
}
5 - In this method you want to create TableViewHeaderCell object and return cell content View like this.
/**
Table Header view cell implement and return cell content view when you create cell object with you identifier and cell name after that you have to mention height for header cell
*/
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell Identifier") as! CellName
return cell.contentView
}
6 - Implement Tableview header height method
/**
Here you can specify the height for tableview header which is actually your `TableViewHeader Cell height`
*/
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
//For eg
return 100
}
From this steps you can acheive dynamic Header view cell as well as footer view cell also, but for footer view cell implement Tableview footer view delegate methods.
Thank You,

How to change height of view in tableview header?

I have a tableView header. I put a view and several items in. However, when a button is tapped, some objects in the view are hidden so that the view doesn't contain it as well anymore:
How would I change the view height to match the items in the view?
Implementing this delegate function can help you...
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return Your_View.frame.height Or Others
}
Try dynamically sizing the headerview by overriding viewDidLayoutSubviews() and see if that works :)
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Dynamic sizing for the header view
if let headerView = tableView.tableHeaderView {
let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
var headerFrame = headerView.frame
// If we don't have this check, viewDidLayoutSubviews() will get
// repeatedly, causing the app to hang.
if height != headerFrame.size.height {
headerFrame.size.height = height
headerView.frame = headerFrame
tableView.tableHeaderView = headerView
}
}
}
You need to set the sectionHeaderHeight property in viewDidLoad. You will need to have tableView reference if you are using storyboard unless if your ViewCOntroller is of type TableViewController then just use below code.
# Set the Header height to 50 points
tableView.sectionHeaderHeight = 50

Custom UITableViewCell from XIB: contentView width is always the same

I've created a custom UITableViewCell with an xib file. In there I have placed several labels and views relative to the width of the contentView. Unfortunately the contentView.bounds.width property stays always the same no matter which device is selected.
My ViewController is also loaded from nib:
class ViewController: UIViewController {
#IBOutlet var tableView: UITableView!
init(title: String) {
super.init(nibName: "ViewController", bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerNib(UINib(nibName: "CustomTableViewCell", bundle: nil), forCellReuseIdentifier: "CustomTableViewCell")
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CustomTableViewCell", forIndexPath: indexPath) as! CustomTableViewCell
return cell
}
}
In the CustomTableViewCell class I print out the width property of the content view:
override func awakeFromNib() {
super.awakeFromNib()
print("self.contentView.bounds.width: \(self.contentView.bounds.width)")
}
This always prints out the width set in the Interface Builder, even though it should follow the width of the tableView which uses AutoLayout:
Trying to set the cell's frame before returning didn't work:
cell.frame = CGRectMake(0, 0, self.tableView.frame.size.width, 71)
If you are using autolayout then count your width in cellforrowAtindexpath and you will get proper width. awakeFromNib gets called before your cell get autolayout so it is giving same width as set in interface builder.
Second thing if you are using autolayout then setting frame has no meaning. if you want to change height or width then you should take outlet of your constraint (height or width) and you can change it's constant to desired value!
If you want to change height only then you can play with heightForRowAtIndexPath with if else which return desire height as per condition!
Swift 4+
You can use following code, it will set the frame of XIB cell and you can adjust content also with help of XIB height and width.
let cellRect = CGRect(x: 0, y: 0, width: postTableView.frame.size.width, height: imageHeight)
cell?.frame = cellRect
Hope it will work

adjusting tableView height while using self sizing cells and a non scrollable table

The problem I have is that I am using self-sizing cells, and estimated row height.
My tableview is a non scrollable table in a scrollview. I have a variable for the table height which I initially set to 0.
I am trying to update my tables height to be the right size depending on how many cells will fill it and depending on the size those cells will take up. I am trying to use tableview.contentSize.height to get the tables height and set the variable to that size.
I then recall my alignment function to reset the tables height. When I build and run the tables size isn't big enough to show all of the cells that it should.
How can I fix this?
You can do it from the following steps ->
Construct the tableview within the UIView with 0 Leading, Top, bottom, Trailing constant
Add height constraint of the UIView and define outlet of it
3. Load Data in the tableview and get tableview's ContentHeight
Update the height constraint of the UIView equal to the tableview's ContentHeight
Dont forget to call layoutIfNeeded() after updating constraint
class ViewController: UIViewController {
#IBOutlet weak var containerViewHeightConstraint: NSLayoutConstraint!
#IBOutlet weak var nameTableView: UITableView!
var dataArrary = [String]()
override func viewDidLoad() {
super.viewDidLoad()
nameTableView.rowHeight = UITableViewAutomaticDimension
nameTableView.estimatedRowHeight = 44.0
nameTableView.tableFooterView = UIView(frame:CGRectZero)
nameTableView.tableHeaderView = UIView(frame:CGRectZero)
dataArrary = ["item1 bshfklsdflkjsdfkljsdklfjlsdkfjlksdjflksdjflkdsjflkdsjflksdjflkjdslkfjdslkjflkdsfjdsfkljsdflkjdslkfj", "item2hfdshjgfhjdsgfhjdgfhjdgfhjdgfhjgfhsdf", "item3", "item4", "item5", "item6kfjdskfljsdlkfjlksdfjlksdjfkldsjfklfjdkslf"]
}
override func viewDidAppear(animated: Bool) {
updateView()
}
func updateView() {
self.nameTableView.scrollEnabled = false
self.nameTableView.frame.size = CGSizeMake(self.view.frame.width, self.nameTableView.contentSize.height)
containerViewHeightConstraint.constant = self.nameTableView.frame.height
// assign height equal to the tableView's Frame
self.view.layoutIfNeeded()
// redraws views and subviews with updated constraint
}
}
extension ViewController:UITableViewDataSource {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArrary.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell
cell.textLabel?.numberOfLines = 0
cell.textLabel?.text = dataArrary[indexPath.row]
return cell
}
}
It should look like this

tableview's Row height is not changing , CollectionView is capturing all the unused space instead of resizing itself in the tableViewCell

I put a UICollectionView into the UITableViewCell by following this tutorial and in my UICollectionViewCell, there's a Image View. So when I run my app, the collection view is not resizing itself at the same time in my cell I put a Text View which is resizing itself according to content, see the below images:
In this first image, I have a text view at the top which have some text in it, and below it with (pink background) is my collection view and inside of that with greenBackground is my image view, as you can see that collection view is taking the extra space instead of reducing itself as Text View Did.
in this second image you can see that my textView haves more content then before so its resized itself now overlapping the CollectionView
this is my TableViewCell:
class TableViewCell: UITableViewCell {
#IBOutlet var txtView: UITextView!
#IBOutlet private weak var collectionView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
// collectionView.frame = self.bounds;
// collectionView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func setCollectionViewDataSourceDelegate
<D: protocol<UICollectionViewDataSource, UICollectionViewDelegate>>
(dataSourceDelegate: D, forRow row: Int) {
collectionView.delegate = dataSourceDelegate
collectionView.dataSource = dataSourceDelegate
collectionView.tag = row
collectionView.reloadData()
}
var collectionViewOffset: CGFloat {
get {
return collectionView.contentOffset.x
}
set {
collectionView.contentOffset.x = newValue
}
}
}
this is my collectionViewCell
class CollectionViewCell: UICollectionViewCell {
#IBOutlet var imgView: UIImageView!
override func awakeFromNib() {
self.setNeedsLayout()
//
// self.contentView.frame = self.bounds;
// self.contentView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
}
}
and this is my TableviewController
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return imageModel.count
}
override func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell",
forIndexPath: indexPath) as! TableViewCell
cell.txtView.text = txtArray[indexPath.row]
return cell
}
override func tableView(tableView: UITableView,
willDisplayCell cell: UITableViewCell,
forRowAtIndexPath indexPath: NSIndexPath) {
guard let tableViewCell = cell as? TableViewCell else { return }
tableViewCell.setCollectionViewDataSourceDelegate(self, forRow: indexPath.row)
tableViewCell.collectionViewOffset = storedOffsets[indexPath.row] ?? 0
}
override func tableView(tableView: UITableView,
didEndDisplayingCell cell: UITableViewCell,
forRowAtIndexPath indexPath: NSIndexPath) {
guard let tableViewCell = cell as? TableViewCell else { return }
storedOffsets[indexPath.row] = tableViewCell.collectionViewOffset
}
}
extension TableViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return imageModel[collectionView.tag].count
}
func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell",
forIndexPath: indexPath) as! CollectionViewCell
cell.imgView.frame = CGRectMake(0, 0, collectionView.frame.width, collectionView.frame.height)
cell.imgView.contentMode = .ScaleAspectFit
//cell.addSubview(imageView)
cell.imgView.image = ResizeImage(UIImage(named: imageModel[collectionView.tag][indexPath.item])!, targetSize: CGSizeMake( cell.imgView.frame.width , cell.imgView.frame.height))
//imageView.image = UIImage(named: imageModel[collectionView.tag][indexPath.item])
return cell
}
}
How can I make this collection view to AutoLayout itself according to the content in it? I also tried this:
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 100;
but didn't worked (my collection view got disappear) if anybody knows how to do this, then please guide me..
I faced a similar issue when i used collection view inside a table view cell. No matter what i did i couldn't get the table view to resize automatically but the collection view did. Soo instead of autolayout i did it using code.
I ended up having to calculate the size of the label in the collection view numberOfSections in collection view and passing this height using a delegate to the view controller that has the tableView's delegate and dataSource and reloading the appropriate row of the table view.
As it happens, the numberOfSections in collectionview data source gets called everytime and the delegate resizes the table view height.
Some thing like this-
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
[self.delegate setViewHeight:[self getHeightForCellAtIndexPath:[NSIndexPath indexPathForRow:currentSelected inSection:0]]];
return 1;
}
This ought to give you a general idea.
EDIT: Sorry i misunderstood, your question before. Here is something that should work for you:
As per my understanding, you have a table view cell with a label view and collection view inside of it.
Now, inside your table view cell, you should add top, leading and trailing constraints space to the label. Now inside your collection view position your image vertically in the center and add an appropriate top and bottom to the cell superview. Your collection view itself should have a CONSTANT value in leading, trailing, top to label and bottom to superview. Also add a fixed height constraint to your collection View (assuming you want the image sizes to remain the same).
Now lets says View Controller A is the data source for your table view and the table view cell is the data source for your collection view.
In your viewController A, you should write your height for row at indexPath as-
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
CGSize stringSize = [yourStringArray[indexPath.row] boundingRectWithSize:CGSizeMake(_yourCollectionView.frame.size.width, FLT_MAX)
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
attributes:#{NSFontAttributeName:[UIFont yourFont size:yourFontSize]} context:nil].
return stringSize.height + 35; //magic number 35, play around with it to suit your need. Did this to always have a minimum fixed height.
}
This will allow your tableViewRowForHeight for that particular index to have the height of your label added to it and the constraints ought to do the rest.

Resources