Self sizing tableview inside self sizing tableview cell - ios

Let's say I have hierarchy like this:
*TableViewCell
**TableView
***TableViewCell
and all of them should be resizable. Did someone face this kind of problem? In past I've used many workarounds like systemLayoutSizeFitting or precalculation of height in heightForRowAt, but it always breaks some constraints, because TableViewCell has height constraint equal to estimated row height and there appear some kinds of magic behavior. Any ways to make this live?
Current workaround:
class SportCenterReviewsTableCell: UITableViewCell, MVVMView {
var tableView: SelfSizedTableView = {
let view = SelfSizedTableView(frame: .zero)
view.clipsToBounds = true
view.tableFooterView = UIView()
view.separatorStyle = .none
view.isScrollEnabled = false
view.showsVerticalScrollIndicator = false
view.estimatedRowHeight = 0
if #available(iOS 11.0, *) {
view.contentInsetAdjustmentBehavior = .never
} else {
// Fallback on earlier versions
}
return view
}()
private func markup() {
contentView.addSubview(tableView)
tableView.delegate = self
tableView.dataSource = self
tableView.register(ReviewsTableViewCell.self, forCellReuseIdentifier: "Cell")
tableView.snp.makeConstraints() { make in
make.top.equalTo(seeAllButton.snp.bottom).offset(12)
make.left.equalTo(contentView.snp.left)
make.right.equalTo(contentView.snp.right)
make.bottom.lessThanOrEqualTo(contentView.snp.bottom)
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ReviewsTableViewCell
cell.viewModel = viewModel.cellViewModels[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! ReviewsTableViewCell
cell.viewModel = viewModel.cellViewModels[indexPath.row]
cell.setNeedsLayout()
cell.layoutIfNeeded()
let size = cell.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize, withHorizontalFittingPriority: .defaultHigh, verticalFittingPriority: .defaultLow)
return size.height
}
}
Self sizing tableView class:
class SelfSizedTableView: UITableView {
override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
self.layoutIfNeeded()
}
override var intrinsicContentSize: CGSize {
self.setNeedsLayout()
self.layoutIfNeeded()
return contentSize
}
}

This is actually not an answer to the question, but just an explanation.
(Wrote here because of the character count limitation for the comments).
The thing is that you're trying to insert a vertically scrollable view inside another vertically scrollable view. If you don't disable the nested tableview's scroll ability, you will have a glitch while scrolling, because the system wouldn't know to whom pass the scroll event (to the nested tableview, or to the parent tableview).
So in our case, you'll have to disable the "scrollable" property for the nested tableviews, hence you'll have to set the height of the nested tableview to be equal to its content size. But this way you will lose the advantages of tableview (i.e. cell reusing advantage) and it will be the same as using an actual UIScrollView. But, on the other hand, as you'll have to set the height to be equal to its content size, then there is no reason to use UIScrollView at all, you can add your nested cells to a UIStackView, and you tableview will have this hierarchy:
*TableView
**TableViewCell
***StackView
****Items
****Items
****Items
****Items
But again, the right solution is using multi-sectional tableview. Let your cells be section headers of the tableview, and let inner cells be the rows of the tableview.

