How to add three tableview cells in a tableview as Subview? - ios

I want to add 2 cells one as header the segue form the previous tableview and the the second cell for details like this prototype .
First Cell class "courseCell"
Second Cell Class "DetailsTVC"
I found many way but it doesn't work I hope someone help
I couldn't follow with this instruction
Initialize your custom tableviewcell - CustomCell1 and CustomCell2
Since tableviewcell is a subclass of UIView you can add it as a subview.
[TableCell addSubview:CustomCell1];
[TableCell addSubview:CustomCell2];
cell?.addSubview(<#view: UIView>)
[cell ?.addSubview(<#view: UIView>)]
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!, object: PFObject!) -> PFTableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell" , forIndexPath : indexPath) as? courseCell
if cell == nil
{
cell = courseCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cell")
}
cell?.name.text = object["title"] as! String!
cell?.location.text = object["Location"] as! String!
return cell!
}

To accomplish it:
TableViewController
- Tableview
- TableViewHeader (create a UIView subclass for this view)
- TableViewContent (datasource)
- TableViewCell1 (virgin american fligth ...)
- TableViewCell1 (departs los angeles)
- TableViewCell1 (arrives new york)
Note that you dont need XIB file to your tableviewController

I am not sure why your question title mentions 3 and your question only mentions 2 but from what I can understand based on the image, it appears you want the header to be the title of the course and the rest of the cells to be the details of the course.
To create a header for your TableView you can create a custom "HeaderCell" as you would create a custom cell and then use it in the method which specifies the Header:
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?{
//set the title, image or whatever you want for your custom header cell
}
Now you can use another custom TableView cell as you normally do to show the details.
Tutorial:
http://www.ioscreator.com/tutorials/customizing-header-footer-table-view-ios8-swift

Related

Can’t deselect previously selected cell when a new one is selected in a tableview where each cell contains tableview

I have a tableview, where each cell is a custom tableview cell. That custom tableview cell contains a tableview and a label.
Lets say, the outer tableview is called MainTableView. Each cell of MainTableView consists another tableview. The problem is , when I select
inner tableview cell one by one the previously selected cell is not get deselected.In first image I have selected cell contains text “Two”. Then In the second image I have selected cell contains text “Five” but perviously selected cell “Two” still in selection mode. I want to deselect the previous cell when select a new one.
I have tried
tableView.deselectRow(at: IndexPath, animated: Bool)
this method inside didSelectRowAt in custom tableviewcell class but it didn’t serve the purpose because the previous indexPath is from seperate tableview. So, how can I deselect the previous one?
to get the correct inner tableView,
Firstly , you should record the outside tableView's cell indexPath, which cell the inner tableView is in.
So your should record two indexPathes
var selectionRowInfo: (lastOutsideIP: IndexPath?, lastInnerIP: IndexPath?)
Secondly, you get the correct tableView, via the outsideTableView.
if the inner table is visible, you shall handle it immediately. through outTableView.indexPathsForVisibleRows
the else condition, you need not handle it. The tableView reuse mechanism will refresh its state.
// pseudo code.
if let lastOut = lastOutsideIP, let visibleRows = outTableView.indexPathsForVisibleRows, visibleRows.contains(lastOut){
let cell = tableView.cellForRow(at: lastOut) as! YourCell
// get the correct inner tableView via the cell
}
Because the inner tableViews are not related to each other. Select the cell of table one, will not affect the selection of cell of table two.
So your should build the connection manually.
Use a property to store the state var lastIndexPath: IndexPath?,
then every time select a indexPath,
// pseudo code.
if let last = lastIndexPath{
tableView.deselectRow(at: last, animated: true)
}
Please notice that, you should find the correct inner tableView, which has the lastIndexPath
The previous answer is along the right lines but has a flaw - it cannot distinguish between tableViews, which is the most important part. Also if the tableViews have different numbers of rows, it risks trying to access a row that doesn't exist and causing a crash.
To track the selected row in two tableViews (tv1 & tv2) you'll need to hold the selected row in each:
var tv1, tv2: UITableView!
var lastRowForTV1, lastRowForTV2: IndexPath?
and then respond to selections by identifying the tableView being used and adjusting the other (this assumes the two tableViews use the same datasource/delegate)
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView === tv1 {
lastRowForTV1 = indexPath
if let last = lastRowForTV2 {
tv2.deselectRow(at: last, animated: true)
lastRowForTV2 = nil
}
} else if tableView === tv2 {
lastRowForTV2 = indexPath
if let last = lastRowForTV1 {
tv1.deselectRow(at: last, animated: true)
lastRowForTV1 = nil
}
}
}
I have solved the problem by using the idea of first answer given by dengApro . The idea was to find the correct inner table which contains the previously selected cell.
I have two files one is ViewController.swift that contains the outer tableview MainTableView. Another one is CustomTableViewCell.swift with CustomTableViewCell.xib that contains the custom cell with tableview.
fileprivate var lastSelectedInnerTableView : Int = -1
fileprivate var lastSelectedRow: Int = -1
fileprivate var tableViewList :[UITableView] = []
I have added these 3 variables in CustomTableViewCell.swift file outside the class CustomTableViewCell. lastSelectedSection , lastSelectedRow these 2 variable are used to keep
track of the last selected inner tableview (lastSelectedInnerTableView) and the last selected cell of that inner tableView (lastSelectedRow). tableViewList variable is used to keep the
Inner tableView. Inside awakeFromNib() I have append the created inner tableviews.
override func awakeFromNib() {
super.awakeFromNib()
self.tableView.delegate = self
self.tableView.dataSource = self
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
tableViewList.append(self.tableView) // append the created inner tableviews
}
Then inside didSelectRowAt I have deselect the previous one:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if lastSelectedInnerTableView != -1 && lastSelectedRow != -1{
self.oldIndexPath.section = 0
self.oldIndexPath.row = lastSelectedRow
tableViewList[lastSelectedInnerTableView].deselectRow(at: self.oldIndexPath, animated: true)
}
lastSelectedInnerTableView = self.innerTableId
lastSelectedRow = indexPath.row
}
In ViewController.swift I have set innerTableId inside cellForRowAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let customCell: CustomTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "customCell") as! CustomTableViewCell
customCell.innerTableId = indexPath.row
customCell.customCellActionDelegate = self
return customCell
}

