Control table views inside a single collection view cell - ios

Hey everyone I have sort of a problem here and I’ll try to explain best as I can. So I have a collection view with a collection view cell and embedded inside that cell is a table view, and with that table view I want to return 3 (for testing more in the future) cells, which would return 3 tableviews. With those tableviews I want to have different types of data, but I want to know how I can do that or if its possible with just one table view in my storyboard. I have already attempted to try this but everything returns as nil. Thanks in advance!
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
if tableView == tableview1 {
return 0;
} else if tableView == tableview2 {
return 3
}
return 0;
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if tableView == tableview1 {
return 2;
} else if tableView == tableview2 {
return 1;
}
return 0;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
if tableView == tableview1 {
cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
} else if tableView == tableview2 {
cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
}
// Configure the cell...
if tableView == tableview1 {
cell.textLabel?.text = "Homeroom"
cell.detailTextLabel?.text = "8:15 AM - 9:00 AM"
cell.selectionStyle = .None
} else if tableView == tableview2 {
cell.textLabel?.text = "Test Table 2 "
cell.detailTextLabel?.text = "1:30 PM - 2:30 PM"
cell.selectionStyle = .None
}
return cell
}

Add tableviews to cell using xibs or storyboard. and Put all these delegates and data source of tableview inside collectionviewcell class, and tell tableviews also that its datasource and delegates are there in this class. let me know if that works.

Related

Adding a cell on the top of a dynamic UITableViewCell

I add a cell with a label in it in a new section on the top of the tableView as section 0 and i show and hide this section according to what type of data i'm displaying.
It works fine when there is no data in the hashtag type posts then when there is hashtag data to be displayed in the array like two or three items it works fine and the top section 0 cell is displayed then when i scroll down and up again i get an error in the AppDelegate after trying to return the top section cell.
I know the question is a little bit complicated but what i'm trying to achieve is to display and hide a cell on the top of my feed according to the type of data i'm displaying in my tableview. If hashtag news feed data then show the top cell in section 0 if showing ordinary news feed in the tableview then return only one section and don't load the top section with the cell inside of it.
By the way i'm displaying the cell as a Nib. And declaring it in the viewDidLoad
let reloadNib = UINib(nibName: "ReloadTableViewCell", bundle: nil)
feedTableView.register(reloadNib, forCellReuseIdentifier: "reloadCell")
Thread 1: EXC_BREAKPOINT (code=1, subcode=0x102a772a0)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! PostTableViewCell
switch indexPath.section {
case 0:
if hashPostsOnly {
let reloadCell = tableView.dequeueReusableCell(withIdentifier: "reloadCell", for: indexPath) as! ReloadTableViewCell
return reloadCell // ERROR AFTER RETURNING CELL
} else {
//For the protocol delegate i made
cell.delegate = self
cell.feed = feeds[indexPath.row]
cell.postCommentTextView.tag = indexPath.row
cell.cellIndexPath = indexPath
cell.userProfilePhotoBtn.tag = indexPath.row
cell.postMoreCommentsBtn.tag = indexPath.row
cell.postMoreCommentsBtn.addTarget(self, action: #selector(moreCommentsTapped(_:)), for: .touchUpInside)
return cell
}
case 1:
//For the protocol delegate i made
cell.delegate = self
cell.feed = feeds[indexPath.row]
cell.postCommentTextView.tag = indexPath.row
cell.cellIndexPath = indexPath
cell.userProfilePhotoBtn.tag = indexPath.row
cell.postMoreCommentsBtn.tag = indexPath.row
cell.postMoreCommentsBtn.addTarget(self, action: #selector(moreCommentsTapped(_:)), for: .touchUpInside)
return cell
default:
return cell
}
func numberOfSections(in tableView: UITableView) -> Int {
if hashPostsOnly {
return 2
} else {
return 1
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
if hashPostsOnly {
return 1
} else {
return feeds.count
}
} else {
return feeds.count
}
}
Here is a screen shot of what i'm achieving but when i scroll down then up it reloads the top section cell "Reload Feeds" and then error.
Since there are just two sections and you had duplicate code, things can be simplified to:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
if indexPath.section == 0 && hashPostsOnly
{
let reloadCell = tableView.dequeueReusableCell(withIdentifier: "reloadCell", for: indexPath) as! ReloadTableViewCell
return reloadCell
}
else
{
let cell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! PostTableViewCell
//For the protocol delegate i made
cell.delegate = self
cell.feed = feeds[indexPath.row]
cell.postCommentTextView.tag = indexPath.row
cell.cellIndexPath = indexPath
cell.userProfilePhotoBtn.tag = indexPath.row
cell.postMoreCommentsBtn.tag = indexPath.row
cell.postMoreCommentsBtn.addTarget(self, action: #selector(moreCommentsTapped(_:)), for: .touchUpInside)
return cell
}
}
I can't be 100% sure without knowing the exact error you're getting or knowing if there are other issues in the code elsewhere causing this, but:
As a general rule, dequeuing twice from a table view and returning a single cell does bad things in weird and mysterious ways. Refactor your code to only deuque a regular cell when you need it and not to do so when you're showing the refresh button

Variable use of multiple custom cells

I'm using a unclickable tableView to display different information of one object.
For this informations I have different custom cell types one where I placed a map, if my object have locations, one have a list with links, and another a multiple line label for a little description...for example.
I manage this cells with:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell: mapCell = tableView.dequeueReusableCellWithIdentifier("mapCell") as! MapCell
return cell
} else if indexPath.row == 1 {
let cell: textCell = tableView.dequeueReusableCellWithIdentifier("textCell") as! TextCell
return cell
} else if indexPath.row == 2 {
let cell: listCell = tableView.dequeueReusableCellWithIdentifier("listCell") as! ListCell
return cell
}
}
So far so good, everything working fine. My problem is, not every object needs a map, some of them just need some text and a list, other objects need a map and a list, other all of them. I want my tableView to skip some cells if there is a condition.
I know, I can make an symbolic array for changing the number of cells of my tableView, but that deleting just from the end of my tableView, not specific cells.
One of my ideas is to generate a empty cell, maybe with a height of 0 or 1 so that I can do something like this:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row == 0 {
if mapCellNeeded {
let cell: mapCell = tableView.dequeueReusableCellWithIdentifier("mapCell") as! mapCell
} else {
let cell: emptyCell = tableView.dequeueReusableCellWithIdentifier("emptyCell") as! EmptyCell
}
return cell
} else if indexPath.row == 1 {
...
}...
}
put I don't know if there isn't an efficient way. Hope you guys can help me.
Your solution would work. Another approach (very nice and swifty) would be not to hardcode row numbers, but rather use enum instead:
enum InfoCellType {
case Map
case Text
case Links
}
...
var rows = [InfoCellType]()
...
// when you know what should be there or not
func constructRows() {
if (mapCellNeeded) {
rows.append(InfoCellType.Map)
}
rows.append(InfoCellType.Text)
... etc
}
Then in the table view methods just see what's the type for current indexPath:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellType: InfoCellType = self.rows[indexPath.row]
switch cellType {
case .Map:
let cell: mapCell = tableView.dequeueReusableCellWithIdentifier("mapCell") as! mapCell
return cell
case .Text:
...
case.Links:
...
}
}
This solution also allows to easily change order of rows - just change the order of items in rows array.