here is an example of how to make a tableview inside a table view cell with automatic height for the cells.
You should use the 'ContentSizedTableView' class for the inner tableViews.
class ViewController: UIViewController {
#IBOutlet weak var outerTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
outerTableView.rowHeight = UITableView.automaticDimension
outerTableView.estimatedRowHeight = UITableView.automaticDimension
outerTableView.delegate = self
outerTableView.dataSource = self
}
}
final class ContentSizedTableView: UITableView {
override var contentSize:CGSize {
didSet {
invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
layoutIfNeeded()
sizeToFit()
return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? TableTableViewCell
return cell!
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
}

Use xib files to simplify the hierarchy.
Get a tableView on your storyboard, and create a nib file for your tableViewCell(say CustomTableViewCell). Inside it create a tableView and again create one more tableViewCell xib file. Now, no need of setting labels into your xib file,(if you want only labels in cells and nothing else, if not, there is another way of adding constraints)
Say you have an array of text, some strings are long and some are short.
register nib file in CustomTableViewCell and extend it to use Delegate and DataSource.
register this CustomTableViewCell in ViewController.
While declaring a cell in CustomTableViewCell, just do=
cell.textLabel?.text = content
cell.textLabel?.numberOfLines = 0
Use heightForRowAt to set outer tableViewCell's height, and let the inner tableView to scroll inside.

Related

How to dynamically resize custom UITableViewCells in UITableView?

I built a generic UITableView to which I can pass a model and a custom UITableViewCell and it's working well except for two things: the cells height and the UITableView height.
I guess these two issues are related.
I'm trying multiple things I found on SO to help with it but nothing worked out.
Here is a sample of my generic UITableView:
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(Cell.self, forCellReuseIdentifier: "Cell")
tableView.dataSource = self
tableView.delegate = self
tableView.separatorStyle = .none
loadList(appendItems: true)
initActions()
initLayout()
}
func initLayout() {
self.view.addSubview(tableView)
self.view.addSubview(emptyView)
self.view.addSubview(loadMoreButton)
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.snp.makeConstraints { (make) -> Void in
make.left.right.leading.trailing.top.equalToSuperview()
}
tableView.isScrollEnabled = false
emptyView.translatesAutoresizingMaskIntoConstraints = false
emptyView.isHidden = true
emptyView.textAlignment = .center
emptyView.snp.makeConstraints { (make) -> Void in
make.left.right.leading.trailing.equalToSuperview()
make.top.equalToSuperview().offset(8)
}
self.loadMoreButton.isHidden = true
if(self.hasLoader == true) {
self.loadMoreButton.isHidden = false
loadMoreButton.translatesAutoresizingMaskIntoConstraints = false
loadMoreButton.snp.makeConstraints { (make) -> Void in
make.centerX.equalToSuperview()
make.top.equalTo(tableView.snp.bottom)
}
}
}
Then, when my data is loaded I do:
self.tableView.reloadData()
self.tableView.snp.remakeConstraints { (make) -> Void in
make.left.right.leading.trailing.top.equalToSuperview()
if(self.hasLoader == true) {
make.bottom.equalTo(self.loadMoreButton.snp.top)
}
make.height.equalTo(self.tableView.contentSize.height)
}
Then, my tableView funcs:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return resourceList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! Cell
let currentResource = resourceList[indexPath.row]
cell.setResource(resource: currentResource) // Here I initialize my labels, UIImage, etc.
cell.isUserInteractionEnabled = true
self.cellHeight = cell.contentView.frame.size.height // Something I tried
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.tableView.deselectRow(at: indexPath, animated: true)
let resource = resourceList[indexPath.row]
if (resource.getRouteParam() != "") {
router.setRoute(routeName: resource.getRouteName(), routeParam: resource.getRouteParam())
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return self.cellHeight ?? 200 // Here I'm trying to return an *automatic* height of my custom cell
}
To complete I can say that my cells have different sizes due to different labels length so I can't define a static height.
I also tried to add estimatedHeightForRowAt in my viewDidLoad method but it didn't work either.
When I use UITableView.automaticDimension my cells have always a height of 44.0
1. UITableView configuration
To be able to have automatically sized cells in UITableView you will need to do set the following properties on your table view:
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 200 // or other value
2. Proper AutoLayout inside of UITableViewCell
Make sure yo have auto layout setup correctly in your cells.
3. (Optional) UITableViewDelegate and estimatedHeightForRowAt
Implement this method of UITableViewDelegate to provide the table with with more accurate size, so it will correctly handle fast scrolling.
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 200 // try to calculate more accurate size
}
Some stuff I noticed
This will not really work how you expect it to work so don't bother with it. Here the cells views are not even correctly laid out, so there is a very high chance you will get problematic values (like 0)
self.cellHeight = cell.contentView.frame.size.height
There should be no need to resetting the constraints after you reload data. Setting them in the viewDidLoad should be enough.
self.tableView.reloadData()
self.tableView.snp.remakeConstraints
Resources
How to make UITableViewCells auto resize to their content
Using Auto Layout in UITableView for dynamic cell layouts & variable row heights

tableView does not appear when centered into its parent view

