I have UITableView in a test project that is created this way:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView.delegate = self
tableView.dataSource = self
}
// height?
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
//return cellHeightWithoutSeparator + cellSeparatorHeight
//print("YES!")
return 80 // arbitrary
}
// table wants a UITableViewCell for each row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomCell
// customize cell here
return cell
}
// how many rows?
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 50 // arbitrary
}
}
The structure of the table view is:
So, as you can see the cell contains a button.
I want the button (or any clickable view that is a subview of the cell) to respond immediately to the touch-down event and so I unchecked "Delay Touch Down" in the ScrollView property inspector in the storyboard.
It is my understanding that unchecking that box is equivalent to the code:
tableView.delaysContentTouches = false
According to the docs, this method is supposed to work on IOS 2.0+
But setting this property true or false only works as expected on IOS 11 and 12, but on IOS 10 (and presumably earlier), it is as if the box was never unchecked and touches on the button are still delayed.
I understand that it is still possible to make views "clickable" in the cell, and respond immediately to "touch up inside," (because the touch-up event will cancel the delay on the touch-down), but I still want the touch down event to call immediately (on all iOS versions, not just 11+) because:
1) I want the re-order control to respond immediately.
2) I want to have visual feedback for touch-down be normal/immediate on views in the cell.
I have also consulted this similar, but not the same, question which was written 4 years ago, and I tried applying the answers, but none of those answers seem to apply to whatever the modern day reason for this problem is.
Does anyone know the cause and (preferably Swift) solution to this?
Related
If I change the orientation of the phone,I wanted to change the rowHeight of the tableview. its work in simulator not in Real Device
Anyone help me to solve this issue
Thank you
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if UIDevice.current.orientation.isLandscape{
printer.isEnabled = true
return 140
}else{
return 90
}
}
I believe the problem is with either your tableViewDelegate or with your tableViewDataSource these both have to be set to the current view controller (the table view's view controller) the Delegate is the thing which handles events such as touches etc and the dataSource places the data into the table view's rows and sections.
If you are NOT using a UITableViewController and instead are using a UIViewController with a UITableView added on it as a sub view then you need to do these two things:
(you can place this in viewDidLoad() or wherever else you feel is best, make sure to place it inside of an appropriate view controller life-cycle method or it will not be executed properly).
tableView.delegate = self
tableView.dataSource = self
An alternative to doing that is using a UITableViewController which you drag out directly from inside of interface builder (This already has the delegate and dataSource hooked up to itself therefore you do not have to set it yourself in code.):
Your code worked for me with one of those:
(Using this method also requires your view controller to inherit from UITableViewController as shown below).
class TableViewController: UITableViewController {
// Other data source methods such as cell for row at etc...
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if UIDevice.current.orientation.isLandscape{
print("row height: 140") // Test
return 140
} else {
print("row height: 90") // Test
return 90
}
}
}
I am trying to learn tableView and custom cell. I was able to find some tutorials.
However, I need to be able to have informations to go from a cell to another, when the users are editing informations.
I also have a problem implementing the code like an Alert view to work on a cell, even know it's working on a view controller.
Can someone guide me toward a tutorial, as I have difficulty to found one.
As I said in my comment, you're asking too many questions in one, anyway this is a small TableView tutorial:
First thing first, you need to create your tableview:
var tableView: UITableView!
then you'll add it to your view:
//Put these in your view did load
//Change tose vaues as you please
tableView = UITableView(frame: CGRect(x: 0.0, y: 0.0, width: UIScreen.main.bounds.width, height: 400), style: .plain)
view.addSubview(tableView)
So you'll need to register the cells:
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell") //Choose the identifier you want but always use the same within a single Table view
You'll have to set the delegate and the data source:
//put these in your view did load
tableView.delegate = self
tableView = self
And your class will need to conform to these protocolls, so, you'll add the UITableViewDelegate and the UITableViewDataSource to it:
class MyViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{}
So you'll have to set the protocol stubs as well:
//Here you'll decide the number of cells your tableView will have
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {}
//Here you'll decide the desired output of your cell (text, color ecc...)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//You have to create the cell first, it can be done as follows:
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) //Notice how I'm using the same identifier
return cell
}
There are plenty of other protocol stubs like:
//Use this to determine the behaviour of the cell when you tap it
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {}
//Use this to set the cell's height
private func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat{}
You can find more in the apple documentation.
I have a dynamic tableview with 3(cell1,cell2,cell3) custom cells, which gets reused anywhere from 0 times up to 9 times. (Maximum cells in the tableview will be 27 cells).
Each of those 3 custom cells have 3 text boxes each, and in addition the second cell has 2 buttons to select gender and 3rd custom cell has a drop down menu, which has values of whatever typed in the first textbox of cell1 cells.
Now the issue is that, textbox takes the touch input from user (as it should) and didselectrowat never gets called and therefore i am not getting indexPath.
But i need the indexPath, so that i can insert user details from each of those cells into an array.
(anArray.insert("", at: indexPath)
Since i am not getting indexPath, everything fails. How can i get the indexPath and get the touch on to the textbox?
I am thinking of something like a function that takes the touch ,gets the indexPath and then passes the control to textbox.
Things i have tried.
I disabled the textboxes before user touches the cell (and makes textboxes disabled once user deselects the cell, so cell takes the touch. Once didselectrowat gets called, make the textbox active again and make
textbox.becomeFirstResponder()
But somehow , the textbox never becomes the first responder even though breakpoint shows the control going through it and the textbox becomes active. But no keyboard popsup, if i touch the cell again, same process happens.
What seems to be the issue here? I saw another post on the same topic, i tried it but unfortunately that doesnt work and it was for a static tableview.
Also is there a good way to do this? Other than making the user click on the cell twice / clicking outside the textbox..
You can set your custom cell as delegate for UITextField and use a callback for editing begins/ends as below,
class MyCustomCell: UITableViewCell, UITextFieldDelegate {
public var editCallback: (() -> Void)?
/// Set 'textField.delegate = self' on initialization.
func textFieldDidBeginEditing(_ textField: UITextField) {
self.editCallback?()
}
}
And use the callback in cellForRowAt as,
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = MyCustomCell()
cell.editCallback = { [weak self] in
print(indexPath)
}
return cell
}
you can try this
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: false)
//your code here
}
I am working on a project that requires fairly sizable UITableViewCells (with large blocks of text in them). In iOS 9, it was not a problem if these cells exceeded a height of 2048. However, iOS 10 seems less forgiving. Whenever a cell reaches that height, the UITableView either renders incorrectly or glitches in some other way (e.g. no scrolling).
I have found this to be reproducible in a bare-bones project. First, create a UIViewController that displays a single (dynamic) UITableViewCell:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.estimatedRowHeight = 44.0
self.tableView.rowHeight = UITableViewAutomaticDimension
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ExampleCell", for: indexPath)
cell.textLabel?.text = "Content"
return cell
}
}
Then, create a UITableView with a prototype cell containing a single UIView with 5 constraints: top (0), left (0), right (0), height (2048) , bottom (0) (with a lower priority on bottom). It seems I can't post images due to lacking reputation, so an image of the UITableView setup can be found here: http://i.imgur.com/nGF8vQI.png
A side-by-side of the same setup with height 2047 vs 2048 can be seen here: http://imgur.com/a/amxqp
The UITableView with a cell of height 2048 does not actually have that height; as soon as you scroll down, the UITableView elastic bands right back up. The 2047 (or less) version works as expected.
Is this behavior intentional on Apple's part, or is something else amiss here?
This is the flow of my app:
First, the TableView is set to hidden. There is a UITextField in the center of the screen. When the user types something and hits Go, this code is run:
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5, animations: {
self.textFieldConstraint.constant = -230
self.tableView.hidden = false
self.goButton.hidden = true
self.view.layoutIfNeeded()
}, completion: nil)
At this point, the tableview is populated. When a row is selected, I need to manipulate the data that is populating it.
However, absolutely nothing happens when I tap a cell.
What am I doing wrong?
My TableView code is here:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: SearchResultsTableViewCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! SearchResultsTableViewCell
cell.label.text = searchResultsNames[indexPath.row]
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchResultsUrls.count
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("HELLO")
}
And, I have set the dataSource and delegate properly.
I also want to clarify that the tableView populates and scrolls properly; it just won't do anything when I tap a cell.
Update:
I've discovered that for some reason, I can select the cells when I press and hold them. It is not what I want, so does anybody know how to fix this?
I have just used your code to create a simple table, selection is working fine and logging out HELLO as expected. Can you check the values of Selection in the attributes inspector? Here is mine, which has Selection set to Single Selection.
And here is the code I used for my simple table
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var searchResults = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
searchResults.append("Testing 1")
searchResults.append("Testing 2")
searchResults.append("Testing 3")
searchResults.append("Testing 4")
searchResults.append("Testing 5")
tableView.dataSource = self
tableView.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("tableCell", forIndexPath: indexPath)
cell.textLabel?.text = searchResults[indexPath.row]
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchResults.count
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("HELLO")
}
}
I also tried hiding and showing the tableView which made no difference on selection.
EDIT & SOLUTION:
In the comments below, we discovered that the issue is related to a tapGestureRecogniser on the view, This was identified by the op only being able to make a selection by holding a tap on the cell. The gesture has to fail before the selection can be made, the op managed to solve the problem by referring to this other SO Answer
if you use "tap" gesture, you can't select table cell. (but, if you click and drag to right a cell, you can select it.)
Check gesture first.
And if your code has self.tableView.allowsSelection = false, replace false to true or delete this line.
My problem has been caused by the tap gesture recognizer on the view controller itself (I had BaseTableViewController I was extending from). Probably it was interfering with the gesture recognizers of UITableView.
In your viewDidLoad, or wherever you set up your view, make certain that your table view even allows selections. That can be controlled with the allowSelection property.
Something like:
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.allowsSelection = true
}
If you have a gesture recognizer, just type gestureRecognizer.cancelsTouchesInView = false
For me I was implementing another did select row method, so I erased it and typed "didSelect..." and selected the first one in the suggested methods, which is this for swift 3:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Row #: \(indexPath)")
}
Try disabling and then enabling the user interaction Enabled property in the attribute inspector of your tableView
Looks something like this
I met the same problem as you and solved it by removing the below code
self.view.layoutIfNeeded()
you can try it.