When UITableView Scrolling Returned Wrong IndexPath.Row Value

This is my codes;
// MARK: - Table View Delegate && Data Source Methods
// **************************************************
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let index = indexPath.row
print(index)
if index == 0 {
let cell = tableView.dequeueReusableCellWithIdentifier("HeaderCell", forIndexPath: indexPath)
cell.backgroundColor = ColorHelper.getCellBackgroundColor()
return cell
}
else {
if let cell = tableView.dequeueReusableCellWithIdentifier("GradeCell", forIndexPath: indexPath) as? GradeCell {
cell.activeViewController = self;
cell.gradeButton.tag = index
cell.creditButton.tag = index
cell.lessonNameTextField.tag = index
cell.lessonNameTextField.delegate = self
cell.backgroundColor = ColorHelper.getCellBackgroundColor()
return cell
}
return UITableViewCell()
}
}
I have 11 cells and someone are missing, When i scrolled table view index returns like this;
0-1-2-3-4-5-6-7-8-0-1-2..
After reload process, my values are confused. Wrong value in wrong cell, how can i fix this ?
Your problem could possible be how many cells you are returning. Especially, if you are having problems with the last one or two. From what you said, it sounds like you have 11 cells, make sure you return 12. The cells in a UITableView always start counting with 0 being the first cell and 10 being that last cell, in your case.
func tableView(tableView: UITableView, numberOfRowsInSection section:Int) -> Int {
return 12
}

Prevent reuse of custom cells in table view Swift

I have a problem which i'm not sure how to solve.
I have two custom cell nibs - data for both is fetched from separate arrays.
The structure is the following
nib1-cell line1
nib1-cell line2
...
nib1-cell line n
nib2-cell line1
there is always one nib2-cell at the end with the uibutton.
Once the uibutton is pressed - the nib1 array is appended.
I figured out a way how to insert values at the bottom of the tableview, but when i scroll downwards or upwards the cell with nib2 is reused and replaced with nib1-cell data.
How can i either prevent those cells from being reused or save their state ?
Thank you.
UPDATE: datasource and cellForRowAtIndexPath code
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return someTagsArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if(indexPath.row < someTagsArray.count - 1){
var cell:TblCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! TblCell
cell.lblCarName.text = someTagsArray[indexPath.row]
return cell
} else if (indexPath.row == someTagsArray.count - 1){
var celle:vwAnswers = self.tableView.dequeueReusableCellWithIdentifier("cell2") as! vwAnswers
celle.Answer1.setTitle(answersdict[answersdict.endIndex - 2], forState:UIControlState.Normal)
answertitle1 = "\(celle.Answer1.currentTitle!)"
celle.Answer2.setTitle(answersdict.last, forState:UIControlState.Normal)
answertitle2 = "\(celle.Answer2.currentTitle!)"
//println(answertitle2)
return celle
} else {
var cell2:TblCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! TblCell
return cell2
}
}
You have to determine which type of cell you want in cellForRowAtIndexPath and dequeue the correct reusable cell. Maybe something like if (indexPath.row + 1)%3 == 0 then dequeue an answer cell.
However, you may possibly want to look in to using a section header for this instead. Hard to say without seeing how you implement your data source.

