I have the following problem.
example
If a cell has no content, I want to hide the cell. The logic as you can see allows, that constantly 5 cells get returned:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(section == 0){
return 1
}
return 5
}
Here is the logic of the actual table view:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.section{
case 0:
let cell = tableView.dequeueReusableCell(withIdentifier: "CampusGoogleMapsTableViewCell", for: indexPath) as! CampusGoogleMapsTableViewCell
if let building = self.selectedPOIOffice.room?.building{
cell.setMarkerForSelectedBuilding(building)
}
return cell
case 1:
let cell = tableView.dequeueReusableCell(withIdentifier: "CampusTableViewCell", for: indexPath) as! CampusTableViewCell
let iconLabel: UILabel = cell.iconLabel
let titleLabel: UILabel = cell.titleLabel
iconLabel.font = UIFont.fontAwesome(ofSize: 25, style: .solid)
switch indexPath.row{
case 0:
//name
titleLabel.text = selectedPOIOffice.name
titleLabel.textColor = UIColor.black
titleLabel.alpha = 0.8
iconLabel.text = FontAwesomeIcons.University.getIcon()
case 1:
//Phone
titleLabel.text = selectedPOIOffice.phone
titleLabel.textColor = HsKAmpusColors.Red
iconLabel.text = FontAwesomeIcons.Phone.getIcon()
case 2:
//email
titleLabel.text = selectedPOIOffice.email
titleLabel.textColor = HsKAmpusColors.Red
iconLabel.text = FontAwesomeIcons.Mail.getIcon()
case 3:
//Opening Hours
if(titleLabel.text == nil){ break}
titleLabel.text = selectedPOIOffice.openingHours
titleLabel.textColor = UIColor.black
titleLabel.alpha = 0.8
iconLabel.text = FontAwesomeIcons.Clock.getIcon()
case 4:
//Location
titleLabel.text = selectedPOIOffice.room?.roomAndBuildingString ?? ""
titleLabel.textColor = UIColor.black
titleLabel.alpha = 0.8
iconLabel.text = FontAwesomeIcons.PositionMarker.getIcon()
default:
break
}
return cell
default:
return UITableViewCell()
}
}
I think I could solve it with a simple for-statement to check if every cell has any content. Can you help me please with the application?
You are going about this all wrong. cellForRowAt is not the place to attempt to hide a cell. By the time it is called, the cell is going to be shown.
Do one of two things:
Update your data model used by your data source methods to only include the data you want to display. Or...
Implement heightForRowAt to return 0 for rows you don't wish to see.
"If a cell has no content, I want to hide the cell."
That sentence shows a basic misunderstanding of how table views and collection views work. Table views display tabular data from a data model. If you have empty entries in your model, remove them from the model before giving it to the table view.
Related
Whenever I scroll in the table view, the cell label's text disappears
which means the tableview data disappears. I tried changing the switch statement to if statement and it still doesn't work. Is my function to change the date format the reason?
Here is my code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "requestsCell", for: indexPath) as! RequestsTableViewCell
let requesten = requestsList[indexPath.row]
switch requesten.type {
case "FLT" :
cell.requestType.text = "Flight"
let reqDateTimeDate = stringToDate(strdate: requesten.reqDate!)
cell.requestDate.text = "\(dateFormatterPrint.string(from: reqDateTimeDate))"
cell.fromDate.isHidden = true
cell.toDate.isHidden = true
case "DOF" :
cell.requestType.text = "Day Off"
let reqDateTimeDate = stringToDate(strdate: requesten.reqDate!)
cell.requestDate.text = "\(dateFormatterPrint.string(from: reqDateTimeDate))"
cell.fromDate.isHidden = true
cell.toDate.isHidden = true
case "VAC" :
cell.requestType.text = "Vacation"
let reqDateFrom = stringToDate(strdate: requesten.dateFrom!)
let reqDateTo = stringToDate(strdate: requesten.dateTo!)
cell.requestDate.isHidden = true
cell.toDate.text = "To: \(dateFormatterPrint.string(from: reqDateTo))"
cell.fromDate.text = "From: \(dateFormatterPrint.string(from: reqDateFrom))"
default:
cell.requestType.text = ""
}
return cell
}
You should set up your code better such that each and every case statement changes the same properties. Or you need to override the prepareForReuse method so that each time the cell is reset to a common state.
Looking at your code - every case statement should assign these properties:
requestType.text
requestDate.text
fromDate.isHidden
fromDate.text
toDate.isHidden
toDate.text
What I want to ask you is "Can one UITableviewcell be used for multiple tableview like viewholder that can use anytime for recyclerview in android?" what I used to do is in one viewcontroller I have a tableview with a custom Cell and gave its identifier as normal but if I trying to use another uitableview in another Viewcontroller with that cell that inside the previous tableview, it always gives me a blank cell with white background. is there a way to use it like that?
EDIT: Here is what my tableview look like when i've already set cellforrow for it already.
Click to view and here what my cell look like Click to view cell and here are my code for different cell in a tableview, It'll work if i use use those 2 cell in current tableview
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0{
let cell = self.mytable.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! HistoryItemTableCell
let view = UIView(frame: CGRect(x: 0, y: cell.frame.maxY, width: cell.frame.size.width, height: cell.frame.size.height))
view.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.3)
cell.selectedBackgroundView = view
let order = OrderItemObj
cell.num_of_day.text = "\(order.ticket_type.name)"
cell.ref_num.text = order.order_tx_number
cell.quantity.text = order.number_tickets
cell.price.text = "$\(order.ticket_type.price).00 USD"
if order.status == "unpaid"{
cell.ic_status.image = UIImage(named: "ic_status_unpaid")
}else{
cell.ic_status.image = UIImage(named: "ic_status_paid")
}
cell.start_date.text = "\(order.start_date)"
cell.end_date.text = "\(order.expired_date)"
return cell
}else{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! OrderDetailTicketCell
let t = listTicket[indexPath.row]
cell.dob.text = t.dob
cell.gender.text = t.sex
cell.nation.text = t.nationality
let url = URL(string: t.photo)
cell.imageN.kf.setImage(with: url)
return cell
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 3
}else{
return self.listTicket.count
}
}
override func viewDidLoad(){
super.viewDidLoad()
mytable.register(HistoryItemTableCell.self, forCellReuseIdentifier: "cell")
ViewHistoryItem()
mytable.dataSource = self
mytable.delegate = self
}
Yes you can. You have to register it again for the new tableView. It is just like how you create variables using the same type. This is also a class which can be used to create objects. Doesn't matter where you want to use it.
On the other hand if you are asking if instances of the same cell which are present in a tableView can be reused in another tableView, then the answer is no, because they have only been registered for that particular tableView.
I have a UITableView that gets reloaded if a button in some of its cells gets tapped. The issue appears if the following steps are made:
Tap a button on a cell so that another cells appear below the tapped cell on reloadData.
Scroll up the table view so that it hides some of the upper content.
Tap the button again to hide the cells that were just shown making another call to reloadData.
Then the table view goes up and hides the upper content (the whole first cell and part of the second one). Here is some of the code:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if shouldShowImageResolutionOptions && (indexPath.row == 2 || indexPath.row == 3 || indexPath.row == 4) {
return isLastKnownDeviceOrientationLandscape ? 60 : 80
}
if shouldShowImageDisplayOptions && (indexPath.row == 3 || indexPath.row == 4) {
return isLastKnownDeviceOrientationLandscape ? 60 : 80
}
return isLastKnownDeviceOrientationLandscape ? tableView.frame.size.height / 2.5 + 40 : tableView.frame.size.height / 4.5 + 40
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if shouldShowImageResolutionOptions {
return 6
}
if shouldShowImageDisplayOptions {
return 5
}
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.row {
case 0:
let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifiers.IntervalCell) as! IntervalCell
cell.selectionStyle = .none
cell.dottedSliderView.contentMode = .redraw
cell.adjustThumbPosition()
return cell
case 1:
let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifiers.SettingsCell) as! SettingsCell
cell.selectionStyle = .none
cell.titleLabel.text = LabelTitles.ImageResolution
cell.choiseLabel.text = LabelTitles.HDResolutioin
cell.onButtonTap = {
self.shouldShowImageResolutionOptions = !self.shouldShowImageResolutionOptions
self.shouldShowImageDisplayOptions = false
self.menuTableView.reloadData()
}
return cell
case 2:
if(shouldShowImageResolutionOptions) {
let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifiers.SingleSettingCell, for: indexPath) as! SingleSettingCell
cell.selectionStyle = .none
cell.mainSettingLabel.text = Settings.HDResolution
return cell
}
let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifiers.SettingsCell) as! SettingsCell
cell.selectionStyle = .none
cell.titleLabel.text = LabelTitles.ImageDisplay
cell.choiseLabel.text = LabelTitles.EnlargeImage
cell.onButtonTap = {
self.shouldShowImageDisplayOptions = !self.shouldShowImageDisplayOptions
self.shouldShowImageResolutionOptions = false
self.menuTableView.reloadData()
}
return cell
case 3, 4:
if(shouldShowImageResolutionOptions) {
let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifiers.SingleSettingCell, for: indexPath) as! SingleSettingCell
cell.selectionStyle = .none
cell.mainSettingLabel.text = indexPath.row == 3 ? Settings.HighResolution : Settings.MediumResolution
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifiers.SingleSettingCell, for: indexPath) as! SingleSettingCell
cell.selectionStyle = .none
cell.mainSettingLabel.text = indexPath.row == 3 ? Settings.ShowFullImage : Settings.EnlargeImage
return cell
}
case 5:
let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifiers.SettingsCell) as! SettingsCell
cell.selectionStyle = .none
cell.titleLabel.text = LabelTitles.ImageDisplay
cell.choiseLabel.text = LabelTitles.EnlargeImage
cell.onButtonTap = {
self.shouldShowImageDisplayOptions = !self.shouldShowImageDisplayOptions
self.shouldShowImageResolutionOptions = false
self.menuTableView.reloadData()
}
return cell
default:
return UITableViewCell()
}
}
From the UITableView's reloadData method documentation (https://developer.apple.com/documentation/uikit/uitableview/1614862-reloaddata):
The table view’s delegate or data source calls this method when it wants the table view to completely reload its data. It should not be called in the methods that insert or delete rows, especially within an animation block implemented with calls to beginUpdates and endUpdates.
There are dedicated insert/delete rows methods for inserting and deleting:
insertRowsAtIndexPaths:withRowAnimation:(https://developer.apple.com/documentation/uikit/uitableview/1614879-insertrowsatindexpaths)
deleteRowsAtIndexPaths:withRowAnimation: (https://developer.apple.com/documentation/uikit/uitableview/1614960-deleterowsatindexpaths)
So when you refactor your code to use those it should work smoothly and as expected.
I' m working to an e-commerce app and I have an issue when trying to set the height of a specific row. When I choose the category the table reloads and depending on the category i choosed more or less rows are coming through the api.
So with this setup it s working when i select a specific category but when i choose another it will mess my rows and will increase the height to another
let firstCellHeight:CGFloat = 265
let addBtnCellHeight:CGFloat = 60
let phoneCellHeight: CGFloat = 95
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let cellIdentifier = self.tableCells[indexPath.row]
let height:CGFloat!
switch cellIdentifier {
case "AddAdImagesCell":
height = self.firstCellHeight
case "AddBtnCell":
height = self.addBtnCellHeight
case "ExtraFieldCell":
if indexPath.row == 4 {
height = self.phoneCellHeight
} else {
height = 44
}
default:
height = 44
}
return height
}
For the cell for row i m having this code:
case "ExtraFieldCell":
let currentField = self.extraFields[indexPath.row - self.firstExtraFieldIndex]
switch currentField.type {
case "select":
let cell = tableView.dequeueReusableCell(withIdentifier: "ExtraFieldSelectCell")!
if currentField.name == "area"
return cell
case "text":
let cell = tableView.dequeueReusableCell(withIdentifier: "ExtraFieldTextCell") as! FiltersTFCell
cell.label.text = "\(currentField.label):"
return cell
case "checkbox":
let cell = tableView.dequeueReusableCell(withIdentifier: "ExtraFieldCheckboxCell") as! CheckboxCell
cell.fieldLabel.text = currentField.label
return cell
default:
let cell = UITableViewCell()
return cell
}
So how to set the height 95 always for the row with the case "text"
try using self.tableView.rowHeight when setting the cell, I tried to apply with your example code below, if you have any questions call me back.
case "ExtraFieldCell":
let currentField = self.extraFields[indexPath.row - self.firstExtraFieldIndex]
switch currentField.type {
case "select":
self.tableView.rowHeight = firstCellHeight
let cell = tableView.dequeueReusableCell(withIdentifier: "ExtraFieldSelectCell")!
if currentField.name == "area"
return cell
case "text":
self.tableView.rowHeight = addBtnCellHeight
let cell = tableView.dequeueReusableCell(withIdentifier: "ExtraFieldTextCell") as! FiltersTFCell
cell.label.text = "\(currentField.label):"
return cell
case "checkbox":
self.tableView.rowHeight = phoneCellHeight
let cell = tableView.dequeueReusableCell(withIdentifier: "ExtraFieldCheckboxCell") as! CheckboxCell
cell.fieldLabel.text = currentField.label
return cell
default:
self.tableView.rowHeight = 44
let cell = UITableViewCell()
return cell
}
importantly, when is test comment or remove this method
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { ... }
This is the error i'm getting. I'm sure this is a simple fix, but i'm having a bit of trouble with it. I updated this thread to add the code.
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 5
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CheckListItem", for: indexPath)
let label = cell.viewWithTag(1000) as! UILabel
switch indexPath.row {
case 0:
label.text = "Walk the dog"
case 1:
label.text = "Brush my teeth"
case 2:
label.text = "Learn iOS development"
case 3:
label.text = "Soccer practice"
case 4:
label.text = "Eat ice cream"
default:
label.text = "Nothing"
}
return cell
}
This is what the viewWithTag(_:) function does:
Returns the view’s nearest descendant (including itself) with a
specific tag, or nil if no subview has that tag.
So what you're getting back is the object itself as well as its nearest descendant. It looks like you're just trying to set the text inside the cell, so what you can do is just change your code to this:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CheckListItem", for: indexPath)
switch indexPath.row {
case 0:
cell.textLabel.text = "Walk the dog"
case 1:
cell.textLabel.text = "Brush my teeth"
case 2:
cell.textLabel.text = "Learn iOS development"
case 3:
cell.textLabel.text = "Soccer practice"
case 4:
cell.textLabel.text = "Eat ice cream"
default:
cell.textLabel.text = "Nothing"
}
return cell
}
I was working through iOS Apprentice series and just encountered the same problem as you did.
I think you might want to check where you attach the tag:1000 to in Main.storyboard -- if you're failing because of this reason, you have probably attached the tag to the UITableViewCell instead of the Label (of type UILabel) under Content View.