Reuse item inside a row when scrolling - ios

I have collectionView inside tableView. collectionView need to horizontal scroll image, tableView for vertical scroll posts. When I had 3 rows I had no problems, but when i create 4 rows i have a problem with scrolling items inside rows. If I start scrolling on 4 row, scrolling is repeated on row 1 and the same thing if i start scrolling on 1 row scrolling is repeating on row 4.
What could be the problem and how to solve it? May be
Can check .gif file. I start on 1 row on name "Oko" and if i scrolling down on 4 row and scroll right collectionCell and return on 1 row i see next image name "City", but there must be name "Oko"
My code:
ViewController:
class PhotoStudiosViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdating {
#IBOutlet weak var tableView: UITableView!
var theStudios: [Studio] = []
var filteredStudios: [Studio] = []
var studiosRef: DatabaseReference!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
tableView.estimatedRowHeight = 475
tableView.rowHeight = UITableViewAutomaticDimension
}
override func viewDidLoad() {
super.viewDidLoad()
studiosRef = Database.database().reference(withPath: "PhotoStudios1")
studiosRef.observe(.value, with: { (snapshot) in
for imageSnap in snapshot.children {
let studioObj = Studio(snapshot: imageSnap as! DataSnapshot)
self.theStudios.append(studioObj)
}
self.tableView.reloadData()
})
}
// MARK: - TableView
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.isActive && searchController.searchBar.text != "" {
return filteredStudios.count
}
return theStudios.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath) as! PhotoStudiosTableViewCell
cell.currentPageNumber.text = "1/\(theStudios[indexPath.row].halls.count)"
if searchController.isActive && searchController.searchBar.text != nil {
cell.theHalls = filteredStudios[indexPath.row].halls
} else {
cell.theHalls = theStudios[indexPath.row].halls
}
cell.nameLabel.text = theStudios[indexPath.row].studioName
cell.addressLabel.text = theStudios[indexPath.row].studioAddress
cell.logoLabel.sd_setImage(with: URL(string: theStudios[indexPath.row].studioLogo))
cell.didSelectAction = {
(innerPath) in
self.showDetailsView(indexPath, cellPath: innerPath)
}
return cell
}
TableViewCell:
class PhotoStudiosTableViewCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource, UIScrollViewDelegate, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var button: UIButton!
#IBOutlet weak var logoLabel: UIImageView!
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var addressLabel: UILabel!
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var currentPageNumber: UILabel!
var didSelectAction: ((IndexPath) -> ())?
var theHalls: [Hall] = [] {
didSet {
collectionView.reloadData()
}
}
var lastContentOffset = CGPoint.zero
override func prepareForReuse() {
super.prepareForReuse()
resetCollectionView()
}
override func awakeFromNib() {
super.awakeFromNib()
currentPageNumber.layer.zPosition = 2
currentPageNumber.layer.cornerRadius = 15.0
currentPageNumber.clipsToBounds = true
}
func resetCollectionView() {
guard !theHalls.isEmpty else { return }
theHalls = []
collectionView.reloadData()
}
// MARK: - CollectionView
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return theHalls.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath) as! PhotoStudiosCollectionViewCell2
cell.hallName.text = theHalls[indexPath.item].hallName
cell.priceLabel.text = theHalls[indexPath.item].hallPrice
cell.metrslabel.text = theHalls[indexPath.item].hallMetrs
cell.photoStudioImage.sd_setImage(with: URL(string: theHalls[indexPath.item].hallImage))
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
didSelectAction?(indexPath)
}
}

You're reusing the collection views in your cells, which is correct, but that means the contentOffset is also left from whatever was scrolled to previously when a cell is reused. It should be sufficient to just reset the contentOffset in cellForRowAtIndexPath when you are setting up your cell by doing something like:
cell.collectionView.contentOffset = .zero
One thing worth mentioning is that I do see you have a property called lastContentOffset in your cells that doesn't do anything yet and I suspect you are going to try to use that to persist the offset for a given cell when it scrolls out of view so that you can set it again when it comes back into view (rather than always resetting).
If you are going to do that, having the property in the cell won't work. You'll need to have a list of offsets for each cell stored alongside your data models in the containing view controller. Then you might save the offset for a given cell in didEndDisplayingCell and setting it in cellForRowAtIndexPath instead of .zero as I did above.