Swift - How to use dequeueReusableCellWithIdentifier with two cell types? (identifiers)

I am trying to build a table view for events, like so:
I have two cell prototypes:
An event cell with identifier "event"
A separator cell with identifier "seperator"
Also, I have this class to represent a date:
class Event{
var name:String = ""
var date:NSDate? = nil
}
And this is the table controller:
class EventsController: UITableViewController {
//...
var eventsToday = [Event]()
var eventsTomorrow = [Event]()
var eventsNextWeek = [Event]()
override func viewDidLoad() {
super.viewDidLoad()
//...
self.fetchEvents()//Fetch events from server and put each event in the right property (today, tomorrow, next week)
//...
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let event = tableView.dequeueReusableCellWithIdentifier("event", forIndexPath: indexPath) as EventTableViewCell
let seperator = tableView.dequeueReusableCellWithIdentifier("seperator", forIndexPath: indexPath) as SeperatorTableViewCell
//...
return cell
}
}
I have all the information I need at hand, but I can't figure out the right way to put it all together. The mechanics behind the dequeue func are unclear to me regrading multiple cell types.
I know the question's scope might seem a little too broad, but some lines of code to point out the right direction will be much appreciated. Also I think it will benefit a lot of users since I didn't found any Swift examples of this.
Thanks in advance!
The basic approach is that you must implement numberOfRowsInSection and cellForRowAtIndexPath (and if your table has multiple sections, numberOfSectionsInTableView, too). But each call to the cellForRowAtIndexPath will create only one cell, so you have to do this programmatically, looking at the indexPath to determine what type of cell it is. For example, to implement it like you suggested, it might look like:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return eventsToday.count + eventsTomorrow.count + eventsNextWeek.count + 3 // sum of the three array counts, plus 3 (one for each header)
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var index = indexPath.row
// see if we're the "today" header
if index == 0 {
let separator = tableView.dequeueReusableCellWithIdentifier("separator", forIndexPath: indexPath) as SeparatorTableViewCell
// configure "today" header cell
return separator
}
// if not, adjust index and now see if we're one of the `eventsToday` items
index--
if index < eventsToday.count {
let eventCell = tableView.dequeueReusableCellWithIdentifier("event", forIndexPath: indexPath) as EventTableViewCell
let event = eventsToday[index]
// configure "today" `eventCell` cell using `event`
return eventCell
}
// if not, adjust index and see if we're the "tomorrow" header
index -= eventsToday.count
if index == 0 {
let separator = tableView.dequeueReusableCellWithIdentifier("separator", forIndexPath: indexPath) as SeparatorTableViewCell
// configure "tomorrow" header cell
return separator
}
// if not, adjust index and now see if we're one of the `eventsTomorrow` items
index--
if index < eventsTomorrow.count {
let eventCell = tableView.dequeueReusableCellWithIdentifier("event", forIndexPath: indexPath) as EventTableViewCell
let event = eventsTomorrow[index]
// configure "tomorrow" `eventCell` cell using `event`
return eventCell
}
// if not, adjust index and see if we're the "next week" header
index -= eventsTomorrow.count
if index == 0 {
let separator = tableView.dequeueReusableCellWithIdentifier("separator", forIndexPath: indexPath) as SeparatorTableViewCell
// configure "next week" header cell
return separator
}
// if not, adjust index and now see if we're one of the `eventsToday` items
index--
assert (index < eventsNextWeek.count, "Whoops; something wrong; `indexPath.row` is too large")
let eventCell = tableView.dequeueReusableCellWithIdentifier("event", forIndexPath: indexPath) as EventTableViewCell
let event = eventsNextWeek[index]
// configure "next week" `eventCell` cell using `event`
return eventCell
}
Having said that, I really don't like that logic. I'd rather represent the "today", "tomorrow" and "next week" separator cells as headers, and use the section logic that table views have.
For example, rather than representing your table as a single table with 8 rows in it, you could implement that as a table with three sections, with 2, 1, and 2 items in each, respectively. That would look like:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 3
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch section {
case 0:
return "Today"
case 1:
return "Tomorrow"
case 2:
return "Next week"
default:
return nil
}
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0:
return eventsToday.count
case 1:
return eventsTomorrow.count
case 2:
return eventsNextWeek.count
default:
return 0
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let eventCell = tableView.dequeueReusableCellWithIdentifier("event", forIndexPath: indexPath) as EventTableViewCell
var event: Event!
switch indexPath.section {
case 0:
event = eventsToday[indexPath.row]
case 1:
event = eventsTomorrow[indexPath.row]
case 2:
event = eventsNextWeek[indexPath.row]
default:
event = nil
}
// populate eventCell on the basis of `event` here
return eventCell
}
The multiple section approach maps more logically from the table view to your underlying model, so I'd to adopt that pattern, but you have both approaches and you can decide.

Resources