Expanding UICollectionView and its cell when tapped - ios

I am trying to make a transition animation like the demonstration in the link here. So when I clicked the cell, it expands and covers the whole screen.
Here are my codes(I have to admit that I am not familiar with CollectionView)`
import UIKit
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var mainDesLabel: UILabel!
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var secDesLabel: UILabel!
let searchBar = UISearchBar()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.collectionView.delegate = self
self.collectionView.dataSource = self
self.collectionView.backgroundColor = UIColor.clearColor()
////////////////////////////////////////////////////////////////////////
self.searchBar.frame = CGRect(x: 175, y: 0, width: 200, height: 50)
self.searchBar.searchBarStyle = UISearchBarStyle.Minimal
self.searchBar.backgroundColor = UIColor.whiteColor()
self.view.addSubview(searchBar)
////////////////////////////////////////////////////////////////////////
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as UICollectionViewCell
cell.layer.cornerRadius = 5
return cell
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
//Use for size
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
self.collectionView.frame = self.view.bounds
let cell = collectionView.cellForItemAtIndexPath(indexPath)
cell!.frame = CGRectMake(0, 0, self.view.bounds.width, self.view.bounds.height)
}
}
So I thought use 'didSelectItemAtIndexPath' would help, however it turns out like this
thoughts? Any help would be highly appreciated!

Or what you can do is expand the item and change its frame with UIAnimation.
And when he cell is tapped, you get the views inside the cell to be expanded also using auto layout and I'm hinting towards (clips to bounds).
something like this:
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let item = collectionView.cellForItemAtIndexPath(indexPath) as! cellCollectionViewExpress // or whatever you collection view cell class name is.
UIView.animateWithDuration(1.0, animations: {
self.view.bringSubviewToFront(collectionView)
collectionView.bringSubviewToFront(item)
item.frame.origin = self.view.frame.origin /// this view origin will be at the top of the scroll content, you'll have to figure this out
item.frame.size.width = self.view.frame.width
item.frame.size.height = self.view.frame.height
})
}
I would suggest you that you use UICollectionView Controller, so things are at ease in general with using that.

You cannot just use didSelectItemAtIndexPath or any similar methods to update the size of a UICollectionViewCell once the UICollectionView is done performing the view layout.
To update cell height,
You can first capture which cell had been tapped in didSelectItemAtIndexPath.
Then, you can either reload the entire collection view with the new cell frame being passed in the sizeForItemAtIndexpath.
Or, you can just reload the specific cell with reloadItemsAtIndexPaths, but you still need to pass the updated size of the cell via sizeForItemAtIndexpath.
UPDATE
I now see the question details have been updated by an animation which you desire to have.
I had performed a similar animation by:-
Capturing the cell which had been tapped in didSelectItemAtIndexPath.
Adding a replica view to the UIViewContrller, but with its frame totally coinciding with the cell which had been tapped.
Then, animating this view which had been added. Thus giving an impression that the cell was animated.
Any additional functionality which has to be given can also be written in this view. Thus the code of the cell and the animated view is separated too.

When a cell tapped or a button or any tappable thing got tapped inside the cell, then you get the call from
didSelectItemAtIndexPath or through delegate, then to give the cell the required size, you have to invalidate the layout of the current collectionview. After this, size for item will get called and give the new size for the,
This will update the size of the collectioncell without reloading it.
You can give animation also.
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.invalidateLayout()
}
}

extension UICollectionView {
func transactionAnimation(with duration: Float, animateChanges: #escaping () -> Void) {
UIView.animate(withDuration: TimeInterval(duration)) {
CATransaction.begin()
CATransaction.setAnimationDuration(CFTimeInterval(duration))
CATransaction.setAnimationTimingFunction(.init(name: .default))
animateChanges()
CATransaction.commit()
}
}
}
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
collectionView.transactionAnimation(with: 0.3) {
self.carInfoCollectionView?.performBatchUpdates({}, completion: { _ in })
}
}

Related

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.

autolayout with dynamics subviews

I am trying to make home screen on my ios 10 app like on photo from above. Green view is actually scroll view and i set constraints for it to cover whole View. Everything on scrollView i want to make scrollable. Yellow part is collection view with prototype cell. Number of items on this view is 6. Cell consists of photo and title. Table view is list of news (photo + title). When start app in table view I load 10 last news and rest of the news I getting with "load more" mechanism. I need proper work of app even on landscape orientation. I have problem to define this layout because collection view and tableView have dynamic height and space between them must be fixed. Usually on almost all tutorials people just fixed scrollView and GridView and in that case app looks good on portrait orientation but i need a more flexibility. Is it possible to achieve this through auto layout and constraints and if yes what are correct directions
UPDATE:
Content view
Collection view
What I want to achieve is to make collection view as a two columns and 3 rows in portrait orientation and 3 columns and 2 rows on landscape. Currently I have collectionView with a scroll but I want to be expanded al the time because content of collectionView should consists of 6 highlighted news.
On viewDidLoad I tried to set table view on correct position (after collection view):
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
collection.dataSource = self
collection.delegate = self
tableView.delegate = self
tableView.dataSource = self
self.view.addConstraint(
NSLayoutConstraint(
item: tableView,
attribute: .top,
relatedBy: .equal,
toItem: collection,
attribute: .bottom,
multiplier: 1.0,
constant: 20
))
tableView.frame = CGRect(x: 0,y: collection.collectionViewLayout.collectionViewContentSize.height,width: tableView.frame.width,height: tableView.frame.width ); // set new position exactly
downloadArticles(offset: "0") {}
}
An example of what I want to achieve is:
Currently I have this:
I think I got it working like this:
import UIKit
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UITableViewDataSource, UICollectionViewDelegateFlowLayout, UITableViewDelegate {
#IBOutlet var collectionView: UICollectionView!
#IBOutlet var tableView: UITableView!
#IBOutlet var collectionViewHeightConstraint: NSLayoutConstraint!
#IBOutlet var tableViewHeightConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "gridCell")
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "listCell")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// shrink wrap the collectionView and tableView to fit their content height snuggly
self.collectionViewHeightConstraint.constant = self.collectionView.contentSize.height
self.tableViewHeightConstraint.constant = self.tableView.contentSize.height
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - CollectionView Methods -
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 6;
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath)
return cell
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
cell.backgroundColor = UIColor.lightGray
}
func calculateGridCellSize() -> CGSize {
// -----------------------------------------------------
// Calculate the size of the grid cells
// -----------------------------------------------------
let screenWidth = self.view.frame.size.width
let screenHeight = self.view.frame.size.height
var width:CGFloat = 0
var height:CGFloat = 0
if(UIDeviceOrientationIsPortrait(UIDevice.current.orientation)) {
width = screenWidth / 2.0 - 0.5
height = width
}
else {
width = screenWidth / 3.0 - 1.0
height = screenHeight / 2.0 - 0.5
}
let size:CGSize = CGSize(width: width, height: height)
return size
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return self.calculateGridCellSize()
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
coordinator.animate(alongsideTransition: { (context) in
print("New screen size = \(size.width) x \(size.height)")
self.collectionView.collectionViewLayout.invalidateLayout()
self.collectionViewHeightConstraint.constant = self.collectionView.contentSize.height
self.tableViewHeightConstraint.constant = self.tableView.contentSize.height
self.view.layoutIfNeeded()
}) { (context) in
self.collectionViewHeightConstraint.constant = self.collectionView.contentSize.height
self.tableViewHeightConstraint.constant = self.tableView.contentSize.height
self.view.layoutIfNeeded()
}
}
// MARK: - TableView Methods -
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "listCell", for: indexPath)
return cell
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.textLabel?.text = "list cell \(indexPath.row)"
}
}
For the interface layout, I did this:
Add scrollView to main view
Pin scrollview all four sides main view
Add contentView to scrollView
Pin contentView all four sides to scrollView
Make contentView width equal to scrollView width
Add collectionView to contentView
Add tableView to contentView and vertically below collectionView
Pin left, top, right of collectionView to contentView
Pin left, bottom, right of tableView to contentView and top of tableView to bottom of collectionView
Make collectionView height 667 and create IBOutlet NSLayoutConstraint for collectionView height (so we can update it later)
Make tableView height 667 and create IBOutlet NSLayoutConstraint for tableView height (also to update later)
Make collectionView min item spacing 1 and line spacing 1
Disable scrollingEnabled for collectionView
Disable scrollingEnabled for tableView
Connect collectionView datasource and delegate to controller
Connect tableView datasource and delegate to controller
Here's a screenshot of the layout if it's any help.
Usually I build my UI using pure code and you would be able to copy and paste, hit the run button but since you're using using Storyboard, I showed it using Storyboard, hopefully you can follow my layout setup instructions.
Here's the result:
Is that what you wanted?

UICollectionViewCell - contents do not animate alongside cell's contentView

Problem looks like this: http://i.imgur.com/5iaAiGQ.mp4
(red is a color of cell.contentView)
Here is the code: https://github.com/nezhyborets/UICollectionViewContentsAnimationProblem
Current status:
The content of UICollectionViewCell's contentView does not animate alongside contentView frame change. It gets the size immediately without animation.
Other issues faced when doing the task:
The contentView was not animating alongside cell's frame change either, until i did this in UICollectionViewCell subclass:
override func awakeFromNib() {
super.awakeFromNib()
//Because contentView won't animate along with cell
contentView.frame = bounds
contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
}
Other notes:
Here is the code involved in cell size animation
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.selectedIndex = indexPath.row
collectionView.performBatchUpdates({
collectionView.reloadData()
}, completion: nil)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let isSelected = self.selectedIndex == indexPath.row
let someSize : CGFloat = 90 //doesn't matter
let sizeK : CGFloat = isSelected ? 0.9 : 0.65
let size = CGSize(width: someSize * sizeK, height: someSize * sizeK)
return size
}
I get the same results when using collectionView.setCollectionViewLayout(newLayout, animated: true), and there is no animation at all when using collectionView.collectionViewLayout.invalidateLayout() instead of reloadData() inside batchUpdates.
UPDATE
When I print imageView.constraints inside UICollectionView's willDisplayCell method, it prints empty array.
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
for view in cell.contentView.subviews {
print(view.constraints)
}
//Outputs
//View: <UIImageView: 0x7fe26460e810; frame = (0 0; 50 50); autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x608000037280>>
//View constraints: []
}
This is a finicky problem, and you're very close to the solution. The issue is that the approach to animating layout changes varies depending on whether you're using auto layout or resizing masks or another approach, and you're currently using a mix in your ProblematicCollectionViewCell class. (The other available approaches would be better addressed in answer to a separate question, but note that Apple generally seems to avoid using auto layout for cells in their own apps.)
Here's what you need to do to animate your particular cells:
When cells are selected or deselected, tell the collection view layout object that cell sizes have changed, and to animate those changes to the extent it can do so. The simplest way to do that is using performBatchUpdates, which will cause new sizes to be fetched from sizeForItemAt, and will then apply the new layout attributes to the relevant cells within its own animation block:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.selectedIndex = indexPath.row
collectionView.performBatchUpdates(nil)
}
Tell your cells to layout their subviews every time the collection view layout object changes their layout attributes (which will occur within the performBatchUpdates animation block):
// ProblematicCollectionViewCell.swift
override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
super.apply(layoutAttributes)
layoutIfNeeded()
}
If you want greater control over your animations, you can nest the call to performBatchUpdates inside a call to one of the UIView.animate block-based animation methods. The default animation duration for collection view cells in iOS 10 is 0.25.
The solution is very easy. First, in ViewController.collectionView(_,didSelectItemAt:), write only this:
collectionView.performBatchUpdates({
self.selectedIndex = indexPath.row
}, completion: nil)
And then, in the class ProblematicCollectionViewCell add this func:
override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
super.apply(layoutAttributes)
self.layoutIfNeeded()
}
Enjoy!
You can apply a transform to a cell, although it has some drawbacks, such as handling orientation changes.
For extra impact, I have added a color change and a spring effect in the mix, neither of which could be achieved using the reloading route:
func collectionView(_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath) {
UIView.animate(
withDuration: 0.4,
delay: 0,
usingSpringWithDamping: 0.4,
initialSpringVelocity: 0,
options: UIViewAnimationOptions.beginFromCurrentState,
animations: {
if( self.selectedIndexPath.row != NSNotFound) {
if let c0 =
collectionView.cellForItem(at: self.selectedIndexPath)
{
c0.contentView.layer.transform = CATransform3DIdentity
c0.contentView.backgroundColor = UIColor.lightGray
}
}
self.selectedIndexPath = indexPath
if let c1 = collectionView.cellForItem(at: indexPath)
{
c1.contentView.layer.transform =
CATransform3DMakeScale(1.25, 1.25, 1)
c1.contentView.backgroundColor = UIColor.red
}
},
completion: nil)
}

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.

Collection View reordering cells upon return to view

I currently have a collection view set up to display a dynamic number of objects in its view. Each cell displays an image from the corresponding object. When a cell is tapped, it triggers a segue to the next view in the hierarchy. The cell's corresponding object is passed to the next view However, I am noticing that when I return the view with the collection, the ordering of cells has changed, and now, when I tap one to got the next view, its properties are from objects of other cells.
Below are my methods of the UICollectionView:
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return objectsCount
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Chow Object Reuse ID", forIndexPath: indexPath) as UICollectionViewCell
cell.backgroundColor = UIColor.whiteColor()
var imageView:UIImageView = UIImageView()
imageView.frame = CGRect(x: 0, y: 0, width: 90, height: 90)
//Since loading of images is a time-intensive task, all the thumbnails
//may have not been fetched yet.
if (imageThumbsArray.count == objectsCount) { //Eventually, a more elegant fix will be needed.
imageView.image = imageThumbsArray[indexPath.row]
}
cell.addSubview(imageView)
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
self.objectToBePassed = self.chowObjectsArray[indexPath.row] as PFObject
self.performSegueWithIdentifier("Show Chow Details", sender: self)
}
I also call self.PastObjectsCollection.reloadData()
Why is the reordering and mixing up of cells happening?
Thanks,
Siddharth

Resources