I have a viewController with the following (static) tableView:
class viewController: UIViewController, UITableViewDelegate {
private let tableView: UITableView = {
let tv = UITableView()
tv.separatorStyle = .singleLine
tv.allowsSelection = true
tv.isScrollEnabled = false
return tv
}()
private let tableData = ["row1", "row2", "row3", "row4"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.delegate = self
tableView.tableFooterView = UIView()
view.addSubview(tableView)
NSLayoutConstraints.activate([
tableView.centerXAnchor.constraints(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
tableView.centerYAnchor.constraints(equalTo: view.safeAreaLayoutGuide.centerYAnchor)
)]
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cell")
cell.textLabel!.text = tableData[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableData.count
}
}
When I run the app, this viewController shows a blank screen. I know that the way I am setting up the tableview's constraints is the problem because when I set up the tableView using topAnchor, bottomAnchor, leftAnchor, and rightAnchor (and with some other tweaking) the tableview appears. Any idea why the app is behaving this way?
Your table view is probably there, and centered, but you didn't define a size, so it's probably being set to zero width and height, that's why you don't see it.
You can fix this by setting a constraint on it's width and height, either to a constant or related to it's superview, depending on what you want.
The problem is this is NOT a static table view. If it were, you would not have implemented cellForRowAt. It is a normal table view and it needs a data source and delegate. Plus it needs a height and a width.

How to dynamically change the height of a UITableView Cell containing a UICollectionView cell in Swift?

Currently, I have embedded a UICollectionViewCell in a UITableViewCell within one of the sections of my UITableView. I know how to dynamically change the cell's height in another section of my UITableView because I have a UITextView in another UITableViewCell that dynamically changes the height of the cell based on how much text is in the UITextView.
The problem I have is in regards to the UITableViewCell containing the UICollectionViewCell. My UICollectionViewCell has one row of 4 images that the user can add via the camera or photo library using a UIImagePickerController.
Currently as I have it, when the 5th picture is generated, the UITableViewCell's height remains static, but the user can scroll horizontally in the UICollectionViewCell like so:
My end goal is this:
And my storyboard:
Pretty self-explanatory but if there is only 4 images, the UITableViewCell remains the same as in screenshoot 1, but the cell's height will dynamically change if the UICollectionViewCell's height changes.
I have set the UICollectionView's scroll direction to be vertical only. Before explaining further, here's my partial code:
class TestViewController: UITableViewController, UITextFieldDelegate, UITextViewDelegate, UIImagePickerControllerDelegate
{
override func viewDidLoad()
{
super.viewDidLoad()
....
tableView.estimatedRowHeight = 40.0
tableView.rowHeight = UITableViewAutomaticDimension
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var cell: UITableViewCell = UITableViewCell()
if indexPath.section == 1
{
cell = tableView.dequeueReusableCell(withIdentifier: "TextViewCell", for: indexPath)
let textView: UITextView = UITextView()
textView.isScrollEnabled = false
textView.delegate = self
cell.contentView.addSubview(textView)
}
else if indexPath.section == 4
{
if let imagesCell = tableView.dequeueReusableCell(withIdentifier: "ImagesCell", for: indexPath) as? CustomCollectionViewCell
{
if images_ARRAY.isEmpty == false
{
imagesCell.images_ARRAY = images_ARRAY
imagesCell.awakeFromNib()
}
return imagesCell
}
}
return cell
}
....
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
if indexPath.section == 1
{
return UITableViewAutomaticDimension
}
else if indexPath.section == 4
{
//return 95.0
return UITableViewAutomaticDimension
}
return 43.0
}
....
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
if let selectedImage = info[UIImagePickerControllerOriginalImage] as? UIImage
{
if let cell = tableView.cellForRow(at: IndexPath(row: 0, section: 4) ) as? CustomCollectionViewCell
{
cell.images_ARRAY.append(selectedImage)
cell.imagesCollectionView.reloadData()
}
}
picker.dismiss(animated: true, completion: nil)
}
....
func textViewDidChange(_ textView: UITextView)
{
...
// Change cell height dynamically
tableView.beginUpdates()
tableView.endUpdates()
}
}
class CustomCollectionViewCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
{
#IBOutlet var imagesCollectionView: UICollectionView!
var images_ARRAY = [UIImage]()
var images = [INSPhotoViewable]()
override func awakeFromNib()
{
super.awakeFromNib()
for image in images_ARRAY
{
images.append(INSPhoto(image: image, thumbnailImage: image) )
}
imagesCollectionView.dataSource = self
imagesCollectionView.delegate = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return images_ARRAY.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! ExampleCollectionViewCell
cell.populateWithPhoto(images[(indexPath as NSIndexPath).row]
return cell
}
....
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets
{
return UIEdgeInsetsMake(0.0, 25.0, 0.0, 25.0)
}
}
Originally, my indexPath.section == 4, which contains the UICollectionViewCell returned a height of 95, but I commented that out and replaced it with returning UITableViewAutomaticDimension. I would assume that adjusted the height of the cell to fit the 5th image, but the cell remained a static height even though the UICollectionViewCell' height changed, allowing me to scroll vertically within that static UITableViewCell height.
I know these are some questions I found very similar to my situation, but they didnt help me resolve my particular issue:
Swift: Expand UITableViewCell height depending on the size of the
UICollectionView inside it
Auto Height of UICollectionView inside UITableViewCell
UICollectionView inside a UITableViewCell — dynamic height?
With some of the answers and suggestions, I've added the following:
imagesCell.images_ARRAY = images_ARRAY
imagesCell.awakeFromNib()
// Added code
imagesCell.frame = tableView.bounds
tableView.setNeedsLayout()
tableView.layoutIfNeeded()
However, this did not have any effects. Can anyone point me in the right direction on what code I need and placed where?
Thanks!
I am using these type of cells in my code, Not performing excellent performance wise(as affecting scrolling smoothness) but will let you achieve required design.
Use CollectionView inside tableViewCell with Vertical ScrollDirection and fixed width(I mean not dynamic in nature). This will put overflowing cells in vertical direction after filling horizontal direction.
Take out NSLayoutConstraint from xib(if you are using that) of collectionViewHeight. We will use it in later part.
set UITableViewAutomaticDimension in tableView in heightForRowAtIndexPath method.
And finally set cell's collectionViewHeight while returning cell in cellForRowAtIndexPath method using constraint that we took out in step 2.
Here I am attaching some code that may will help:
UITableView Part:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: xyzTableViewCell.self), for: indexPath) as! xyzTableViewCell
cell.collectionViewHeight.constant = (numberOfCells/5) * cell.cellHeight
return cell
}
UITableViewCell Part:
#IBOutlet fileprivate weak var collectionView: UICollectionView!
#IBOutlet weak var collectionViewHeight: NSLayoutConstraint
And you will need to reload that particular tableViewCell and reload collectionView inside this tableViewCell so that height function of tableView will be called and height of that tableViewCell will be refreshed, and to handle focused condition of that tableViewCell(when tableViewCell is in focus), I am saying this because if it's not in focus(or say cache, there is difference between them though) then cellForRowAtIndexPath method will be called on scrolling(when this cell is going to come in focus) then tableViewCell height will already be taken care of.
Hope this will help to achieve required functionality.

