How to properly add UITextField to UITableViewCell - ios

What I'm trying to do should be pretty straight forward from my understanding, yet I seem to be having some trouble with this.
I'm trying to add UITextField to a UITableViewCell in my Storyboard, however it only lets me place the text field as the child of a UIView, with the latter placed in the table view cell.
ie:
UITableViewCell
|__UIView
|__UITextView
I create my IBOutlet in my Swift ViewController and add the following function to have the keyboard appear on touch
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.nameTextField.becomeFirstResponder()
}
When I run this though, the cell responds to touch however it's as if the text view is always hidden and never gets enabled or becomeFirstResponder. I'm presuming it has to do with the UIView in the way, however I've been unsuccessful in placing the text view directly inside the table view cell.
If anyone has an idea on what I'm doing wrong, I'd greatly appreciate the help,
Thank you!

Create a custom UITableView cell as a xib and make a corresponding .swift file for this. Add a textField to the cell and create an IBOutlet for the textField in the custom .swift class. Make sure your constraints are set in this xib.
In your ViewController .swift class, make the func tableView(cellForRowAtIndexPath) return your custom cell.
You do not have to set the firstResponder because the keyboard will come up by default. Unless of your cell is basically just a textField, it doesn't seem wise to have the didSelectRowAtIndexPath to always bring up the keyboard. If, for some reason that the cell always takes the touch action, then use didSelectRowAtIndexPath, but use
tableView.cellForRowAtIndexPath(indexPath).textField.becomeFirstResponder()
Set textField delegate to the custom cell and add this in the CustomTableViewCell.swift to dismiss the keyboard upon press of return key:
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return false
}
Side note: The keyboard will not show in the simulator unless you deselected the option to use the hardware keyboard.
Here is a sample project with what I think you are trying to achieve:
https://mega.nz/#!B1ASDIQT!8-diyv_Fd2H66nb08gs3ObzlQqVHKUUIs4U_BbI6s4s

Related

StoryBoard actions cannot be targeted at repeating content [duplicate]

I have just created an app and have started hooking up #IBOutlet's to the storyboard. I am connecting some of them to labels in a UITableViewCell Prototype Cell with a Basic Style. When I connect it though I get this error in the Storyboard:
The detailText Outlet from the TableViewController to the UILabel is invalid. Outlets cannot be connected to repeating content.
Can someone help me out? I have set it up the way I always do successfully but this time it has chucked me this error.
Create a table view cell subclass and set it as the class of the prototype. Add the outlets to that class and connect them. Now when you configure the cell you can access the outlets.
There are two types of table views cells provided to you through the storyboard, they are Dynamic Prototypes and Static Cells
1. Dynamic Prototypes
From the name, this type of cell is generated dynamically. They are controlled through your code, not the storyboard. With help of table view's delegate and data source, you can specify the number of cells, heights of cells, prototype of cells programmatically.
When you drag a cell to your table view, you are declaring a prototype of cells. You can then create any amount of cells base on this prototype and add them to the table view through cellForRow method, programmatically. The advantage of this is that you only need to define 1 prototype instead of creating each and every cell with all views added to them by yourself (See static cell).
So in this case, you cannot connect UI elements on cell prototype to your view controller. You will have only one view controller object initiated, but you may have many cell objects initiated and added to your table view. It doesn't make sense to connect cell prototype to view controller because you cannot control multiple cells with one view controller connection. And you will get an error if you do so.
To fix this problem, you need to connect your prototype label to a UITableViewCell object. A UITableViewCell is also a prototype of cells and you can initiate as many cell objects as you want, each of them is then connected to a view that is generated from your storyboard table cell prototype.
Finally, in your cellForRow method, create the custom cell from the UITableViewCell class, and do fun stuff with the label
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "yourCellIdentifier") as! YourCell
cell.label.text = "it works!"
return cell
}
2. Static Cells
On the other hand, static cells are indeed configured though storyboard. You have to drag UI elements to each and every cell to create them. You will be controlling cell numbers, heights, etc from the storyboard. In this case, you will see a table view that is exactly the same from your phone compared with what you created from the storyboard. Static cells are more often used for setting page, which the cells do not change a lot.
To control UI elements for a static cell, you will indeed need to connect them directly to your view controller, and set them up.
If you're using a table view to display Settings and other options (like the built-in Settings app does), then you can set your Table View Content to Static Cells under the Attributes Inspector. Also, to do this, you must embedded your Table View in a UITableViewController instance.
Or you don't have to use IBOutlet to refer to the object in the view. You can give the Label in the tableViewCell a Tag value, for example set the Tag to 123 (this can be done by the attributes inspector). Then you can access the label by
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "someID", for: indexPath)
let label = cell.viewWithTag(123) as! UILabel //refer the label by Tag
switch indexPath.row {
case 0:
label.text = "Hello World!"
default:
label.text = "Default"
}
return cell
}
With me I have a UIViewcontroller, and into it I have a tableview with a custom cell on it. I map my outlet of UILabel into UItableviewcell to the UIViewController then got the error.
As most people have pointed out that subclassing UITableViewCell solves this issue.
But the reason this not allowed because the prototype cell(UITableViewCell) is defined by Apple and you cannot add any of your own outlets to it.
Sometimes Xcode could not control over correctly cell outlet connection.
Somehow my current cell’s label/button has connected another cell
I just remove those and error goes away.
For collectionView :
solution:
From viewcontroller, kindly remove the IBoutlet of colllectionviewcell
. the issue mentions the invalid of your IBOutlet. so remove all subclass which has multi-outlet(invalids) and reconnect it.
The answer is already mentioned in another question for collectionviewcell
Click on simulator ,
Navigate to Window and enable Device Bezels

