I would like to modify the height of the View in Xib Size Inspector flexibly in the code.
I also want to modify the Height Equals in Constraints in the UICollectionView in Xib.
Short Code :
class ShopImagesViewCell: UITableViewCell
{
#IBOutlet weak var imageCollectionView: UICollectionView!
override func awakeFromNib()
{
super.awakeFromNib()
// ......
}
}
extension ShopImagesViewCell
{
func setView(urls : [String], isZzip : Bool)
{
self.imageCollectionView.delegate = self
self.imageCollectionView.dataSource = self
self.imageCollectionView.regCells(cells:
["ShopImageCollectionViewCell","ShopNonImageCollectionViewCell"])
// ......
}
}
extension ShopImagesViewCell : UICollectionViewDelegateFlowLayout, UITableViewDelegate
{
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
return CGFloat(291) // It does not work.
}
}
When Xib is first loaded, it is executed only once to fit in size.
Modifying Xib height length in code and modifying the Height Equals of Constraintsd in UICollectionView in code.
Please tell me where I should use the code.
According to you pasted code, it seems that you placed a custom UITableViewCell into the nib. Assumed on that, you cannot set height directly without the help of UITableViewDelegate.
Where the UITableViewDelegate is implemented or you intended to implement add this delegate method
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// you have the indexPath based on which you can set the height dynamically, based on the cell position.
// return your intended height here
}
Hope it helps.
Related
I have a UITableView which has a UITableViewCell which contains a UIImageView.
The constraints are setup such that the UIImageView has padding 20 points at the top and sides, and a size ratio of 1:1, so the UIImageView will always be square regardless of the device width.
I apply a cornerRadius to the UIImageView so the image is circular.
However.... the autolayout doesn't seem to work on the first load. But after the first load, it works perfectly.
I have tried every known combination of setNeedsLayout or layoutIfNeeded - both inside the UITableViewCell and in the UITableView code. Nothing works the first time it loads.
Please help!
Code looks like this:
class CircularProfileCell: UITableViewCell {
#IBOutlet weak var circularView: UIView!
func setup() {
circularView.layer.cornerRadius = circularView.bounds.height / 2
}
}
class CircularProfileVC: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.separatorStyle = .none
self.tableView.register(UINib(nibName: "CircularProfileCell", bundle: nil), forCellReuseIdentifier: "CircularProfileCell")
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CircularProfileCell", for: indexPath) as! CircularProfileCell
cell.setup()
return cell
}
}
Setup looks like this:
Because corner radius is a layer property it does not always play well with auto layout. In addition, I guess you set it up with frame properties of the view (i.e imageView.layer.cornerRadius = imageView.bounds.height/2).
Hence you should try and set the corner radius on the layoutSubviews() function of the cell. This will make sure to render the correct size
override func layoutSubviews() {
super.layoutSubviews()
imageView.layer.cornerRadius = imageView.bounds.height/2
...
}
This only happens when the tableView.separatorStyle = .none
So to fix it I simply leave the separator on, but set the separator color to clear
self.tableView.separatorStyle = .singleLine
self.tableView.separatorColor = UIColor.clear
Thanks to #CloudBalacing for the help. More info about this problem here
So I have 2 Views that are shown inside a UIView, based on what is selected on the SegmentViewController. I create dummy data, returning 20 row of a custom cell. This works great.
Everything is fine, till I interact with the TableView.
Bellow is my code:
GoalsViewController.swift
import UIKit
class GoalsViewController: UIViewController {
#IBOutlet weak var goalsTableView: UITableView!
let goalCellIdentifier = "goalCell"
override func viewDidLoad() {
super.viewDidLoad()
goalsTableView.delegate = self
goalsTableView.dataSource = self
goalsTableView.register(UINib(nibName: "GoalsViewCell", bundle: nil), forCellReuseIdentifier: goalCellIdentifier)
goalsTableView.reloadData()
}
}
extension GoalsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = goalsTableView.dequeueReusableCell(withIdentifier: goalCellIdentifier, for: indexPath) as! GoalsViewCell
cell.goalTitle.text = "aaaaa"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Selected \(indexPath.row)")
}
}
After any of the empty rows is selected, the didSelectRowAt is not called, so the cells are not there at all. I tried to find a solution, but I was only to find issues about empty lists, before being populated.
What could be the reason for the empty tableview?
I might be wrong here but one thing that I've noticed is that you are not implementing a function which sets the height of each cell.
// Specify the height of your cells
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100 // Or your given cell height.
}
So here is my theory: If you are using constraints something is missing and your cell's height can't be identified by constraints alone or you are not using constraints at all thus you must use heightForRowAt function to specify each cell's height.
I will explain what was the issue for people who probably did not know (like me).
So my UITableView is inside a UIView that changes based on what the user is selecting. In total I had 2 different Views that where switching. The reason that it was emptying it was because my parent ViewController, could not access the delegate for UITableView. To fix that, after adding a subview to the UIView, you need also to move the ViewController to the parent controller. In code it goes like this.
// Empty array of UIViewControllers
var views: [UIViewController]!
// Add the UIViewControllers to the array
views = [UIViewController()]
views.append(EventsViewController())
views.append(GoalsViewController())
for view in views {
// Needed to adjust the size of Subview to size of View
view.view.frame = containerView.bounds
// Add the subviews
containerView.addSubview(view.view)
}
// Bring the view in position 1 to the front of the UIView
containerView.bringSubviewToFront(views[1].view)
// Add Views[1] UIViewController as a child to the parent controller
self.addChild(views[1])
views[1].didMove(toParent: self)
// After done with everything with the UIViewController remove it
views[1].removeFromParent()
addChild Apple.com
didMove Apple.com
I am building a table view scene and I have defined a custom height for the prototype cell. I have created a file for the custom cell and added an image and two labels via IBOutlets in that file. I have wired the table view and the table is calling in the information, however the height is not being applied. Here is my syntax for the cell class and the cellForRow method.
TableView Article Object:
import Foundation
import UIKit
class FeedItem {
var feedItemTitle: String
var feedItemImage: UIImage
var feedItemExcerpt: String = "Lorem ipsum dolor amet flexitarian art party skateboard, ethical pop-up drinking vinegar readymade humblebrag hot chicken. Retro kogi quinoa..."
var feedItemLike: UIImage
var feedItemComment: UIImage
init(feedItemTitle: String, feedItemImage: UIImage, feedItemLike: UIImage, feedItemComment: UIImage) {
self.feedItemTitle = feedItemTitle
self.feedItemImage = feedItemImage
self.feedItemLike = feedItemLike
self.feedItemComment = feedItemComment
}
}
TableView Cell:
import UIKit
class FeedTableViewCell: UITableViewCell {
#IBOutlet weak var articleImageView: UIImageView!
#IBOutlet weak var articleTitleLabel: UILabel!
#IBOutlet weak var articleExcerptLabel: UILabel!
}
TableView Extension:
extension NewFeedViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return articlesArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Article Cell", for: indexPath) as! FeedTableViewCell
cell.articleImageView.image = articlesArray[indexPath.row].feedItemImage
cell.articleTitleLabel.text = articlesArray[indexPath.row].feedItemTitle
cell.articleExcerptLabel.text = articlesArray[indexPath.row].feedItemExcerpt
return cell
}
}
Here is a screenshot of what the table view looks like when the app is ran:
Here is a screenshot of the Prototype cell and the Size Inspector in Xcode:
Use this property
self.table.estimatedRowHeight = 44.0
self.table.rowHeight = UITableViewAutomaticDimension
Or even you can use delegates also
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 44
}
You have to explicitly specify row height in delegate method. Just like this:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
return 100.0;//Choose your custom row height
}
In viewDidLoad() use
tableView.rowHeight = 400
Hope it helps!
1- use dynamic tableView like that in viewDidLoad
tableView.estimatedRowHeight = 400;
tableView.rowHeight = UITableViewAutomaticDimension;
2- don't implement hightforCellAtIndexpath
3- adjust constraints properly in cell xib from top to bottom
4- regarding the collectionview , imageView and reply section give them all a height constraint of zero initially then, hook the height constraint for each on as IBOulet and change it's constant value in cellForRowAtIndexpath according to the current item manage the hide/show by changing the corresponding constant value of the constraint
5- before return cell do this
cell.layoutSubviews()
cell.layoutIfNeeded()
return cell
so cell can be relayouted to reflect the change of it's subviews's constraints
Use Delegate method with heightForRowAt of you UITableView -
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
return 130.0;
}
I need to support UITableViewAutomaticDimension (for dynamic height) with variations in the constraints: some need to be active, some not.
I setup the storyboard with aConstraint not installed, and bConstraint installed. I activate/deactivate them on need in tableView(_:cellForRowAt:).
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView! {
didSet {
tableView.estimatedRowHeight = 10
tableView.rowHeight = UITableViewAutomaticDimension
}
}
}
class MyTableViewCell: UITableViewCell {
#IBOutlet var aConstraint: NSLayoutConstraint!
#IBOutlet var bConstraint: NSLayoutConstraint!
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyTableViewCell", for: indexPath) as! MyTableViewCell
cell.contentView.translatesAutoresizingMaskIntoConstraints = false
if indexPath.row % 2 == 0 {
NSLayoutConstraint.deactivate([cell.aConstraint])
NSLayoutConstraint.activate([cell.bConstraint])
} else {
NSLayoutConstraint.deactivate([cell.bConstraint])
NSLayoutConstraint.activate([cell.aConstraint])
}
return cell
}
}
Issue
The initial visible layout is ignoring all those activations/deactivations, and all the cells are identical to the original storyboard state.
You will notice that the correct constraints are only applied after scrolling.
Attempts
I did try without success some cell.setNeedsUpdateConstraints(); cell.updateConstraintsIfNeeded(); cell.layoutIfNeeded(); ...
Sample project shared on https://github.com/Coeur/dynamic-cell-height
Setup storyboard with both 'aConstraint' and 'bConstraint' installed, but put a lower priority on 'aConstraint' to remove warnings and it works :)
A couple of problems setting up constraints in your code.
1.
UILabel intrinsicContentSize will participate auto layout.
Auto layout system will create a width and height constraints based on intrinsicContentSize.
You explicitly set a height constraint of the label in xib file, causing an ambiguity, then added a vertical centre Y constraint to cover the problem.
2
If you want to try active or deactivate constraints, you may want to do that with a simple UIView.
3
If you want to do various height rows, take advantage of intrinsicContentSize, and set preferredWidth of UILabel.
I have created UIViewController and added UITableView on it (pinned to all four edges with autolayout).
Then I set estimatedRowHeight (44) and rowHeight (UITableViewAutomaticDimension) and returned 5 custom cells. And it worked.
Now, I want to add custom UITableViewHeaderFooterView that would have dynamic height.
I'm doing next:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 88.0
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return R.nib.orderStatusHeaderView.firstView(owner: self)!
}
My OrderStatusHeaderView is a xib view that has UITableView on it pinned to all 4 edges with autolayout.
OrderStatusHeaderView:
final class OrderStatusHeaderView: UITableViewHeaderFooterView {
#IBOutlet weak var tableView: UITableView! {
didSet {
tableView.dataSource = self
tableView.delegate = self
tableView.rowHeight = 44.0
}
}
}
extension OrderStatusHeaderView: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: "\(indexPath.row)")
cell.textLabel?.text = "\(indexPath.row)"
cell.backgroundColor = .red
return cell
}
}
This displays like:
And when I tap or scroll, all red cells disappears. What could it be? And how to make UITableView dynamically load content and UITableViewHeaderFooterView will size itself so it fit UITableView.contentSize. Is it possible?
Check out:
tableView(_:estimatedHeightForFooterInSection:)
and
tableView(_:heightForFooterInSection:)
You also have the equilavant for headerInSection.
Since you are asking for a table's header and footer view, you can skip the delegate methods you describe. Those (as the name implies) are for SECTION headers and footers.
When you set a view that is using AutoLayout as the table's header or footer, the its frame still has a zero height (That's why buttons in such view for example won't work as they are not receiving the touches).
To correctly size a table's header or footer views using AutoLayout you have to apply a trick to actually calculate the height yourself, and set the headerView again. It is described in detail in many posts like these:
https://stackoverflow.com/a/28102157/756039
https://gist.github.com/marcoarment/1105553afba6b4900c10
http://collindonnell.com/2015/09/29/dynamically-sized-table-view-header-or-footer-using-auto-layout/
http://roadfiresoftware.com/2015/05/how-to-size-a-table-header-view-using-auto-layout-in-interface-builder/
Hope this helps.