This question already has an answer here:
deleteRowsATIndexPaths crashing [duplicate]
(1 answer)
Closed 7 years ago.
hi this is my code i keep getting the error when I delete a row.
I know that my array is not being updated as i delete a row and hence the error. Orerlist.list is an array getting my data
How many i resolve this issue?
Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
class RootTableViewController: UITableViewController,MenuItemSelectionDelegate {
var orderList = OrderList()
var delegate:MenuItemSelectionDelegate! = nil
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return orderList.list.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
cell.textLabel?.text = orderList.list[indexPath.row].menuItem
return cell
}
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
self.tableView.beginUpdates()
self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
self.tableView.endUpdates()
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "reserve"{
let vc = segue.destinationViewController as! BeautyViewController
vc.delegate = self
}
}
func didSelectMenuItem(controller: UITableViewController, order: OrderModel) {
orderList.addList(order)
controller.navigationController?.popViewControllerAnimated(true)
tableView.reloadData()
}
}
You have to delete the corresponding element from the orderList.list as well.
Otherwise you tell the tableview you delete some data, it then re-queries the data and finds out that the data stayed the same - a contradiction to what you have just told it.
Something like
orderList.list.removeAtIndex(indexPath.row)
Related
I am facing an issue with UITableView.
I want to dynamically fill its cells with data fetched from a remote database, so it takes some times before the data arrived.
Here is the code:
class MailBoxViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var users: [NSDictionary] = []
override func viewDidLoad() {
super.viewDidLoad()
// call to rest API code to get all users in [NSDictionary]
(...)
// set table view delegate and data source
self.tableView.delegate = self
self.tableView.dataSource = self
}
// set number of sections within table view
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
// set number of rows for each section
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return self.users.count
}
return 0
}
// set header title for each section
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "Users"
}
}
// set cell content for each row
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// deque reusable cell
let cell = tableView.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath) as UITableViewCell
// set item title
if indexPath.section == 0 {
cell.textLabel?.text = self.users[indexPath.row]["firstname"] as? String
}
return cell
}
}
The problem is that when tableView functions are called to set number of rows for each section and to set cell content for each row, my [NSDictionary] users is still empty.
How could I do to set rows and cells only after my call to rest API code to get all users in [NSDictionary] is finished?
Thank you for any feedback.
Regards
When you get the response from the API, you should set self.users = arrayOfUsersYouReceivedFromServer and then call self.tableView.reloadData(). This
After users is populated, call tableView.reloadData(). This will call your data source methods again.
When you're done fetching the users call tableView.reloadData().
I have a TableViewController (lets call TVC1) with a row that says "OD" (which stands for Outer Diameter).
Upon selecting this row, a bunch of rows in a new TableViewController (lets call TVC2) containing the various OD (casingOD in my code) shows. What I want to happen is when the user selects the OD it will segue back to the main TableViewController with the string that corresponds to the user selection. My code for this currently fails...Could anyone help point me in the right direction? If you require TVC1 code i'll happily post it, i'm just trying to save any unneccessary code reading for you folks :)
My TVC2 code is as follows:
import UIKit
class CasingSelectionTableViewController: UITableViewController {
var selectedData: Data?
let casingOD = ["114.3", "127.0", "139.7", "168.3" , "177.8", "193.7", "219.1", "244.5", "247.6", "273.1", "298.4", "298.4", "339.7", "406.4", "473.0", "508"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
switch selectedData! {
case .OuterDiameter:
print(casingOD)
case .Weight:
print(casingWeight114) // I deleted the casingWeight114 line of code as its not required for this question
case .InnerDiameter:
print(id114) // I deleted the id114 line as its not required for this question
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// 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 {
// #warning Incomplete implementation, return the number of rows
return casingOD.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var casingSpec: UITableViewCell!
if selectedData == Data.OuterDiameter {
casingSpec = tableView.dequeueReusableCellWithIdentifier("selectedCasingSpec", forIndexPath: indexPath)
let casingODSpec = casingOD[indexPath.row]
casingSpec.textLabel?.text = casingODSpec
return casingSpec
} else {
return casingSpec
}
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selection: UITableViewCell!
selection.textLabel?.text = indexPath.row as! String
}
What I want to happen is when the user selects the OD it will segue back to the main TableViewController with the string that corresponds to the user selection.
First of all you'll need to implement a way for TVC2 to notify TVC1 that a value has been selected.
A common way to do such thing is by using delegation. You can define a delegate protocol like this:
protocol TVC2Delegate {
func tvc2(tvc2: TVC2, didSelectOuterDiameter outerDiameter: String)
}
Then add a var delegate: TVC2Delegate? property to TVC2.
You'll then make TVC1 comform to TVC2Delegate by implementing that method in TVC1.
When presenting TVC2 from TVC1 remember to set it as the delegate for TVC2.
// In TVC1
tvc2.delegate = self
To connect TVC1 and TVC2 you could add a bit o logic to your tableView(tableView:,didSelectRowAtIndexPath:) method call the delegate with the selected value
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let stringValue = indexPath.row as! String
// Do anything you need to do related to TVC2 here.
// Then finally
delegate?.tvc2(self, didSelectOuterDiameter: stringValue)
}
Finally, in TVC1's implementation of the delegate method you can take care of dismissing TVC2 if needed.
Update:
This is how the final implementation of these bits might look like:
// In TVC1
class TVC1: UITableViewController, TVC2Delegate {
// ...
// Implement the method(s) of TVC2Delegate
func tvc2(tvc2: TVC2, didSelectOuterDiameter outerDiameter: String) {
// Do whatever you need to do with the outerDiameter parameter
}
}
// In TVC2
protocol TVC2Delegate {
func tvc2(tvc2: TVC2, didSelectOuterDiameter outerDiameter: String)
}
class CasingSelectionTableViewController: UITableViewController {
var delegate: TVC2Delegate?
// ...
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let stringValue = casingOD[indexPath.row]
// Do anything you need to do related to TVC2 here.
// Then finally
delegate?.tvc2(self, didSelectOuterDiameter: stringValue)
}
}
Use the delegate approach as suggested in the answer by #Mokagio. And in case you're having issue in getting the string, here is the answer
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath) as! UITableViewCell
let stringValue = cell.textLabel.text //You can get this from your datasource as well)
//call the delegate
}
I have 2 sections, First have 1 row and Second have 4 rows. I can see the switch, labels and text fields in the storyboard. It is loading blank in the Simulator. I have checked all the IBOutlets are connected.
Following is my code from Table View Controller
import UIKit
class InfoTableViewController: UITableViewController {
var sectionArray: NSMutableArray = ["Social Security Benefits", "Demographics"]
var socialArray: NSMutableArray = ["Estimate Social Security Benefits"]
var demoArray: NSMutableArray = ["Name:", "DOB:", "Age of Retirement:", "Years of Retirement"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return self.sectionArray.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0{
return socialArray.count
} else {
return demoArray.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.section == 0 {
if indexPath.row == 0 {
println(indexPath.section)
println(indexPath.row)
let cell: SocialTableCell = SocialTableCell(style: UITableViewCellStyle.Default, reuseIdentifier: "social")
cell.social?.text = "Estimate"
cell.toggle?.setOn(false, animated: true)
return cell
}
} else if indexPath.section == 1 {
if indexPath.row == 0 {
println(indexPath.section)
println(indexPath.row)
let cell: NameTableCell = NameTableCell(style: UITableViewCellStyle.Default, reuseIdentifier: "name")
return cell
}else if indexPath.row == 1{
println(indexPath.section)
println(indexPath.row)
let cell: DOBTableCell = DOBTableCell(style: UITableViewCellStyle.Default, reuseIdentifier: "DOB")
}else if indexPath.row == 2{
println(indexPath.section)
println(indexPath.row)
let cell: RetirementTableCell = RetirementTableCell(style: UITableViewCellStyle.Default, reuseIdentifier: "AgeOfRetirement")
}else if indexPath.row == 3{
println(indexPath.section)
println(indexPath.row)
let cell: YearsRetirementTableCell = YearsRetirementTableCell(style: UITableViewCellStyle.Default, reuseIdentifier: "YearsOfRetirement")
}
}
return UITableViewCell()
//cell.configureFlatCellWithColor(UIColor.greenSeaColor(), selectedColor: UIColor.cloudsColor(), roundingCorners: UIRectCorner())
}
}
I also have created 5 Table View Cells Classes for each cell. I also have checked for all the identifiers and they are correct.
Following is the image of my storyboard
But when i run it, the simulator only shows sections and the cells are empty.
Any Help will be highly appreciated.
Thanks
You can do this with using Static cells.
Go to your Table view => Attribute Inspector => in content select static cells, then in section , put 2 ( because you have 2 sections )
Then Click a one section in Storyboad Hierarchy => Attribute Inspector => put number of rows ( put 1 because you have one row in 1st section ) you want in each section.Again select the other section and put number of rows you want( put 4 becuase you have 4 rows in 2nd section)
Then Click the each row cell and add Label and put your label name. and you can put toggle button too for tour first section.
Create a new cocoa touch class :- The class look like this ( You Should comment or deleter these functions ,
// MARK: - Table view data source
/*
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return 2
}
*/
/*
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath) as! UITableViewCell
// Configure the cell...
return cell
}
*/
And Finally Table class looks like this ,
import UIKit
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
}
}
This is how your cellForRowAtIndexPath: should look like. Below is an example from your first section first row, you can align all others accordingly.
As a side note, noticed that except for your first section first row, for all other table rows you are not setting any labels etc. and that control end up coming to statement return UITableViewCell() leading to empty cells. Please double check that too.
if indexPath.row == 0 {
println(indexPath.section)
println(indexPath.row)
let socialCellId: NSString = "social"
var cell: SocialTableCell = tableView.dequeueReusableCellWithIdentifier(socialCellId) as SocialTableCell
if cell == nil
{
cell = SocialTableCell(style: UITableViewCellStyle.Default, reuseIdentifier:socialCellId)
}
cell.social?.text = "Estimate"
cell.toggle?.setOn(false, animated: true)
return cell
}
I had the exact same issue, and none of the comments above helped me. I finally figured it out, perhaps you were having the same issue.
For me, I had accidentally copied and pasted a second tableview in the designer. The second tableView was on top of the first but only the first table view was connected to my TableViewController, and so the cells for the visible one at runtime were never created, hence blank.
Check that, hopefully that helps.
Double TableView
I need to pass data from one view controller to another view controller. I used segue (detail) and define a model class named as "Photo".
TableViewController looks like the following:
var photos = [Photo]() //strongly typed swift array
override func viewDidLoad() {
super.viewDidLoad()
var newPhoto = Photo(name:"cat ", fileName:"cat", notes:"cat_file")
photos.append(newPhoto)
var newPhoto2 = Photo(name:"r2 ", fileName:"r2", notes:"r2")
photos.append(newPhoto2)
}
And the other view controller (DetailViewController) looks like the following:
import UIKit
class PhotoDiplayViewController: UIViewController {
var currentPhoto: Photo?
#IBOutlet weak var currentImage: UIImageView!
#IBOutlet weak var currentLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
var image = UIImage(named: currentPhoto!.fileName)
self.currentImage.image = image
self.currentLabel.text = currentPhoto?.name
println(currentPhoto!.name + currentPhoto!.fileName + currentPhoto!.notes)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
When I am running the program, the table view is loading fine and if i click on any cell it is going to the detail view controller. but noting is there in the detail view controller. And I used println() to check and the output is coming in the debugger like the following:
cat cat cat_file
To pass data, I used the following segue code block:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
var secondScene = segue.destinationViewController as! PhotoDiplayViewController
if let indexPath = self.tableView.indexPathForSelectedRow(){
let selectedPhoto = photos[indexPath.row]
secondScene.currentPhoto = selectedPhoto
}
}
But still no luck! Tried to figure out where I am missing? Can anybody tell me where I am lagging?
UPDATE: complete detail view controller class code
UPDATE: Full detail of My table view code
import UIKit
class PhotoTableViewController: UITableViewController {
var photos = [Photo]() //strongly typed swift array
override func viewDidLoad() {
super.viewDidLoad()
var newPhoto = Photo(name:"cat ", fileName:"cat", notes:"cat_file")
photos.append(newPhoto)
var newPhoto2 = Photo(name:"r2 ", fileName:"face.jpg", notes:"r2")
photos.append(newPhoto2)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return photos.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("photoCell", forIndexPath: indexPath) as! UITableViewCell
var currentPhoto = photos[indexPath.row]
cell.textLabel?.text = currentPhoto.name
return cell
}
/*
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the item to be re-orderable.
return true
}
*/
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
var secondScene = segue.destinationViewController as! PhotoDiplayViewController
if let indexPath = self.tableView.indexPathForSelectedRow(){
let selectedPhoto = photos[indexPath.row]
secondScene.currentPhoto = selectedPhoto
}
}
}
Don't use a segue. Use this, its easier.
Follow these steps...
1: Create a Separate file called Manager.swift and place this code in it...
//manager.swift
import Foundation
struct Manager {
static var dataToPass = String()
}
2: Clean your project by pressing Shift+Command+K.
3: In the first view controller set the dataToPass to the data you want to pass...
Manager.dataToPass = self.dataToPass
4: In the second view controller retrieve the data and set the content to the dataToPass...
self.dataToReceive = Manager.dataToPass
5: Your Finished!!
The code which I presented is working fully after deleting all the image view and label from the storyboard and remapping. But I am wondering what was the problem. However, I want to share one screen shot:
In the screen shot, you will see 3 components: 2 labels and 1 image view. One 1 label's text is dark black colored but the for the other 2 it's not alike them. All of them are properly configured.
Still I don't know why this happens? I am not sure .... is it possible to add some hidden components on the top of storyboard??? or is it a bug of Xcode????
However, if you have similar experience please share. My aim is not only solve the problem but also to understand the cause of the problem.
:)
I have a view with two tables (propertyTypeList & propertyDetailList) and a text view (propertyDetailView)
What I'm trying to do is have propertyDetailList populate with an array based upon the selection made in propertyTypeList, then have propertyDetailView populate based upon the selection in propertyDetailList.
I can view the currently selected row using my bottom function and indexPath.row, but I can't get that call to work in the function above.
here is my code:
class NewProjectView: UIViewController {
#IBOutlet weak var propertyTypeList: UITableView!
#IBOutlet weak var propertyDetailList: UITableView!
#IBOutlet weak var propertyDetailView: UITextView!
var testarray = ["test1", "test2"]
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int
{
if tableView == propertyTypeList {
return projectSources.count;
}
else {
return testarray.count
}
}
func tableView(tableView: UITableView!,
cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!
{
let cell:UITableViewCell = UITableViewCell(style:UITableViewCellStyle.Default, reuseIdentifier:"Cell")
if tableView == propertyTypeList {
cell.textLabel?.text = projectSources[indexPath.row]
if indexPath.row == 0 {
println("Row 0")
}
else
{
println("Not Row 0")
}
return cell
}
else
{
cell.textLabel?.text = testarray[indexPath.row]
return cell
}
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
println(indexPath.row)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
How do I access indexPath.row from the second tableView Func?
regarding your comment on #Duncan C's answer #BlueRad just reload your second tableView data in if statement
propertyDetailList.reloadData()
That will do the trick.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView == propertyTypeList {
self.propertyDetailList.reloadData()
}
}
It looks like there is a typo in your question. You said:
I have a view with two tables (propertyTypeList & propertyDetailList)
and a text view (propertyDetailView)
What I'm trying to do is have propertyDetailList populate with an
array based upon the selection made in propertyDetailList, then have
propertyDetailView populate based upon the selection in
propertyDetailList.
Did you mean to say "What I'm trying to do is have propertyDetailList populate with an array based upon the selection made in propertyTypeList..." That would make more sense.
So you essentially have a master-detail setup, where propertyTypeList is a master table view and propertyDetailList is the detail table view.
I'm assuming that both table views have their data source and delegate pointed to the same view controller.
What you need to do is write your tableView:didSelectRowAtIndexPath:method to check the tableView parameter. That method will be called when the user selects a row in either tableView, but the tableView parameter will let you figure out which table view the selected cell belongs to.
So your code might look something like this:
func tableView(
tableView: UITableView,
didSelectRowAtIndexPath indexPath: NSIndexPath)
{
if (tableView == propertyTypeList)
{
//Switch the selected item in the detail list
}
else
{
//do whatever is appropriate for selecting a detail cell.
}
}