How to do selection in custom UITableViewCell

When I use base cell (UITabelViewCell) with disclosure indicator and enabled selection, selection occurs by click anywhere in row.
But when I use custom class for cell with UITextField selection occurs only by click outside UITextField. How can I fix it?
you can set the userInteractionEnabled = NO for textfield.
but if you do this, the textfield won't be selected anymore.
When you use a custom cell which has UITextField embedded inside it, the click on UITextfield will call the UITextField Delegate method not any UITableView delegate method. If you need to select the Custom cell you have to write the code explicitly inside the UITextField delegate method. In order to identify which cell has the UITextField you can use tag property of UITextField which can mention the cell identifier.

Locate a specific content view in a UITableViewController using viewWithTag function

I need to locate a specific content view in a table view.
This table view is driven by a UITableViewController that is used to show my user with some parameters he can change in my app.
For one of those cell, I need to programatically add a UITextView that I had subclass (NSTextStorage) to change text attribute, have rich text editing and so.
So I need to find that specific cell (content view) to by able to call my .addSubview. I thought this you be as simple as adding a specific tag to this view and call the .viewWithTag later in my viewDidLoad. The problem is a got a nil!?!
Here's the little code snippet and a capture of my table hierarchy
override func viewDidLoad() {
super.viewDidLoad()
let specific = self.view.viewWithTag(99) //--> Return nil
let specific2 = self.tableView.viewWithTag(99) //--> Also nil
What I'm doing wrong or did I miss something? This's my first shot at this method (viewWithTag) and maybe there's something else I need to do?
Thx!
You can't call this on viewDidLoad, since you are using an UITableView, all those Cells will be only loaded AFTER viewDidLoad on the method tableView:cellForRowAtIndexPath:.
So if you want to insert anything on a Cell, you must use tableView:cellForRowAtIndexPath: to do that.
I'm not sure if viewWithTag works with TableView, but you shouldn't do that. To get a View for a specific Cell (after it being loaded on the method I said above) you can call the method below:
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[[NSIndexPath indexPathForRow:0 inSection:0]];
After a lot of search, I decided to go with a Container View.
So I place UIContainerView control inside my cell. This create a embed segue to a custom view.
In this view a can override the viewDidLoad and load my custom UITextView Control...
class CustomView: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// ... code of my super class UITextView with my custom NSTextStorage
self.view.addSubview(specialTextBox)
}
So now it works! :-)

Simulate didSelectRowAtIndexPath with a UIButton in a customized UITableViewCell

I have a UIButton in a customized UITableViewCell, which subclassing UITableViewCell.
The UIButton is IBOutlet-ed in IB, and IBAction-ed to a function:
- (IBAction)clickRead:(id)sender;
Now, when I click on the button, the button has no response, since the function has nothing inside. What should I write in the function so that the UIButton can simulate tableView's didSelectRowAtIndexPath function ( i.e. select the table cell ) ?
This answer might help: hitting a next button for a UITableViewcell
You can call UITableView selectRowAtIndexPath:animated:scrollPosition: to visually select the row, but you will still have to manage any events for yourself: loading another view, calling didSelectRowAtIndexPath:, storyboard segues, etc.

UITableView didSelectRowAtIndexPath: not being called on first tap

