Two TableViews One Data Source? - ios

I have setup a segue that will show a view controller with a small TableView. I want a different segue to show a bigger TableView but I want the bigger table to have the same exact info as the smaller table. Got the smaller tableView working perfect on its own, but once I give the bigger table a Data source, reset and try it out.....crashes.
// IndexPath or First Cell in TableView
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = UITableViewCell()
if self.TaskTableViews.hidden == false {
cell = tableView.dequeueReusableCellWithIdentifier( "FirstTask" , forIndexPath: indexPath) as UITableViewCell!
let list = frc.objectAtIndexPath(indexPath) as! List
cell.textLabel?.text = list.taskName
cell.textLabel?.textColor = UIColor.whiteColor()
TaskTableViews.backgroundColor = UIColor.lightGrayColor().colorWithAlphaComponent(0.55)
TaskTableViews.layer.cornerRadius = 8
TaskTableViews.separatorColor?.colorWithAlphaComponent(2.0) }
if self.TaskTable2.hidden == false {
cell = tableView.dequeueReusableCellWithIdentifier( "Second Task" , forIndexPath: indexPath) as UITableViewCell!
let list = frc.objectAtIndexPath(indexPath) as! List
cell.textLabel?.text = list.taskName
cell.textLabel?.textColor = UIColor.whiteColor()
TaskTable2.backgroundColor = UIColor.lightGrayColor().colorWithAlphaComponent(0.55)
TaskTable2.layer.cornerRadius = 8
TaskTable2.separatorColor?.colorWithAlphaComponent(2.0) }
return cell as UITableViewCell
}

The problem is that the code for your two tables is slamming into each other. To fix this, rejigger your logic. Do not make your logic depend on what is hidden. You can handle only one table at a time; just one table is calling here. That table comes in as the tableView parameter. Make your logic depend on that. Depending what table view that tableView parameter is, configure the cell and return it for that table view.

I think that you should consider changing your approach by just resizing the table view dynamically when you go from one scene to the other instead of having two table views if the information is exactly the same.
If you're still pushing for this approach then don't make the condition be that the table view is hidden and instead implement your own logic or boolean to determine this. But again, I'd rather resize a single table view as needed.

You have many problems in your code
1.
var cell = UITableViewCell()
What's the point of this line?
2.
cell = tableView.dequeueReusableCellWithIdentifier( "FirstTask" , forIndexPath: indexPath) as UITableViewCell!
What's the point of casting? This function returns UITableViewCell (not even optional)
3.
cell.textLabel?.text = list.taskName
Should not compile, cause UITableViewCell doesn't have textLabel
4.
TaskTableViews.backgroundColor = UIColor.lightGrayColor().colorWithAlphaComponent(0.55)
What's the point of doing this every cell request? Move this to viewDidLoad or other appropriate place
Your if { } parts are identical except of reuse identifier
Use tableView argument when needed
Use if { } else
8.
return cell as UITableViewCell
Why cast? Just return
I'm sure I haven't found all of them))

Related

UITableView not check marking cells (outside of didSelect and didDeselect) in Swift