What does Apple mean by term "registers" in regards to creating new tableView cells?

I'm working through an exercise which uses tableviews. I noticed within a test during the exercise, they use a method I haven't needed in the past when implementing tableviews from storyboards. The method is:
func register(AnyClass?, forCellReuseIdentifier: String)
After reading the short description of this function in the reference pages. I'm curious to know what does apple mean by term "registers"? I half assume that since we are doing this exercise programmatically at the moment, this function is only needed if you're creating UITableviews programmatically. If this statement is incorrect, please let me know as I'd like to learn more.
Here is the code from the example:
func test_CellForRow_DequesCellFromTableView(){
let mockTableView = MockTableView()
mockTableView.dataSource = sut
mockTableView.delegate = sut
mockTableView.register(ItemCell.self, forCellReuseIdentifier: "ItemCell")
sut?.itemManger?.add(ToDoItem.init(title: "Foo"))
mockTableView.reloadData()
_ = mockTableView.cellForRow(at: IndexPath.init(row: 0, section: 0))
XCTAssertTrue(mockTableView.cellGotDequeed)
}
The DequeueReusable methods are there to check if any reusable cells are left before creating new ones. Hope you have an idea about the working of reusable cells
What happens when the queue is empty? Now we do need to create a cell. We can follow 2 methods to create a cell,
Create cell manually
Create it automatically by registering cell with a valid xib file
METHOD 1
if you do it with manually, you must check cell is empty or not after dequeueReusableCell check. Just like below,
// create a cell for each table view row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Reuse an old cell if exist else return nil
let cell:UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell!
//check cell is nil if nil you want to allocate it with proper cell
if(cell == nil){
//create cell manually
cell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "CellSubtitle")
}
// do stuff to the cell here
return cell
}
METHOD 2
We could create the cell manually like above which is totally fine. But it would be convenient if the table view would create the cell for us directly.
That way we don't have to load it from a nib or instantiate it.
For registering a cell with a xib or class we use func register(AnyClass?, forCellReuseIdentifier: String) method. Let see an example,
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(MyCell.self, forCellReuseIdentifier: "Cell")
}
// ...
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath:indexPath) as MyCell
// no "if" - the cell is guaranteed to exist
// ... do stuff to the cell here ...
cell.textLabel.text = // ... whatever
// ...
return cell
}
You are "registering" your custom Cell class - ItemCell - for reuse as a cell for your tableview.
See: https://developer.apple.com/reference/uikit/uitableview/1614888-register
"Register" tells XCode that the cell exists. A cell is registered under a "reuse identifier." This is a unique string that corresponds to your TableViewCell, in this case ItemCell.
A cell can also be registered in the Storyboard by filling out the "Identifier" in the cell's attributes inspector.

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.

Is there a way of styling UITableViewCell in swift?