I'm having an issue with UITableView's didSelectRowAtIndexPath.
My table is setup so that when I select row it initializes a new view controller and pushes it.
The first time I tap any row in the table, the method does not get called. Once I select another row, it begins to work as normal.
I have verified this by setting a breakpoint on didSelectRowAtIndexPath. When adding an NSLog to the method I see that when I select the second row that finally pushes the new view controller, I see two log statements appear in the console at the same time.
Any suggestions?
Any chance you accidentally typed didDeselectRowAtIndexPath?
Also check the selection property of your table view in xib file. Use 'Single Selection' or 'Multiple Selection' as required.
I experienced the following issue:
first tap in row -> no effect, no selection, never
second tap and following -> correct selection behavior, always
In my case, my error was checking Show Selection on Touch in Interface Builder. You can uncheck it in IB here:
Hope that helps someone
Check If you have set any Gesture recognisers in your class. Removing gesture worked for me.
I debated even posting this answer because I think the stars kind of aligned in order for this to manifest itself.
I am having a variation of this problem and have checked the other solutions. On my table view it isn't processing the very last row of my table on the first tap. It highlights it, but didSelectRowAtIndexPath isn't being called. All the other rows work fine. But if I turn the tableview bounce on then it seems to solve the problem (but then you have to deal with a tableview bounce).
UITableViewCellSelectionStyleNone was set for the cell displaying that problem
(ios9).
I have ended up calling
[tableView deselectRowAtIndexPath:indexPath animated:NO];
first thing in
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
which is not the same, but is good enough.
makes me wonder what kind of table breakage ios10 brings.
this issue happens also when you are working with gesture recogniser within a tableView in this case you don't need to remove them, you need only to make sure that your gesture property cancelsTouchesInView = false this is a boolean value affecting whether touches are delivered to a view when a gesture is recognised.
SWIFT 2
Make sure you have this set to true:
self.tableView.allowsSelection = true
Put this above your right after your viewDidLoad() and before the super.viewDidLoad()
SWIFT 3
If you are working with Swift 3 in a class which isn't a UITableViewController and you are using UITableViewDelegate, then you may need to use the method title:
#objc(tableView:didSelectRowAtIndexPath:) func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){...}
This worked for me, although I have just migrated my project to Swift 3. It's fairly new so this may be fixed later.
SWIFT 3
None of the other answers worked in my case, what fixed it for me was:
tableView.delaysContentTouches = false
If you have set any UITapGestureRecognizer in your class, you can add this line in your didSelectRowAtIndexPath:
[tableView deselectRowAtIndexPath:indexPath animated:NO];
This worked for me.
Swift 4.2
Disabling "Delay Touch Down" in the attributes inspector solved the issue.
After that the clicks are smooth and didSelectRowAt fires immediately.
didSelectRowAt function was not called in my app in first or second tap...
I was trying to solve the problem could not find any solution. But suddenly I was recognise, I was using view.animation color change... Delegate method was not called while animation persist
In my case I had a UITableView section header with a gesture recognizer. When the header is tapped, it should insert few rows into that section and animate the insertion (like expand/collapse section).
First time when expanded and tapped, the didSelectRow delegate method was not fired. For further taps and expand/collapse actions, it was working as expected.
Per answer by #Ayoub Nouri I set cancelsTouchesInView to false, and this resolved the issue.
tapGestureRecognizer?.cancelsTouchesInView = false
What worked for me was to start typing "didSelect..." and then let autocorrect fill in the rest. Apparently some detail of my syntax was wonky. As others have said, Use the IDE!!
I had the same issue on my tableView(swift 4.2), it could be fixed with allowsMultipleSelection property on tableView.
If allowsMultipleSelection is set to true the table view selection mechanism will change in a way that by selecting each cell the tableView didSelectRowAtIndexPath: is called for the first time and by selecting the same cell for the second time the tableView didDeselectRowAtIndexPath: is called.
It means that if the number of times a cell tapped are odd (1, 3, 5, ...) then always tableView didSelectRowAtIndexPath: will be called and if the number of times a cell tapped are even (2, 4, 6, ...) then always tableView didDeselectRowAtIndexPath: will be called.
This makes tableView didSelectRowAtIndexPath: function to be called on the third selection for the same cell and so the result is double tap for calling didSelectRowAtIndexPath:!!!
If you want the tableView didSelectRowAtIndexPath: to be called on each selection for a cell then the tableView multiple selection has to be set false, tableView.allowsMultipleSelection = false.
By doing this, every time the cell is tapped tableView didSelectRowAtIndexPath: will be called on table view and by selecting another cell tableView didDeselectRowAtIndexPath: will be called for the cell was selected before and then the tableView didSelectRowAtIndexPath: will be called for the newly selected cell.
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.allowsMultipleSelection = false
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("This will be called for each cell tap")
}

Resources