I have a UITableView where I allow the user to select cells and when they do I set the cell.accessoryType to .checkmark. My app is set up in a way that the user is supposed to go to the "main" ViewController, segue to the TableView, select a few cells, and then repeat this process a few times. My problem is that I don't want the tableView selections the user has already made to disappear. I have already tried self.clearsSelectionOnViewWillAppear = false and it does not work. So I made an array, that contains all of the indexPaths for the cells the user has selected, so when the user goes to the "main" ViewController and comes back I want my app to essentially reselect those cells.
Here is my code for this process...
for row in selectedRows {
print("Reselecting: \(row)")
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: row)
print(cell)
cell.accessoryType = .checkmark
}
When I print the cell it shows up in the log like this <UITableViewCell: 0x7fc2f90f3e00; frame = (0 0; 375 44); text = 'Title'; clipsToBounds = YES; layer = <CALayer: 0x600000aae260>>. One thing I noticed is that the text on my cell when it is properly displayed is not Title like the log suggests (Although it is the default text in my storyboard), could this have something to do with my problem? Why are the cells not check marked after running this code?
I think you should use tableView(_:cellForItemAt:) from UITableViewDataSource protocol.
func tableView(_ tableView: UITableView, cellForItemAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "<your reusable identifier", for: indexPath) as! YourCellSubclassIfAny
cell.accessoryType = isIndexPathSelected(indexPath) ? .checkmark : .none
// do whatever setup you need
return cell
}
Of course you need to set the dataSource of your tableView (if not using UITableViewController. Usually it is set to the view controller which contains the tableView. You can do it in storyboard or in code.

UITableview with 2 cells

I am trying to build an app with 2 cell on a tableview
the header that will have the Headlines and the second that will have the normal feeds.
My question is how to connect this two cells
The code for the raw feeds are working
Example:
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return self.posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TableViewCell
// Configure the cell...
let post = self.posts[indexPath.row]
cell.title.text = post.title?.utf8Data?.attributedString?.string
if let imageUrl = post.imageUrl {
cell.imgView?.downloadImage(from: imageUrl)
}
let myFormatter = DateFormatter()
myFormatter.dateStyle = .none
myFormatter.timeStyle = .short
cell.pubDate.text = myFormatter.string(from: post.pubDate!) // What gives?
return cell
}
}
You can do this by filling a solid color, like white, for both the cells and set Table View separator to none.
tableView.separatorStyle = .none
And if you want to create a serious of such combined cells, then add a view to the bottom of the second cell, i.e. Feed Row, and fill that view with other color, like grey. This view will work as a separator for serious.
I suggest you let your tableview empty in storyboard. (delete all cells)
What I usually do, and what I feel is more flexible and cleaner in terms of code and files, is always use custom cells.
Create a subclass of UITableViewCell for each cell type you need (here, 2). One could be HeaderCell, and the other could be FeedCell.
If you use the "Create UITableViewCell class" I think you can find in Xcode (haven't used it in a long time), it should create the class for you, as well as a .xib file. If you don't have the corresponding .xib just create it and link it manually.
The .xib file is where you will put your labels, connect the outlets and maange everything you need. That's where you'll remake the cells you deleted ealier on. I think you can actually cut and paste in the xib and it might work.
Now you have a custom class, with its corresponding xib, which you can use as a cell.
Make sure your tableview is connected as delegate and datasource in storyboard (it should already be done in your setup).
In ViewDidLoad you will need to register all your cell classes to the tableview.
That is done using this method call on your tableview property (outlet)
I'm not sure how to write it in swift, but it's
MyTableView.Register(nib, key) in pseudo code.
Both the parameters come from your custom cell class (it's the .Nib and the class name, which you could hardcode). I usually do MyHeaderCell.Nib and MyHeaderCell.Key that are put in static, but that you can do however you want.
Now that your tableview is aware of the cells it will have to display, you just have to manage it in your cellForRow method.
Simply do it by index :
var cell;
if (indexPath.Row == 0)
{
cell = tableView.dequeueReusableCell(withIdentifier: "HeaderCell", for: indexPath) as! HeaderCell
}
else
{
cell = tableView.dequeueReusableCell(withIdentifier: "FeedCell", for: indexPath) as! FeedCell
}
let post = self.posts[indexPath.row]
cell.title.text = post.title
return cell
And there you go :)
I've never done swift so there are certainly syntax mistakes but the idea is how you should do it.

Switch statement for the tag property of a view

I'm trying to set up a tableview that has 2 cells. Both cells have their own classes and they have their own methods to configure them. In code, when i get to the cellforrowatindexpath method, i get stuck. I can only dequeue one of the cells and call it's methods, which means the other cell won't configured. I want to configure both. Here's what i'm (currently) trying in the cellforrow method:
let cells = [tableView.viewWithTag(1), tableView.viewWithTag(2)]
for view in cells {
var reuseIdentifier = "cellID"
switch view?.tag {
case 1: // error occurs here
reuseIdentifier = "storyCell"
let storyCell1 = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier, forIndexPath: indexPath) as! StoryCell1
storyCell1.theCategory.text = newsItem.storyCategory
storyCell1.theTitile.text = newsItem.titleText
storyCell1.firstParagraph.text = newsItem.paragraph1
case 2: // error occurs here too
reuseIdentifier = "storyCell2"
let storyCell2 = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier, forIndexPath: indexPath) as! StoryCell2
storyCell2.configureCell(newsItem)
default:
}
in the storyboard, i have given both those cells tags of 1 and 2 respectively, hence the array at the very beginning. I have 2 problems with this approach.
I can't use it because the switch statement is throwing me an error: Expression pattern of type 'Int' cannot match values of type 'Int?'
even if it were to not have the error above, i can still only return one cell at the end of the method.
any help on my approach or a different, better way to handle this would be appreciated. Thanks!
EDIT:
Since I'm sure i've added the tag property i force unwrapped the view!.tag property and the error goes away. So, the 2nd question now remains.
I don't really get what you want to do here.
What I think you're trying to do, is to configure and return two cells in the tableView(_:cellForRowAtIndexPath:) method. If you really want to do this, you're totally doing it wrong.
The table view's data source methods asks questions. And your job is to answer those questions by returning a value. For example, numberOfSectionsInTableView(_:) asks you how many sections should there be. An example answer might be return 1, return 10 etc.
Similarly, tableView(_:cellForRowAtIndexPath:) asks
What should be the cell that should be shown in the section and row specified by the index path?
And you answer by returning a UITableViewCell. You can't return two cells because it is asking you to provide a cell to be displayed at that specific section and row. If you gave it two cells to display, how can the table view display them? It doesn't make sense! Each row in the table view can only display one cell!
Therefore, instead of giving tags to the cells, use the indexPath parameter to decide which cell to create.
Let's say you want the first row to display the cell with the identifier "storyCell". And you want the second row to display the cell with the identifier "storyCell2". And your table view has only one section. You can just do this:
switch indexPath.row {
case 0:
reuseIdentifier = "storyCell"
let storyCell1 = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier, forIndexPath: indexPath) as! StoryCell1
storyCell1.theCategory.text = newsItem.storyCategory
storyCell1.theTitile.text = newsItem.titleText
storyCell1.firstParagraph.text = newsItem.paragraph1
return storyCell1
case 1:
reuseIdentifier = "storyCell2"
let storyCell2 = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier, forIndexPath: indexPath) as! StoryCell2
storyCell2.configureCell(newsItem)
return storyCell2
default:
// this shouldn't be reached if you do your other data source methods correctly
return UITabeViewCell()
}
And you should delete these nonsense:
let cells = [tableView.viewWithTag(1), tableView.viewWithTag(2)]
for view in cells {
var reuseIdentifier = "cellID"

How to display two cells each with different functionality in the same tableView simultaneously?

I have tableView.
I have tableViewCellOne. tableViewCellOne allows text input and image input by the user which gets saved in a database. This part works fine.
I want to build tableViewCellTwo. This cellTwo has a different function than cellOne. cellTwo will retrieve this data and display it.
I want both cells to be visible at the same time. tableViewCellOne above, and tableViewCellTwo below, on the same tableView. Here is the relevant portion of the code I wrote. The way that it works as of now, it only displays tableViewCellOne which is not my intent. This code is written in my TableViewController.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
if (indexPath.row == 0) {
let cellIdentifier = "MyTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! MyTableViewCell
// Fetches the appropriate data for the data source layout.
let data = myData[indexPath.row]
cell.MyData1.text = data.1
cell.MyData2.image = data.2
cell.MyData3.text = data.3
return cell
} else {
let cellIdentifier = "TheirDataTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! TheirDataTableViewCell
// Fetches the appropriate data for the data source layout.
let data = theirData[indexPath.row]
cell.TheirData1.text = data.1
cell.TheirData2.image = data.2
cell.TheirData3.text = data.3
return cell
}
}
With this if else statement, I am trying to say: when we are loading cell 1 then load the following data, when we are loading cell2 then load the following other data. But I think instead its interpreting it as: if (something) load cell1, if (something else) load cell2. I could be way off base with that diagnosis though.
How do I display two cells each with different functionality in the same tableView simultaneously?