Layouting dynamic subviews in a UITableviewCell, make constrains to margins

In my app, I have a UITableViewCell which can contain an arbitrary UIView (or subclass hereof). I want the view to fit into the UITableViewCell's contentView's margins. And the cell height will be adjusted to the needs of my view.
I've tried various things but with no positive result.
Here's the code. It does not work. :(
It does not fit the margins and the content is wider than the screen size.
What to do?
class CustomView: UIStackView {
class func viewFromNib() -> CustomView {
guard let view = NSBundle(forClass: CustomView.self).loadNibNamed("CustomView", owner: nil, options: nil).first as? CustomView else {fatalError("CustomView not found")}
return view
}
}
class ViewController: UITableViewController {
let customView = CustomView.viewFromNib()
var containerCell: ContainerTableViewCell?
override func viewDidLoad() {
super.viewDidLoad()
containerCell = ContainerTableViewCell.cell(customView, height: nil)
tableView.reloadData()
tableView.estimatedRowHeight = 40
tableView.rowHeight = UITableViewAutomaticDimension
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return containerCell ?? UITableViewCell()
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
}
class ContainerTableViewCell: UITableViewCell {
var singleView: UIView?
class func cell(view: UIView, height: CGFloat?) -> ContainerTableViewCell {
let cell = ContainerTableViewCell()
cell.setup(view, height: height)
return cell
}
func setup(view: UIView, height: CGFloat?) {
singleView = view
contentView.addSubview(singleView!)
let margins = contentView.layoutMarginsGuide
singleView?.topAnchor.constraintEqualToAnchor(margins.topAnchor).active = true
singleView?.leadingAnchor.constraintEqualToAnchor(margins.leadingAnchor).active = true
singleView?.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor).active = true
singleView?.bottomAnchor.constraintEqualToAnchor(margins.bottomAnchor).active = true
setNeedsLayout()
if let height = height {
contentView.heightAnchor.constraintEqualToConstant(height).active = true
}
}
}
You should implement
optional func tableView(_ tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
in your viewController and to know the height of each cell when refreshing the table.
cellForRowAtIndexPath must use a dequeue family method to get your table cell instance:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/dequeueReusableCellWithIdentifier:forIndexPath:
Such that the object will be created through its regular constructor, not your factory method. You don't have that flexibility.
So in your subclass, put your generic constraints in awakeFromNib or an overridden constructor. Don't forget to account for the auto-resizing mask which interferes with programmatic constraints, so this field must be set to false:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/occ/instp/UIView/translatesAutoresizingMaskIntoConstraints
Finally, within the cellForRowAtIndexPath, you can modify the constraints of the UITableViewCell instance returned by the dequeue call, to get your custom height in there.
Good luck!

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