I have a table view and I am adding several cells to it based on my json.
So far the code for adding cells looks as follows:
override func viewWillAppear(animated: Bool) {
let frame:CGRect = CGRect(x: 0, y: 90, width: self.view.frame.width, height: self.view.frame.height-90)
self.tableView = UITableView(frame: frame)
self.tableView?.dataSource = self
self.tableView?.delegate = self
self.view.addSubview(self.tableView!)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("CELL")
if cell == nil {
cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "CELL")
}
let user:JSON = JSON(self.items[indexPath.row])
cell!.textLabel?.text = user["description"].string
var photoURL = "/path/to/my/icon/google.png"
if let data = NSData(contentsOfFile: photoURL)
{
cell!.imageView?.image = UIImage(data: data)
}
return cell!
}
Besides the description in my json I have also username and price. So far - since I'm adding only imageView and description, 3 cells look like this:
Is there a way to style it so that each cell looks similar to this:
(price and username are grey here`)? How can I achieve this effect?
===EDIT:
this is how I populate my table:
I'm fetching data from rest webservice to json:
func getAllUsers() {
RestApiManager.sharedInstance.getUsers { json in
let results = json
for (index: String, subJson: JSON) in results {
let user: AnyObject = JSON.object
self.items.addObject(user)
dispatch_async(dispatch_get_main_queue(),{
self.tableView?.reloadData()
})
}
}
}
and I invoke this method in my viewWillAppear function
You can make your table use custom UITableViewCells and style them to your liking.
In a nutshell, you create a prototype cell in Storyboard that looks like the example you posted and connect it to a custom UITableViewCell class with the elements you created. At cellForRowInIndexPath you return your custom cell rather than regular UITableViewCells.
Check out this tutorial for details: http://shrikar.com/uitableview-and-uitableviewcell-customization-in-swift/
Create the layout of the cell using a custom style. Place labels and imageView like you would anywhere else in storyborad.
You will need to create a UITableViewCell file. The one I used is named ExampleTableViewCell. Make note of the subclass.
Now connect your cell to the ExampleTableViewCell you just created.
Now we can make outlets from the labels and imageView of the cell into the ExampleTableViewCell. Control drag from each element into the ExampleTableViewCell.
The final step is to configure the cell using the cellForRowAtIndexPath func. Make note of the var cell. We now cast this to the ExampleTableViewCell. Once we do this we can use the outlets in the ExampleTableViewCell to set our labels and image. Make sure you set the resuseIdentifier for the cell in the storyboard. If you are unfamiliar with this leave a comment and I can add instructions for this.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier") as! ExampleTableViewCell
cell.imageDisplay.image = yourImage
cell.descriptionLabel.text = yourDescription
cell.priceLabel.text = yourPrice
cell.usernameLabel.text = yourUsername
return cell
}
Subclass UITableViewCell. You can go to the TableView on your storyboard and go to one of the prototypes and set it's class to your custom class and it's style to Custom and then you can ctrl+click & drag outlets/actions to the UITableViewCell subclass the same way you would for a basic view controller.

Adding various content type within Table View Cell

I am new to IOS programming and will need some direction here.
I am trying to create a tableview with each rows having a image and some text.
I am able to take a TableViewController and programmatically was able to add basic text and rows. But can you please tell me how should i add more complex content. Trying to achieve something like this using program.
My current code looks like this in my TableViewController and its able to print a text message on each row.
override func numberOfSectionsInTableView(tableView: UITableView!) -> Int {
return 1
}
override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
return 10
}
override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell? {
var mycell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("frontcell", forIndexPath: indexPath) as UITableViewCell
mycell.textLabel.text = "Just a generic message"
return mycell
}
There is a
mycell.contentView
which takes in UIView. But how does it works? How should this object be created?
Appreciate your help.
------Update -----
Thanks to Suryakant for helping out. Answer below for his step by step how to do. Any one who needs the source code can use this. http://pastebin.com/ZfNqK4tW
Though you can achieve it by default UITableViewCell also, as #meda mention in his answer,
but it seems, you want different UIImageView size and 2 UILabels with different font size or may be some more controls there. For Achieving that you need to customize UITableViewCell and you can do that by subclassing UITableViewCell class.
Create a class by subclassing UITableViewCell.
e.g. your subclass say MyCell look like —
2.Go to storyboard and select prototypeCell and select Identity inspector, in Class type your custom class name (e.g MyCell )in place of UITableViewCell.
drag-n-drop all the controls you need and link with their IBOutlets (From MyCell to prototypeCell).
This goes as below..
3.Now goto Attributes Selector and give some Identifier to your MyCell, you can give any string you want.
4.Goto the class where you implemented UITableView delegates and update your cellForIndexPath as bellow
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath:
NSIndexPath!) -> UITableViewCell! {
let kCellIdentifier:String = "cell"
var cell = tableView.dequeueReusableCellWithIdentifier(kCellIdentifier) as MyCell!
if cell == nil {
// register Custom UITableView Class to UITableView
tableView.registerClass(MyCell.classForCoder(), forCellReuseIdentifier: kCellIdentifier)
cell = MyCell(style: UITableViewCellStyle.Default, reuseIdentifier: kCellIdentifier)
}
if var label = cell.cellMyCity{
label.text = cityList[indexPath.row]
}
if var label = cell.cellMyCountry{
label.text = countryList[indexPath.row]
}
if var imageView = cell.imageView{
imageView.image = UIImage(named :"img.png")
}
return cell
}
For reference you can see example code here.
You would create a subclass of UITableViewCell and then assign values to the property of your cell
mycell.textLabel.text = "Just a generic message"
mycell.detailTextLabel.text = "Just a detail message"
mycell.imageView.text = myImage
And for that you would use only one prototype Cell no need to duplicate them.

Resources