I have a UITableView that uses custom UITableViewCell. cell contains a UITextField. like in image below.
each TextField is configured in the cellForRowAtIndexPath method. eg. textContentType, keyboardType etc.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ReuseCellIdentifier", for: indexPath) as! FormTableViewCell
let inputItem = inputItems[indexPath.row]
cell.titleLabel.text = inputItem.title
cell.inputTextField.placeholder = inputItem.placeholder
cell.inputTextField.textContentType = inputItem.contentType
cell.inputTextField.keyboardType = inputItem.keyboardType
cell.inputTextField.isSecureTextEntry = inputItem.isSecure
return cell
}
However when editing last TextField(Re-Password) it shows email addresses as autofill suggestions although the textContentType is set to newPassword.
if tapped on one of the suggestions, the email(3rd textField) is populated with the email. not the textField that was being edited. also 4th textfield(password) becomes first responder.
weirdly, if I remove this line, the problem goes away
cell.inputTextField.isSecureTextEntry = inputItem.isSecure
InputItem class,
class InputItem:Mappable {
var title:String?
var placeholder:String?
var contentType:UITextContentType?
var keyboardType:UIKeyboardType?
var isSecure:Bool = false
}
// you can set autocorrectionType to turn on/off suggestions
// UITextAutocorrectionType can be .no or .yes
cell.inputTextField.autocorrectionType = inputItem.autocorrectionType // set .no to turn off suggestions
Related
timer.gif
↑↑↑↑↑↑↑↑↑ I show the problem in this GIF ↑↑↑↑↑
There is a timer and stratButton in the tableView cell, when I click the Button, the timeLabel will start running.
Now the problem is when I scroll the running timer out of the screen and scroll it back, the timer will reset and stop.
Hope someone can help me! I check many solution but they didn't work for me! I am almost cry.
my cellForRow:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! TimerTableViewCell
cell.timer?.invalidate()
let item = myTimerList[indexPath.row]
cell.timerName.text = item.timerName
cell.secondLeftLabel.text = "\(item.timerSecond)"
return cell
}
I created a small project contained my code for you to modify : https://app.box.com/s/axluqkjg0f7zjyigdmx1lau0c9m3ka47
You need to preserve the state of each cell
class Service {
static let shared = Service()
var myTimerList = [TimerClass]()
}
//
here i added another 2 vars , why timerName is left despite you have to init them the same because current will hold the changing value
class TimerClass {
let timerSecond : Int
let timerName : String
var current: Int
var isPlaying = false
init(second:Int, name:String) {
timerSecond = second
timerName = name
current = second
}
}
//
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! TimerTableViewCell
let item = Service.shared.myTimerList[indexPath.row]
cell.tag = indexPath.row // to access the array inside the cell
if item.isPlaying {
cell.play() // add play method to the cell it has same button play action
}
else {
cell.timer?.invalidate()
}
cell.timerName.text = item.timerName
cell.secondLeftLabel.text = "\(item.current)"
return cell
}
//
inside the cell when the button is clicked , change isPlaying property to true and to false when stopped like this
// here self is the cell itself
Service.shared.myTimerList[self.tag].isPlaying = true
also when the timer ticks change
Service.shared.myTimerList[self.tag].current = // value
You are reusing UITableViewCell. It means once cell is out of screen, it will be reused for the cell getting into the screen. Whenever it happens, it calls the delegate method - means cell.timer?.invalidate() is invoked again and again.
Remove cell.timer?.invalidate() from cellForRowAt method, because every time you scroll the tableView this method is called to display new cells. In your case when you have started a timer for a cell and that cell is not in the view, next time you scroll to bring that cell again to the view cell.timer?.invalidate() causes it to stop.
I am having some trouble with the dequeue reusable cell function in my UITableView. The tableview has a couple of cells, each of which contains a button.
When I scroll, the cell is recreated, and new buttons begin to overlap old buttons (until I have a bunch of the same buttons in the same cell). I've heard that your supposed to use the removeFromSuperview function to fix this, but i'm not really sure how to do it.
Here is a picture of my app:
And here is the cellForRowAtIndexPath (where the problem is occurring)
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath)
let nameLabel: UILabel = {
let label = UILabel()
label.text = "Sample Item"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let actionButton = YSSegmentedControl(
frame: CGRect.zero,
titles: [
"No",
"Yes"
])
The reason you are seeing multiple buttons appear is because the cellForRowAtIndexPath: method is called every time a new table cell is needed. Since you are likely creating the button in that method body, it is getting recreated every time the cell is reused and you'll see them stack on top like that. The correct way to use dequeueReusableCell: with custom elements is to create a subclass of a UITableViewCell and set that as the class of your table cell in your storyboard. Then when you call dequeueReusableCell: you'll get a copy of your subclass that will contain all of your custom code. You will need to do a type conversion to get access to any of that custom code like this:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
if let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as? MyCustomCellClass {
cell.nameLabel.text = "Sample item"
}
// This return path should never get hit and is here only as a typecast failure fallback
return tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath);
}
Your custom cell subclass would then look something like this:
class MyCustomCellClass: UITableViewCell {
#IBOutlet var nameLabel: UILabel!
#IBOutlet var actionButton: UIButton!
#IBAction func actionButtonPressed(_ sender: UIButton) {
//Do something when this action button is pressed
}
}
You can add new label/button in cellForRowAtIndexPath, but you need to make sure there is no existing label/button before you create and add new ones. One way to do it is to set a tag to the label/button, and before you generate new label/button, check if the view with the tag is already in the cell.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
if let label = cell.viewWithTag(111) as? UILabel
{
label.text = "Second Labels"
}
else{
let label = UILabel()
label.tag = 111
label.text = "First Labels"
cell.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.frame = CGRect(x:0, y:10, width: 100, height:30)
}
return cell
}
This is my requirement:
I want my tableView's cell to be like the last cell, its border is margin the tableView some pix, not contradict the tableview's edge.(I want this is because when I click down the cell, there is gray effect on the cell)
How to do with that?
u can't resize the cell's, instead u can set the views's layer properties to achieve the similar effect, for example, (u are not mentioning which language u are using, i assume u are using swift).
i will assume your custom cell contains a UIView and some other view components, like below,
and also add outlet for imageHolderView in the above image,
out let name will be holderView as shown in below image,
in the custom cell class, define two methods for selection management, and your custom cell class would look like below,
class CustomCell: UITableViewCell {
#IBOutlet weak var circleNameTextField: UILabel!
#IBOutlet weak var holderView: UIView!
var cellindexPath:IndexPath?
var selectedIndexPath:IndexPath?
func selectTheCell() {
if self.selectedIndexPath?.row == self.cellindexPath?.row {
self.holderView.layer.cornerRadius = 6.0
self.holderView.layer.masksToBounds = true
self.holderView.layer.borderWidth = 4.0
self.holderView.layer.borderColor = UIColor.red.cgColor
self.backgroundColor = UIColor.gray
} else {
self.resetCellWith(animate: false)
}
}
func resetCellWith(animate:Bool) {
self.holderView.layer.cornerRadius = 0.0
self.holderView.layer.masksToBounds = false
self.holderView.layer.borderWidth = 0.0
self.holderView.layer.borderColor = UIColor.clear.cgColor
self.backgroundColor = UIColor.orange
}
}
now all u have to do is call the above methods, from controller and update the cell behaviour, for example,
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.selIndexPath = indexPath
self.aTableView.reloadSections(IndexSet(integer: 0), with: .none)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : CustomCell? = tableView.dequeueReusableCell(withIdentifier: "CUSTOM_CELL", for: indexPath) as? CustomCell//tableView.dequeueReusableCell(withIdentifier: "CUSTOM_CELL") as? CustomCell
cell?.cellindexPath = indexPath
if let selectedIndexPath = self.selIndexPath {
cell?.selectedIndexPath = selectedIndexPath
cell?.selectTheCell()
} else {
cell?.resetCellWith(animate:false)
}
cell?.selectionStyle = .none
return cell!
}
with the above arrangement, u can get the table cell and selection like below,
NOTE: well, above is one way achieve this effect. and method names i simply used the sample project that i created for different purpose. :)
What property can I access to find out?
I was learning some swift from this tutorial, and I decided to give myself some problems to do. One was that in the tutorial, to edit a name, there is a UIAlert. I want to get rid of that and instead, have UITextField in place of the UILabel, so the user could simply tap on a name, and edit it. I would use the textFieldDidEndEditing(textField: UITextField) function to update the model, which is a dictionary of names and picture filenames.
I set the view controller as the UITextFieldDelegate, I put in the function, but now I'm stuck, because although the text was updated just fine in one of the cells, I don't know how to tell which cell it happened in.
In this case, you probably can consider to subclass a UITextField to refer a Dictionary item.
class DictionaryTextField: UITextField {
var item: [String : AnyObject]?
}
Also create a subclass of UITableViewCell to hold above DictionaryTextField as an IBOutlet property.
class TextFieldTableViewCell: UITableViewCell {
#IBOutlet weak var textField: DictionaryTextField!
}
After finished above setting, an item Dictionary of datasource can be set in func tableView(:, cellForRowAtIndexPath: ).
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CellIdentifier", forIndexPath: indexPath) as! TextFieldTableViewCell
cell.textField.delegate = self
let item = items[indexPath.row]
cell.textField.item = item
cell.textField.text = "TextField \(item["name"]!)"
return cell
}
Later, in the UITextFiedDelegate, cast the textField as DictionaryTextField. Then the item can be retrieved directly.
func textFieldDidBeginEditing(textField: UITextField) {
guard let textField = textField as? DictionaryTextField else {
return
}
print("Did begin editing: \(textField.item)")
}
The revised codes can be downloaded again with this link: https://db.tt/8j9ENf7b
I'm having a problem with my TableViewCell
I have two type of cell in my storyboard.
when i scroll, the text overlaps in some cells. I Try everything but I do not know how else to do. thank you very much for the help
public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var storeNew = systemsBlog.getStore(listNews[indexPath.row].getIdStore())
var newNotice = listNews[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("TimelineCell", forIndexPath: indexPath) as? TimelineCell
cell!.nameLabel.text = storeNew.getName()
cell!.postLabel?.text = newNotice.getText()
cell!.postLabel?.numberOfLines = 0
cell!.dateLabel.text = newNotice.getDate()
cell!.typeImageView?.tag = indexPath.row;
return cell!
}
class TimelineCell : UITableViewCell {
#IBOutlet var nameLabel : UILabel!
#IBOutlet var postLabel : UILabel?
#IBOutlet var dateLabel : UILabel!
override func awakeFromNib() {
postLabel?.font = UIFont(name: "Roboto-Thin", size: 14)
}
override func layoutSubviews() {
super.layoutSubviews()
}
I can fix the problem. In the storyboard, the label have unchacked "Clears Graphics Context". I checked and for now it solved! Thanks for the help!
I had a similar issue with one of my UITableViews in the past. There are a bunch of things that could cause this, but maybe it is the same thing that happened to me.
I see that you are using a custom tableViewCell. What could be happening is when you set the text of the cell, it adds a label view with that text. Now say you scroll through the tableview and that cell disappears. If you were to reuse that cell and you did not remove the label from the subview, or set the text of that label again to the desired text, you will be reusing the tableviewcell with a previous label on it and adding a new label with new text to it, overlapping the text.
My suggestion would be to make sure you do not keep adding UIlabels as subviews in the TimelineCell class unless no label exists. if a label exists edit the text of that label not of the cell.
As per apple documentation:
The table view’s data source implementation of
tableView:cellForRowAtIndexPath: should always reset all content when
reusing a cell.
It seems that your problem is that you not always setting postLabel, causing it to write on top of the other cells, try this:
//reuse postLabel and set to blank it no value is returned by the function
let cell = tableView.dequeueReusableCellWithIdentifier("TimelineCell", forIndexPath: indexPath) as? TimelineCell
cell!.nameLabel.text = storeNew.getName()
if newNotice.getText() != nil{
cell!.postLabel.text = newNotice.getText()
} else {cell!.postLabel.text = ''}
cell!.postLabel.numberOfLines = 0
cell!.dateLabel.text = newNotice.getDate()
cell!.typeImageView?.tag = indexPath.row;
return cell!
}
//Make postLabel mandatory and set the font details in the xcode
class TimelineCell : UITableViewCell {
#IBOutlet var nameLabel : UILabel!
#IBOutlet var postLabel : UILabel!
#IBOutlet var dateLabel : UILabel!
override func awakeFromNib() {
//set this in xcode
}
override func layoutSubviews() {
super.layoutSubviews()
}
Also be sure that you are not creating any UI element and appending to the cell, as if you are you need to dispose it before you recycle the cell.
You can try setting:
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 44.0 // Set this value as a good estimation according to your cells
}
In the View Controller containing your tableView.
Make sure the layout constraints in your TimelineCell define a clear line of height constraints
Another option is responding to:
tableView(_ tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 44.0 // Your height depending on the indexPath
}
Always in the ViewController that contains your tableView and, I assume, is the tableView's UITableViewDelegate
Hope this helps
Set cell to nil that will fix some error.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
var cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as? ImageCellTableViewCell
cell = nil
if cell == nil {
cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for:indexPath) as? ImageCellTableViewCell
}
cell.yourcoustomtextTextLabel.text = "this is text"
cell.yourcoustomtextImageView.image = image
return cell
}