Related

Collection View inside Table View Cell not being called

I am trying to implement a collection view inside a table view cell.
My table view cell is a xib, and I've dragged a collection view into it.
Then, I created a class and xib for the collection view cell:
class MyCollectionViewCell: UICollectionViewCell {
var media: Image?
#IBOutlet weak var imageView: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
func initialize(media: PostImage) {
self.media = media
if let url = media.url {
imageView.kf.setImage(with: URL(string: url))
}
}
}
And I've given the xib the class "MyCollectionViewCell" and also given it the identifier "MyCollectionViewCell".
Then, in my table view cell class, I have done the following:
class MyTableViewCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
var post: Post!
#IBOutlet weak var title: UILabel!
#IBOutlet weak var mediaCollectionView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
mediaCollectionView.delegate = self
mediaCollectionView.dataSource = self
let mediaCollectionViewCell = UINib(nibName: "MyCollectionViewCell", bundle: nil)
mediaCollectionView.register(mediaCollectionViewCell, forCellWithReuseIdentifier: "MyCollectionViewCell")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCollectionViewCell", for: indexPath as IndexPath) as? MyCollectionViewCell else {
fatalError("The dequeued cell is not an instance of MyCollectionViewCell.")
}
let media = post.images[indexPath.row]
cell.initialize(media: media)
return cell
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func initialize(post: Post) {
self.post = post
title.text = post.title
self.mediaCollectionView.reloadData()
}
}
The problem is, the collection view never shows when I run this. The title label text shows fine, but the collection view does not show, and I don't know what I'm doing wrong.
cellForItemAt doesn't even seem to get called, because when I add print("hello") at the top of the function, it never shows up in the console.
What am I doing wrong?
I think the problem is the height of the collection view is very small that it isn't shown.
Try to set the height for the table view cell:
func tableView(_: UITableView, heightForRowAt _: IndexPath) -> CGFloat {
return 100
}
where the 100 should be bigger than the collection view

Can't call object from another class