Returning multiple dequeueReusableCells in a TableView, Swift

The problem I'm trying to solve is to return more than one cell for the loaded task, since i cannot iterate without getting a brand new task loaded.
Each view has one or more cell that is supposed to be loaded for that specific task and i can't seem to solve the problem. Thank you for helping out!
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let categoryCells = self.tasks[indexPath.row] as? Task
var cellChooser: BoredTaskCell!
if selectedTaskCategory == "Movie"{
let cell = tableView.dequeueReusableCellWithIdentifier("imdbCell") as! BoredTaskCell!
cell!.selectionStyle = UITableViewCellSelectionStyle.None
cellChooser = cell
}
else if selectedTaskCategory == "Lifestyle" {
let emailCell = tableView.dequeueReusableCellWithIdentifier("emailCreatorCell") as! BoredTaskCell!
emailCell!.selectionStyle = UITableViewCellSelectionStyle.None
cellChooser = emailCell
}
else {
let emptyCell = tableView.dequeueReusableCellWithIdentifier("messageCell") as! BoredTaskCell!
cellChooser = emptyCell
emptyCell!.selectionStyle = UITableViewCellSelectionStyle.None
}
return cellChooser
}
The short answer is that you cannot return two cells from one call to cellForRowAtIndexPath. But to achieve what you want, change the way you construct the tableView, so it has one section for each task, rather than one cell for each task. ie use indexPath.section as the index for your array. You will need to amend all the table view data source methods (eg. so numberOfSectionsInTableView will return self.tasks.count). The trick comes in numberOfRowsInSection, which should typically return 1, but for those tasks where you want two cells, it should return 2. Then in cellForRowAtIndexPath, you determine which task to display using indexPath.section. Then if indexPath.row is 0 use one cell type (eg imdbcell), and if it's 1 use the other cell type (eg message cell).

Resources