I have a UITableView, and added it as a subview of a UIView.
Whenever I select a cell that had everything disappears in UITableView.
Already had this problem at other times, however it stopped happening without changes on code.
Currently I use Swift, however already happened in Objective-C.
Code:
TableView delegates:
numberOfSectionsInTableView
numberOfRowsInSection
cellForRowAtIndexPath
heightForRowAtIndexPath
didSelectRowAtIndexPath
Also registered the identifier of the cell
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
listObjectsToList = ["String 1","String 2","String 3"]
self.view.frame.size = CGSizeMake(300, 110)
self.view.layer.cornerRadius = 5
self.view.layer.masksToBounds = true
self.tableView.scrollEnabled = false
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listObjectsToList.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell
cell.textLabel.text = listObjectsToList.objectAtIndex(indexPath.row) as? String
return cell
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 40
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.tableView.deselectRowAtIndexPath(indexPath, animated: true)
let objectSelected: AnyObject = listObjectsToList.objectAtIndex(indexPath.row)
object.type = objectSelected as String
self.view.removeFromSuperview()
}
However the method didSelectRowAtIndexPath is not called...
Code adding tableview:
var listTypeTableViewController = ListTypeTableViewController()
listTypeTableViewController.view.frame = CGRectMake(10, 80, 300, 130)
self.view.addSubview(listTypeTableViewController.view)
I have tried:
var listTypeTableViewController = ListTypeTableViewController()
listTypeTableViewController.view.frame = CGRectMake(10, 80, 300, 130)
self.view.addSubview(listTypeTableViewController.tableView)
But without success
The problem was and I was just trying to extract the UITableView to show it. When I need to add a UITableViewController as a child of my UIViewController:
var listTypeTableViewController = ListTypeTableViewController(nibName: "TableViewTypeScheduling", bundle: nil)
listTypeTableViewController.view.frame = CGRectMake(10, 80, 300, 130)
addChildViewController(listTypeTableViewController)
view.addSubview(listTypeTableViewController.view)
The reason is this line in tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) method:
self.view.removeFromSuperview()
You remove a view and all of its superviews, including the table view.
Related
I have a table view with height of cells set to automatic.
Once in a few runs, a few cells in the table are displayed empty (white space). Upon debugging, I noticed that the 'cellForRowAtIndexPath' function returns these cells as usual. Also, every time this bug shows up, the console has this message:
"Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a tableview cell's content view. We're considering the collapse unintentional and using standard height instead."
Scrolling up and down a couple of times fixes the issue and cells are displayed.
I use this following functions for height:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
I have been stuck with this for a week and any help would be greatly appreciated!
Edit: Code for the cells:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var row = indexPath.row
tableView.allowsSelection = false;
let cellIdentifier = "testTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! testTableViewCell
let post: PostMdl!
if(row < posts.count) {
post = posts[row]
}
else {
return cell
}
cell.label.delegate = self
cell.label.enabledTextCheckingTypes = NSTextCheckingType.Link.rawValue
cell.label.userInteractionEnabled = true
cell.brandName.text = post.brandName
cell.timeStamp.text = post.timeStamp
cell.brandImg.sd_setImageWithURL(post.brandImgUrl, placeholderImage: UIImage(named: "placeHolder"))
if let img = post.postImg {
cell.mainImg.image = img
} else {
cell.mainImg.image = UIImage(named:"lazyLoad")
}
cell.label.text = post.postTag
cell.labelDistanceFromImg.constant = 30
cell.labelDistanceToBtm.constant = 30
cell.postTag = post.postTag
cell.socNtwkImg.image = UIImage(named: post.socNtwk)
return cell
}
I guess you had set wrong constraints to subviews of UITableViewCell.contentView.
This image shows what I reproduce your error:
Please note the right constaints of red view, it just has Top Left Right Height, lacks bottom constraint, when I add it and it looks good:
Yes, there still have problem that Unable to simultaneously satisfy constraints, we can modify the priority of height constraint, default priority is required(1000), we change to Hight(750), it works!
I don't know your cell's detail, but the error's reason is likely, hope my answer helps you.
Append codes
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return tableView.dequeueReusableCellWithIdentifier("cell")!;
}
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
}
What is your mobile phone version of the iOS?
In the previous iOS8 version I also encountered similar problems.
My method is as follows:
tableView.estimatedRowHeight = 88.0
tableView.rowHeight = UITableViewAutomaticDimension
Hope can help you
I'm trying to build a uitableviewcontroller and am having difficulty dealing with swift 3. Whenever the numberOfRowsInSection gets called, it gets called 4 times and then the app crashes. Does anyone know how to implement this is swift 3?
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 6;
}
There are 6 items in the array that I want to populate the table with. I printed the array count to confirm. this is the full view controller.
class PCRInpatientsViewController: UITableViewController
{
var listInpatient = [PCRPatient]();
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated);
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.separatorStyle = UITableViewCellSeparatorStyle.none;
self.title = "Inpatients";
let view = self.view.frame;
let background = UIView(frame: view);
background.backgroundColor = Constants.medisasDarkGrey;
self.tableView.backgroundView = background;
self.definesPresentationContext = true;
getPatients();
createUI();
}
func getPatients() {
var array = [PCRPatient]();
let i = Patients.sharedInstance.patients
for int in 0..<i.count {
let d = i[int];
if d.status == PCRPatientStatus.PreAdmit {
print(d.name)
array.append(d);
}
}
print(array.count);
self.listInpatient = array;
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated);
}
func createUI (){
self.navigationController?.navigationBar.barTintColor = Constants.medisasRed;
self.navigationController?.navigationBar.isTranslucent = false;
self.navigationController?.navigationBar.tintColor = UIColor.white();
self.navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName:UIColor.white()];
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 6;
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 150;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> PCRCustomCell {
let patient = listInpatient[indexPath.row];
print("sjddsodkso \(patient.name)");
let cell = PCRCustomCell(reuse: "Inpatient", patient: patient);
cell.contentView.backgroundColor = Constants.medisasGrey;
return cell;
}
}
A couple syntax changes in Swift 3 that you need to incorporate. Xcode 8 beta will often suggest the correct fixes to your code but not always. I wrestled with this before finding out that you can check the updated documentation in Xcode 8 beta by hitting Shift + Command + 0 and searching for the first few characters of almost anything. In this case, search for UITableViewDataSource and you'll see everything I'm about to note.
1) cellForRowAtIndexPath is now cellForRowAt. NSIndexPath is now IndexPath. Implement like this:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// if you use prototype cells
let cell = tableView.dequeueReusableCell(withIdentifier: "prototypeCellIdentifier") as! CustomUITableViewCell
// configure your cell
return cell
}
2) heightForRowAtIndexPath is similarly now heightForRowAt. I haven't implemented this personally but but from the docs it's implemented like this:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return float
}
3) As others noted, numberOfRowsInSection should be dynamic, but I don't believe it's causing the crashes.
This is what I currently have
This is the code extending UITableViewDelegate
extension IngredientsViewController: UITableViewDelegate {
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 0
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if self.collapses[indexPath.section] == true {
return 0
}
return 50
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView(frame: CGRectMake(0, 0, tableView.frame.size.width, 50))
headerView.backgroundColor = UIColor(hexString: AppConfiguration.UIColorCode["orange"]!, alpha: 0.7)
headerView.tag = section
let headerString = UILabel(frame: CGRect(x: 12, y: 0, width: tableView.frame.size.width-24, height: 50)) as UILabel
headerString.text = (self.stores[section] as! Store).name!.uppercaseString
headerString.textColor = UIColor.whiteColor()
headerString.font = FontCollection.tableHeaderFont
headerView .addSubview(headerString)
let headerTapped = UITapGestureRecognizer (target: self, action:"sectionHeaderTapped:")
headerView .addGestureRecognizer(headerTapped)
return headerView
}
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if self.deselecting {
self.deselecting = false
return
}
if self.selectedIndexPath.indexOf(indexPath) != nil {
cell.selected = true
}
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
self.selectedIndexPath.removeAtIndex(self.selectedIndexPath.indexOf(indexPath)!)
self.deselecting = true
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.selectedIndexPath.append(indexPath)
}
func sectionHeaderTapped(recognizer: UITapGestureRecognizer) {
let indexPath : NSIndexPath = NSIndexPath(forRow: 0, inSection:(recognizer.view?.tag as Int!)!)
if (indexPath.row == 0) {
self.collapses[indexPath.section] = !self.collapses[indexPath.section]
//reload specific section animated
let range = NSMakeRange(indexPath.section, 1)
let sectionToReload = NSIndexSet(indexesInRange: range)
self.tableView.reloadSections(sectionToReload, withRowAnimation: .Fade)
}
}
}
My questions
The animation feels like each of the cell is fading in/out by itself. I'd rather have the whole section slide up/down. Been looking around but doesn't find much example of this. And I don't understand why the section header is having the white highlight on touch?
To maintain the selected between collapsing and expanding, I'm storing the selection in an array and use willDisplayCell to determine the selection state upon rendering the cell. Is this the right and most efficient approach?
Using the above code, when a cell is selected, collapse the section that cell belongs to, expand that section, then the said cell become unresponsive to didSelect and didDeselect (while unselected cells are still responsive). Any ideas why? Update for this issue below.
Thank you.
------ UPDATE ------
The above approach I used for rendering selected state with collapsed cell is flawed (issue #3). The below code will make the cell not interactive anymore
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if self.deselecting {
self.deselecting = false
return
}
if self.selectedIndexPath.indexOf(indexPath) != nil {
cell.selected = true
}
}
Instead of that, one should do this
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if self.selectedIndexPath.indexOf(indexPath) != nil {
tableView.selectRowAtIndexPath(indexPath, animated: false, scrollPosition: .None)
}
}
Apple has a great piece of sample code that I believe does exactly what you want: https://developer.apple.com/library/ios/samplecode/TableViewUpdates/Introduction/Intro.html. Summary: you use table header views to insert and remove rows when tapped, and UITableView can animate all that for you.
This StackOverflow answer contains a Swift-ified version of the Apple code, if that's what you'd prefer.
Either way, this approach is significantly more efficient than some implementations I've seen where people hide and show a UIStackView in their rows.
I'm creating an iOS app using swift. I want to build a non scrollable tableView which shows on screen all informations contained in datasource, so the height of each cell depends on the number of entries in data. For example, if the height of the view is 500 and data.count = 10, each cell's height is 50. A problem appears when the cell's height is ~100.8 (corresponding to 5 entries in my data, using my iPhone 5). In fact, even by setting tableView.separatorStyle = .None , a weird separator appears for this cell's height.
Below, the first image (7 entries in data) is normal and on the second (5 entries in data) those separators appear.
Here is my view controller :
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
let reuseIdentifier = "Cell"
var data = ["bobby", "bob", "john", "helena", "clara", "oliver", "steve"]
var visibleHeight:CGFloat!
override func viewDidLoad() {
super.viewDidLoad()
edgesForExtendedLayout = .None
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: reuseIdentifier)
editModeOff()
tableView.scrollEnabled = false
visibleHeight = viewVisibleSize.height
tableView.separatorStyle = .None
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return data.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier, forIndexPath: indexPath)
cell.textLabel!.text = data[indexPath.row]
cell.selectionStyle = .None
return cell
}
func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
let stringToMove = data[sourceIndexPath.row]
data.removeAtIndex(sourceIndexPath.row)
data.insert(stringToMove, atIndex: destinationIndexPath.row)
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete{
data.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Fade)
}
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
print(visibleHeight/CGFloat(data.count))
return visibleHeight/CGFloat(data.count)
}
func editModeOn(){
tableView.setEditing(true, animated: true)
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Done, target: self, action: "editModeOff")
}
func editModeOff(){
tableView.setEditing(false, animated: true)
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Edit, target: self, action: "editModeOn")
}
}
extension ViewController{
var viewVisibleSize:CGSize{
var size = view.bounds.size
if !UIApplication.sharedApplication().statusBarHidden{
size.height -= UIApplication.sharedApplication().statusBarFrame.height
}
if let navigationController = navigationController{
size.height -= navigationController.navigationBar.bounds.height
}
if let tabBarController = tabBarController{
size.height -= tabBarController.tabBar.bounds.height
}
return size
}
}
I always clear the color of the separator:
tableView.separatorColor = UIColor.clearColor()
I have found a dirty solution by setting :
cell.contentView.layer.borderWidth = 0.5
cell.contentView.layer.borderColor = UIColor.whiteColor().CGColor
in tableView:cellForRowAtIndexPath: method. But for sure, there is a better way...
In your viewDidLoad: you can try self.tableView.tableFooterView = UIView()
It is funny. Problem in your function tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
It has strange behavior for some float numbers and a separator will appear.
I offer you round a returning height like this:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
print(self.visibleHeight/CGFloat(self.tableArray.count))
let rett = self.visibleHeight/CGFloat(self.tableArray.count)
let convert = NSString(format: "%.0f", rett)
return CGFloat(convert.floatValue)
}
I have a UITableView inside of another view. Whenever I click on a cell in the table, I would like that cell to be highlighted until it is clicked again to deselect it. Using the didSelectRowAtIndexPath method, I have accomplished this. However, the cell selection takes a long time. I have to hold down on a cell for 3 seconds before it highlights the cell, rather than it being instantaneous. How do I get it to select the cell the instant it is touched?
Here is my relevant code.
class AddDataViewController : UIViewController {
#IBOutlet weak var locationTableView: UITableView!
var fstViewController : FirstViewController?
let locationTableViewController = LocationTableViewController()
override func viewWillAppear(animated: Bool) {
// Set the data source and delgate for the tables
self.locationTableView.delegate = self.locationTableViewController
self.locationTableView.dataSource = self.locationTableViewController
// Set the cell separator style of the tables to none
self.locationTableView.separatorStyle = UITableViewCellSeparatorStyle.None
// Refresh the table
self.locationTableView.reloadData()
}
override func viewDidLoad(){
super.viewDidLoad()
// Create a tap gesture recognizer for dismissing the keyboard
let tapRecognizer = UITapGestureRecognizer()
// Set the action of the tap gesture recognizer
tapRecognizer.addTarget(self, action: "dismissKeyboard")
// Add the tap gesture recognizer to the view
//self.view.addGestureRecognizer(tapRecognizer)
}
}
class LocationTableViewController : UITableViewController, UITableViewDelegate, UITableViewDataSource {
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return tableView.frame.height / 5
}
override func tableView(tableView: UITableView, shouldHighlightRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
override func tableView(tableView: UITableView, didHighlightRowAtIndexPath indexPath: NSIndexPath) {
tableView.cellForRowAtIndexPath(indexPath)?.backgroundColor = UIColor.greenColor()
}
override func tableView(tableView: UITableView, didUnhighlightRowAtIndexPath indexPath: NSIndexPath) {
tableView.cellForRowAtIndexPath(indexPath)?.backgroundColor = UIColor.whiteColor()
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.cellForRowAtIndexPath(indexPath)?.backgroundColor = UIColor.blueColor()
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.Value2, reuseIdentifier: "addDataCell")
cell.selectionStyle = UITableViewCellSelectionStyle.None
cell.textLabel!.text = "Test"
return cell
}
}
The problem was the UITapGestureRecognizer was interfering with the tap of the cell. I apologize that the tap gesture code was not in my initial post as I did not realize that could be the culprit. I have added it into the code snippet in the original post.
This should work but it would be much cleaner to make a custom cell subclass.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.Value2, reuseIdentifier: "addDataCell")
cell.textLabel!.text = "Test"
let backgroundView = UIView()
backgroundView.backgroundColor = YOUR_COLOR
cell.selectedBackgroundView = backgroundView
return cell
}
Remove because we want cell selection
cell.selectionStyle = UITableViewCellSelectionStyle.None
Remove since this is already taken care of in the cell subclass
override func tableView(tableView: UITableView, didHighlightRowAtIndexPath indexPath: NSIndexPath) {
tableView.cellForRowAtIndexPath(indexPath)?.backgroundColor = UIColor.greenColor()
}
override func tableView(tableView: UITableView, didUnhighlightRowAtIndexPath indexPath: NSIndexPath) {
tableView.cellForRowAtIndexPath(indexPath)?.backgroundColor = UIColor.whiteColor()
}
I don't have enough reputation points to reply to #Jason247, so hence typing it as an answer. He's right. I had the same issue with a delay of the cells registering a click. Commented out the UITapGestureRecognizer and the delay went away.
In my context, I was using a UISearchBar. I replaced my UITapGestureRecognizer with the search bar's optional delegate method:
class ViewController: UIViewController, UISearchBarDelegate{
func searchBarSearchButtonClicked(searchBar: UISearchBar)
{
self.mySearchBar.endEditing(true)
}
}
You can find more solutions for dismissing the keyboard when using a UISearchBar here