I have a table view with expanding cells. The expanding cells come from a xib file. In the class of the table is where all of the code is that controls the expansion and pulling data from plist. I'm trying to add a close button but only want it to show when the cell is expanded. As it stands, I can't reference the button to hide it because it's in another class. Here is how I am trying to access it:
import UIKit
class SecondPolandViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var customTableViewCell:CustomTableViewCell? = nil
var items = [[String:String]]()
override func viewDidLoad() {
super.viewDidLoad()
**REFERENCING CLASS**
customTableViewCell = CustomTableViewCell()
let nib = UINib.init(nibName: "CustomTableViewCell", bundle: nil)
self.tableView.register(nib, forCellReuseIdentifier: "cell")
self.items = loadPlist()
}
func loadPlist()->[[String:String]]{
let path = Bundle.main.path(forResource: "PolandResourceList", ofType: "plist")
return NSArray.init(contentsOf: URL.init(fileURLWithPath: path!)) as! [[String:String]]
}
var selectedIndex:IndexPath?
var isExpanded = false
func didExpandCell(){
self.isExpanded = !isExpanded
self.tableView.reloadRows(at: [selectedIndex!], with: .automatic)
}
}
extension SecondPolandViewController:UITableViewDataSource, UITableViewDelegate{
***HIDING BUTTON***
let button = customTableViewCell?.closeButton
button?.isHidden = true
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.selectedIndex = indexPath
self.didExpandCell()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell
cell.selectionStyle = .none
let item = self.items[indexPath.row]
cell.titleLabel.text = item["title"]
cell.shortLabel.text = item["short"]
cell.otherImage.image = UIImage.init(named: item["image"]!)
cell.thumbImage.image = UIImage.init(named: item["image"]!)
cell.longLabel.text = item["long"]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let height = UIScreen.main.bounds.height
if isExpanded && self.selectedIndex == indexPath{
//return self.view.frame.size.height * 0.6
return 400
}
return 110
//return height * 0.2
}
}
This does not hide it though.
Here is the xib that I am calling from if it helps. It is probably simple, I am just a newly self taught developer.
import UIKit
class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var closeButton: UIImageView!
#IBOutlet weak var otherImage: UIImageView!
#IBOutlet weak var thumbImage: UIImageView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var shortLabel: UILabel!
//#IBOutlet weak var longLabel: UITextView!
#IBOutlet weak var longLabel: UITextView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
//let width = UIScreen.main.bounds.width
//let height = UIScreen.main.bounds.height
//thumbImage.frame.size.width = height * 0.19
//thumbImage.frame.size.height = height * 0.19
}
}
It seems like that you just need to add these lines into cellForRowAt:indexPath method:
if indexPath == selectedIndexPath {
cell.closeButton.isHidden = false
} else {
cell.closeButton.isHidden = true
}
You may add them right before return line
The normal iOS answer for this is a delegate, but you could get away with a simple closure in this case.
In CustomTableViewCell, add
public var closeTapped: ((CustomTableViewCell) -> ())?
Then in that class, when close is tapped, call
self.closeTapped?(self)
In the VC, in cellForRowAt,
cell.closeTapped = { cell in
// do what you want with the VC
}
For delegates, this might help: https://medium.com/#jamesrochabrun/implementing-delegates-in-swift-step-by-step-d3211cbac3ef
The quick answer to why to prefer delegates over the closure is that its a handy way to group a bunch of these together. It's what UITableViewDelegate is (which you are using). Also, it's a common iOS idiom.
I wrote about this here: https://app-o-mat.com/post/how-to-pass-data-back-to-presenter for a similar situation (VC to VC communication)

reloading visible uicollectionviewcell when nested in uitableviewcell

I have a UICollection that shows a padlock on cells that locked to users who aren't logged in. The user can view the collection and then login in a modal. When the modal dismisses, I am trying to reload the cells of the table and the nested collection to remove the padlocks from the cells.
The visible cells are not refreshing to remove the padlock. When the collection is scrolled, the cells offscreen are correct and show with padlock. I am calling reloaddata() on both the tableview and each nested collectionview.
The code I have is separated to:
UIViewController
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("SectionWorkouts", forIndexPath: indexPath) as! SectionTableViewCell
cell.delegate = self
// Return the workouts count from the section
let intIndex = indexPath.row
let index = workoutSections.startIndex.advancedBy(intIndex)
let currentWorkoutSectionKey = workoutSections.keys[index]
if let currentWorkoutSection = workoutSections[currentWorkoutSectionKey] {
cell.workoutsCollection.dataSource = sectionWorkoutsCell
cell.workoutsCollection.delegate = sectionWorkoutsCell
cell.updateCellWithWorkouts(currentWorkoutSectionKey, workouts: currentWorkoutSection)
}
}
return cell
}
UITableViewCell
class SectionTableViewCell: UITableViewCell,UICollectionViewDelegate, UICollectionViewDataSource {
var workouts = [Workout]()
var delegate: WorkoutCellDelegate?
#IBOutlet weak var sectionTitle: UILabel!
#IBOutlet weak var workoutsCollection: UICollectionView!
func updateCellWithWorkouts(title: String, workouts: [Workout]){
self.sectionTitle.text = title
self.workouts = workouts
dispatch_async(dispatch_get_main_queue(),{
self.workoutsCollection.reloadData()
})
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("SectionWorkoutCell", forIndexPath: indexPath) as! SectionCollectionViewCell
let row = indexPath.row
let workout = workouts[row]
cell.configCell(workout)
return cell
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return workouts.count
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let row = indexPath.row
let feature = workouts[row]
if let delegate = self.delegate{
delegate.didSelectWorkoutCell(feature)
}
}
}
UICollectionViewCell
class SectionCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var imageContainer: UIView!
#IBOutlet weak var image: UIImageView!
#IBOutlet weak var tintOverlay: UIView!
#IBOutlet weak var padlock: UIImageView!
#IBOutlet weak var workoutTitle: UILabel!
#IBOutlet weak var duration: UILabel!
var locked = true
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func configCell(workout: Workout){
self.workoutTitle.text = workout.name
if workout.type == "Free" || AccountManager.userIsLoggedInMember() {
self.setToUnLocked()
}else{
self.setToLocked()
}
self.layoutIfNeeded()
}
func setToUnLocked(){
locked = false
tintOverlay.alpha = 0
padlock.alpha = 0
}
func setToLocked(){
locked = true
tintOverlay.alpha = 0.6
padlock.alpha = 1
}
}
You should probably move the call to configure the cell to the willDisplayCell: method instead. You can remove it from the cellForItemAtIndexPath method. This is the correct time to configure any visual aspects of the cell to be displayed.
func collectionView(collectionView: UICollectionView,
willDisplayCell cell: UICollectionViewCell,
forItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let row = indexPath.row
let workout = workouts[row]
cell.configCell(workout)
return cell
}
The problem lies in the fact that:
the tableViewCell owns the data that is used for configuring the collectionViewCell.
If you want your collectionViewCell to update without the need for tableView.reloadData, the data that is used for configuring the collectionViewCell(in your case 'workout') must be fetched from elsewhere than from the tableViewCell.

UICollectionView cells with Images inside UITableView prototype

UPDATE: I solved my primary issue of correct images not loading until scrolling on the collectionView. I added a collectionView.reloadData() to the tableView:cellForRowAtIndexPath. I also made some changes to pre-load the sequence array, instead of building it while scrolling through the table (tableView:cellForRowAtIndexPath).
Added the updates to GitHub if you are interested.
https://github.com/Druiced/OpenDeck
I will follow-up once I figure out how to prevent the App from crashing when a dynamic value is placed in the return (if i set this to 15, the app will not crash):
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return count(Array(sequenceArray[collectionView.tag])) / 2
}
ORIGINAL POST:
request for some guidance.
This tutorial helped me realize this must have to do with my DataSource/Delegate. The author builds the cell with addSubview instead of taking advantage of the Xcode prototype cell, which seems like a cool thing, so I'm trying to do it.
http://ashfurrow.com/blog/putting-a-uicollectionview-in-a-uitableviewcell
Any criticism about my approach or failure to follow best practices is welcome.
Each cell in the table has a UICollectionView. Each cell in the Collection View displays an image in order of the saved "Sequence" string. example: "ADKDQDJDTD" link up to AD.png KD.png QD.png JD.png TD.png
I have two issues I can't seem to get past.
numberOfItemsInSection gets whacky when the number of cards is driven by the array length (return handArray.count / 2). If I place a fixed number the app will work, but not very slick.
When the table first comes up, the correct cards do not display until I scroll up and down the table. It also appears the data for each CollectionView is crossing paths as the wrong cards show up when scrolling up and down rapidly.
I'm almost positive this has to do with how my datasource is setup.
DeckTableViewController.swift
import UIKit
import Parse
var deviceID: String?
var noRefresh: Bool?
var sequenceArray: Array<Character>?
class DeckTableViewController: UITableViewController, UICollectionViewDelegate, UICollectionViewDataSource {
var handArray: Array<Character>!
var timeLineData:NSMutableArray = NSMutableArray()
override func viewDidLoad() {
super.viewDidLoad()
noRefresh = false
deviceId = UIDevice.currentDevice().identifierForVendor.UUIDString
}
override func viewDidAppear(animated: Bool) {
if noRefresh == false {
loadData()
noRefresh = true
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return timeLineData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:DeckTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! DeckTableViewCell
let deck:PFObject = timeLineData.objectAtIndex(indexPath.row) as! PFObject
cell.collectionView.dataSource = self
cell.collectionView.delegate = self
let sequenceTemp = deck.objectForKey("Sequence") as! String
handArray = Array(sequenceTemp)
cell.sequenceId.setTitle(deck.objectId, forState: UIControlState.Normal)
cell.cardCountLabel.text = "\((count(sequenceTemp)/2))"
// Date to String Stuff
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "(MM-dd) hh:mm:ss"
cell.timeLabel.text = dateFormatter.stringFromDate(deck.updatedAt!)
let layout:UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.itemSize = CGSizeMake(99, 140)
layout.scrollDirection = UICollectionViewScrollDirection.Horizontal
cell.collectionView.collectionViewLayout = layout
return cell
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return handArray.count / 2
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell:TableCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! TableCollectionViewCell
var bcolor : UIColor = UIColor.orangeColor()
cell.layer.borderColor = bcolor.CGColor
cell.layer.borderWidth = 2
cell.layer.cornerRadius = 3
var firstLetter: Character!
var secondLetter: Character!
//Building card file names from Sequence data
if (indexPath.row * 2) + 1 <= handArray.count {
firstLetter = handArray[indexPath.row * 2]
secondLetter = handArray[indexPath.row * 2 + 1]
let imageNameString = "\(firstLetter)\(secondLetter).png"
let front = UIImage(named: imageNameString)
cell.ImageView.backgroundColor = UIColor.orangeColor()
cell.ImageView.image = front
}
return cell
}
DeckTableViewCell.swift
import UIKit
class DeckTableViewCell: UITableViewCell, UITextViewDelegate {
#IBOutlet var collectionView: UICollectionView!
#IBOutlet var sequenceId: UIButton!
#IBOutlet var timeLabel: UILabel!
#IBOutlet var cardCountLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
TableCollectionViewCell.swift
import UIKit
class TableCollectionViewCell: UICollectionViewCell {
#IBOutlet var ImageView: UIImageView!
}
For this example I set (return handArray.count / 2) to a 10 and loaded 3 sequences.
The number in the top center represents the number of cards for each row.
Notice the CollectionView does not update with the right cards, it's picking up data from the other CollectionViews. IF I add bunch more sequences to this mix, when scrolling up and down, the correct cards WILL populate SOMETIMES, but unpredictable.
Thanks for any suggestions, I'm happy to go back to the drawing board. Cheers
Ok lets think this way, your DeckTableViewController acts as datasource for tableview, and DeckTableViewCell acts as datasource for collection view.
with the above thing in mind we create a sample project
i am not going in depth, i am giving example like the tutorial as u go through
lets create a sample project with single view app and in ViewController
past the below code, i took one array of integers which contains some values as how many cells to be appears in collection view. don't forget add tableview and set its datasource and deleagte.
before we are coding to controller class we need some classes like custom tableview cell and custom collection view cell we create them first
create a new file which is the subclass of UICollectionViewCell and name it as CustomCollectionViewCell and with xib file.
class CustomCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var aLabel: UILabel! //to show the card number
#IBOutlet weak var imageView: UIImageView! //imageview i am setting it's background color
override init(frame: CGRect) {
super.init(frame: frame)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
}
}
and create a outlets for label and image view as in the above code.
Next, create new file subclass of UITableViewCell and name it as CustomTableViewCell with xib file. open up CustomTableViewCell.xib file and drag and drop the collection view and set it's datasource and delegate to cell not the controller.
and create a outlet for the collection view and name it as foldersCollectionView.
pass the below code
import UIKit
class CustomTableViewCell: UITableViewCell,UICollectionViewDataSource,UICollectionViewDelegate {
#IBOutlet weak var foldersCollectionView: UICollectionView!
override init(frame: CGRect) {
super.init(frame: frame)
}
required init(coder aDecoder: NSCoder) {
// fatalError("init(coder:) has not been implemented")
super.init(coder: aDecoder)
}
var folderCount:Int?
{
didSet(value)
{
}
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
//configure our collectionview
var aFlowLayout : UICollectionViewFlowLayout = UICollectionViewFlowLayout()
aFlowLayout.scrollDirection = UICollectionViewScrollDirection.Horizontal
aFlowLayout.itemSize = CGSizeMake(60.0, 90.0)
aFlowLayout.minimumLineSpacing = 10.0
aFlowLayout.minimumInteritemSpacing = 0.0
aFlowLayout.sectionInset = UIEdgeInsetsMake(2, 9, 0, 10)
foldersCollectionView.collectionViewLayout = aFlowLayout
foldersCollectionView.registerClass(CustomCollectionViewCell.self, forCellWithReuseIdentifier: "FOLDER_CELL")
var cNib:UINib? = UINib(nibName: "CustomCollectionViewCell", bundle: nil)
foldersCollectionView.registerNib(cNib, forCellWithReuseIdentifier: "FOLDER_CELL")
foldersCollectionView.frame = self.bounds
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
class func CreateCustomCell() -> CustomTableViewCell
{
var nibElements: Array = NSBundle.mainBundle().loadNibNamed("CustomTableViewCell", owner: self, options: nil)
var item: AnyObject?
for item in nibElements
{
if item is UITableViewCell
{
return item as CustomTableViewCell
}
}
return item as CustomTableViewCell
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell :CustomCollectionViewCell? = collectionView.dequeueReusableCellWithReuseIdentifier("FOLDER_CELL", forIndexPath: indexPath) as? CustomCollectionViewCell
//hear u can modify which image to be displayed in the collection view cell
cell?.aLabel.text = "Card:\(indexPath.row)"
return cell!
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return folderCount!
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
}
now we are going the code the ViewController class
now just past the below code
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
var cardCountArray:[Int] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
cardCountArray = [5,15,6,12,7,10]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return cardCountArray.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var cell:CustomTableViewCell? = tableView.dequeueReusableCellWithIdentifier("CELL") as? CustomTableViewCell;
if(cell == nil)
{
cell = CustomTableViewCell.CreateCustomCell()
}
cell?.folderCount = cardCountArray[indexPath.section]
cell?.foldersCollectionView.reloadData()
cell?.clipsToBounds = true
return cell!;
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
return 100.0
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
var headerView:UIView = UIView(frame: CGRectMake(0, 0, tableView.bounds.size.width, 70.0))
var labelTitle:UILabel = UILabel(frame: CGRectMake(0, 0, tableView.bounds.size.width, 35))
var descriptionTitle:UILabel = UILabel(frame: CGRectMake(0, 20,tableView.bounds.size.width , 30))
headerView.addSubview(labelTitle)
headerView.addSubview(descriptionTitle)
labelTitle.text = "TOTAL_CARDS in section:\(section)"
descriptionTitle.text = "This CARD_SECTION contains \(cardCountArray[section]) CARDS"
return headerView
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50.0
}
}
result will be like below
if any thing missing please let me know
For your comment I have an array, for example, ["2C3C4C5C6C7C", "AD2D3D4D5D", "9H8H7H"]
for this u need to make below modification
//for first row u get like this
//the string for the row is 2C3C4C5C6C7C
//stringForCell = "2C3C4C5C6C7C"
//2C
//3C
//4C
//5C
//6C
//7C
//for other cells u can get like below
//the string for the row is AD2D3D4D5D
//stringForCell = "AD2D3D4D5D"
//AD
//2D
//3D
//4D
//5D
//the string for the row is 9H8H7H
//stringForCell = "9H8H7H"
//9H
//8H
//7H
//in controller controller class define array of string
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
var cardCountArray:[Int] = []
var stringArray : [String] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
stringArray = ["2C3C4C5C6C7C", "AD2D3D4D5D", "9H8H7H"]
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
// return cardCountArray.count
return stringArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var cell:CustomTableViewCell? = tableView.dequeueReusableCellWithIdentifier("CELL") as? CustomTableViewCell;
if(cell == nil)
{
cell = CustomTableViewCell.CreateCustomCell()
}
//cell?.folderCount = cardCountArray[indexPath.section]
cell?.stringForCell = stringArray[indexPath.section];
cell?.foldersCollectionView.reloadData()
cell?.clipsToBounds = true
return cell!;
}
//in custom tableview cell add a string variable
class CustomTableViewCell: UITableViewCell,UICollectionViewDataSource,UICollectionViewDelegate {
#IBOutlet weak var foldersCollectionView: UICollectionView!
var stringForCell:String = "" //add the string to hold the string
//rest of the code
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell :CustomCollectionViewCell? = collectionView.dequeueReusableCellWithReuseIdentifier("FOLDER_CELL", forIndexPath: indexPath) as? CustomCollectionViewCell
var str:NSString = stringForCell
var length = str.length
var totalLlength:Int = length/2
var indexStart = indexPath.row * (2);
var aRange = NSMakeRange(indexStart, 2)
var cardString:NSString = str.substringWithRange(aRange)
println(cardString)
cell?.aLabel.text = "Card: \(cardString)"
return cell!
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
println("the string for the row is \(stringForCell)")
var str:NSString = stringForCell
var length:Int = str.length
return length / 2
//return folderCount!
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
i written a detailed post about how to add collection view inside custom table view cell hear hope this gives more detailed explanation than this post.

dynamic tableview inside custom tableviewcell

I'm trying to create a tableview with custom cells that each one holds a tableview.
I want to show the inner tableview just when it have some data (most of the time it's empty). I've managed to display the cells but can't display their tableview if it's populated with data.
The problem also is that the cell height needs to be dynamic according to the amount of data to display.
The cell code:
class feedViewCell: UITableViewCell , UITableViewDataSource , UITableViewDelegate {
#IBOutlet var feedCellImage: UIImageView!
#IBOutlet var feedCellUserName: UILabel!
#IBOutlet var feedCellDate: UILabel!
#IBOutlet var feedCellComments: UILabel!
#IBOutlet var cardView: UIView!
#IBOutlet var repliesTableView: UITableView!
var repliesArray:[Reply] = []
#IBAction func addComment(sender: AnyObject) {
}
override func awakeFromNib() {
super.awakeFromNib()
var nib = UINib(nibName: "feedComment", bundle: nil)
repliesTableView.registerNib(nib, forCellReuseIdentifier: "commentCell")
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
override func layoutSubviews() {
cardSetup()
}
func cardSetup() {
cardView.layer.masksToBounds = false
cardView.layer.cornerRadius = 5
cardView.layer.shadowOffset = CGSizeMake(-0.2, 0.2)
cardView.layer.shadowRadius = 1
cardView.layer.shadowOpacity = 0.2
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return repliesArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = repliesTableView.dequeueReusableCellWithIdentifier("commentCell", forIndexPath: indexPath) as CommentFeedCell
cell.commentCellUserName.text = repliesArray[indexPath.row].userName
return cell
}
}
And the Main controller code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var comment = comments[indexPath.row]
let cell: feedViewCell = tableView.dequeueReusableCellWithIdentifier("feedCell", forIndexPath: indexPath) as feedViewCell
cell.feedCellUserName.text = comment.userName
cell.feedCellImage.backgroundColor = UIColor.redColor()
cell.feedCellComments.text = "\(comment.replies.count) COMMENTS"
cell.repliesArray = comment.replies
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd-MM-yyy"
cell.feedCellDate.text = dateFormatter.stringFromDate(NSDate())
if cell.repliesArray.count > 0 {
cell.repliesTableView.rowHeight = UITableViewAutomaticDimension
}
cell.repliesTableView.reloadData()
return cell
}
How to show the inner tableview only in cells which have comments (and hiding the tableview in cells with 0 comments)?
Call super in layoutSubviews and let us know what happens.
override func layoutSubviews() {
super.layoutSubviews()
cardSetup()
}

Resources