I have a table view that includes multiple prototype cells. I want to get values of textField in these cells and append an array, when tapped a button.
Cells has an object that called element. I want to append this element to elementsArray in my view controller. The problem is when cellForRow method worked this elements keys is created but because of cellForRowAt method works only one time, "value" key of element object take initial value of txtContent.
How can I take txtContent text after write a text for each cell?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let object = surveyDetailArray.first!.elements[indexPath.row]
switch object.type {
case CellConfig.email.rawValue:
let cell = tableView.dequeueReusableCell(withIdentifier: "EmailCell", for: indexPath) as! EmailCell
cell.lblTitle.text = object.title
cell.txtContent.inputAccessoryView = toolBar
cell.element["formElementId"] = object.id as AnyObject
cell.element["options"] = optionArray as AnyObject
elementsArray.append(cell.element as AnyObject)
return cell
case CellConfig.number.rawValue:
let cell = tableView.dequeueReusableCell(withIdentifier: "NumberCell", for: indexPath) as! NumberCell
cell.lblTitle.text = object.title
cell.txtContent.inputAccessoryView = toolBar
cell.txtContent.keyboardType = .numberPad
cell.element["formElementId"] = object.id as AnyObject
cell.element["options"] = optionArray as AnyObject
elementsArray.append(cell.element as AnyObject)
return cell
}
My cell class
class EmailCell: UITableViewCell,UITextFieldDelegate {
#IBOutlet weak var lblTitle: UILabel!
#IBOutlet weak var txtContent: CustomTextField!
var element = ["formElementId":"",
"value":"",
"options":[]] as [String : Any]
override func awakeFromNib() {
super.awakeFromNib()
txtContent.backgroundColor = #colorLiteral(red: 0.1570051014, green: 0.1588717997, blue: 0.2081049681, alpha: 0.1)
txtContent.layer.borderWidth = 0
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
txtContent.delegate = self
// Configure the view for the selected state
}
func textFieldDidEndEditing(_ textField: UITextField) {
element["value"] = textField.text!
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let object = surveyDetailArray.first!.elements[indexPath.row]
switch object.type {
case CellConfig.email.rawValue:
let cell = tableView.dequeueReusableCell(withIdentifier: "EmailCell", for: indexPath) as! EmailCell
cell.txtContent.delegate = self
return cell
case CellConfig.number.rawValue:
let cell = tableView.dequeueReusableCell(withIdentifier: "NumberCell", for: indexPath) as! NumberCell
return cell
}
extension ViewController: UITextFieldDelegate{
func textFieldDidEndEditing(_ textField: UITextField) {
var cell: UITableView Cell!
if let emailCell: UITableViewCell = textField.superview.superview as? EmailCell{
cell = emailCell
}
if let numbCell: UITableViewCell = textField.superview.superview as? NumberCell{
cell = numbCell
}
var table: UITableView = cell.superview as UITableView
let textFieldIndexPath = table.indexPathForCell(cell)
// HERE DO THE UPDATION YOU WANT TO DO
}
}
Related
I have a UILabel that is inside a TableView, I want to change the color of the UILabel to red on user tap. I am using a UITapGestureRecognizer and on tapping the UILabel I can get the content of the UILabel but I can't get the actual UILabel since to my knowledge you can't have parameters inside a UIGesture function.
This is my code and it will help clear things up
class HomeProfilePlacesCell: NSObject {
var Post = [String]()
#objc func PostTap(_ sender: UIGestureRecognizer) {
print(Post[(sender.view?.tag)!])
}
func HomeProfilePlaceTVC(_ tableView: UITableView, cellForRowAt indexPath: IndexPath, streamsModel : streamModel,HOMEPROFILE: HomeProfile, controller: UIViewController) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTVC", for: indexPath) as! HomeTVC
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(PostTap(_:)))
tapGesture.delegate = self as? UIGestureRecognizerDelegate
cell.post.addGestureRecognizer(tapGesture)
cell.post.text = streamsModel.Posts[indexPath.row]
cell.post.tag = indexPath.row
Post = streamsModel.Posts
return cell
}
}
My function there is PostTap whenever a user taps the UILabel which is the cell.post then I can read it's content inside PostTap but in order to change the color of that UILabel then I'll have to pass the let cell constant into the PostTap function.
Is there anyway I can do that or a work around ? I am new to Swift
Use TableView Delegates: [SWIFT 4.0]
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
let cell = tableView.cellForRowAtIndexPath(indexPath) as! <your Custom Cell>
cell.<your CustomCell label name>.textColor = UIColor.red
//OR
cell.<your Customcell label name>.backgroundColor = UIColor.green
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.None)
}
func tableView(tableView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath)
{
let cell = tableView.cellForRowAtIndexPath(indexPath) as! <your Custom Cell>
// change color back to whatever it was
cell.<your Customcell label name>.textColor = UIColor.black
//OR
cell.<your Customcell label name>.backgroundColor = UIColor.white
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.None)
}
Add tag to cell as indexPath.row
cell.tag = indexPath.row
Then
#objc func PostTap(_ sender: UIGestureRecognizer) {
let cell = self.tableVIew.cellForRow(at: sender.tag) as! HomeTVC
// Now you access your cell label here, and can do whatever you want
}
you can make it possible by using
tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: IndexPath)
when user tap on a cell this method called
in this method do this
tableView.cellForRow(at: indexPath)
this will give you cell cast it as your cell class
and now u can do anything with your label in that cell
cell.label....
To change the color of clicked index label first you need to declare on varible to identify the clicked position
var selectedCellIndex = "" // initialize as empty string
In you cellForRowAt
func HomeProfilePlaceTVC(_ tableView: UITableView, cellForRowAt indexPath: IndexPath, streamsModel : streamModel,HOMEPROFILE: HomeProfile, controller: UIViewController) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTVC", for: indexPath) as! HomeTVC
cell.post.text = streamsModel.Posts[indexPath.row]
cell.post.tag = indexPath.row
cell.post.isUserInteractionEnabled = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(PostTap(_:)))
tapGesture.delegate = self as? UIGestureRecognizerDelegate
cell.post.addGestureRecognizer(tapGesture)
Post = streamsModel.Posts
if self.selectedCellIndex == "\(indexPath.row)" {
cell.post.text = UIColor.red
} else {
cell.post.text = UIColor.blue
}
return cell
}
In your Tap function
func PostTap(_ sender:UIGestureRecognizer){
let tapView = gesture.view!
let index = tapView.tag
self. selectedCellIndex = "\(index)"
self.YOUR_TABLE_NAME.reloadData()
}
Hope this will help you
Try Closure approach in Cell:
In Custom Table View cell:
class HomeTVC: UITableViewCell {
#IBOutlet weak var labelPost: UILabel!
var callBackOnLabelTap: (()->())?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(postTap(_:)))
tapGesture.numberOfTapsRequired = 1
tapGesture.delegate = self
self.labelPost.addGestureRecognizer(tapGesture)
}
#objc func postTap(_ sender: UIGestureRecognizer) {
self.callBackOnLabelTap?()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Then in cellForRowAt indexPath :
func HomeProfilePlaceTVC(_ tableView: UITableView, cellForRowAt indexPath: IndexPath, streamsModel : streamModel,HOMEPROFILE: HomeProfile, controller: UIViewController) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTVC", for: indexPath) as! HomeTVC
cell.callBackOnLabelTap = {
cell.labelPost.backgroundColor = UIColor.black
}
return cell
}
For me, I wanted the color for the label to change when the container cell of a label is tapped.
You can select what color you want for the Label text, when tapped by selecting, Highlighted (in Attributes inspector) for Label. From drop down you can select the color you want to see when the cell was tapped.
Attributes Inspector: Highlighted Property for label
i have custom table view cell that having rating stars. i'm using https://github.com/hsousa/HCSStarRatingView for rating View.
there is my code for table view and cell view.
class RatingTableViewCell: UITableViewCell {
var value : CGFloat = 0.0
#IBOutlet weak var starRatingView: HCSStarRatingView!
#IBOutlet weak var titleLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
initStarRatingView()
starRatingView.addTarget(self, action: #selector(DidChangeValue(_:)), for: .valueChanged)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
private func initStarRatingView() {
var scalingTransform : CGAffineTransform!
scalingTransform = CGAffineTransform(scaleX: -1, y: 1);
starRatingView.transform = scalingTransform
starRatingView.emptyStarImage = #imageLiteral(resourceName: "strokStar")
starRatingView.halfStarImage = #imageLiteral(resourceName: "halfStar")
starRatingView.filledStarImage = #imageLiteral(resourceName: "fillStar")
starRatingView.allowsHalfStars = true
}
#IBAction func DidChangeValue(_ sender: HCSStarRatingView) {
self.value = sender.value
}
class RatingViewController: CustomViewController,UITableViewDelegate,UITableViewDataSource {
var values : [CGFloat] = [0.5,0.0,0.0,0.0,0.0,0.0,0.0]
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "RatingTableViewCell", for: indexPath) as! RatingTableViewCell
values[indexPath.row] = cell.value
cell.starRatingView.value = values[indexPath.row]
return cell
}
//MARK: _Table data source
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
values.count
}
}
there is a problem when i scroll table view. dequeue Reusable Cell data is wrong. how can update value data for each cell?
The problem is that you are storing the value in the cell. Take a look at those two lines:
let cell = tableView.dequeueReusableCell(withIdentifier: "RatingTableViewCell", for: indexPath) as! RatingTableViewCell
values[indexPath.row] = cell.value
You dequeue a cell and assign it's value to values[indexPath.row]. The problems that you are noticing when scrolling are caused by the fact that the reused cell was previously used for a different indexPath, which means that their value (that you assign to values[indexPath.row]) is meant for its previous indexPath.
To fix that, I would advise getting rid of the value variable in RatingTableViewCell. Instead, define a protocol RatingTableViewCellDelegate that will be used to inform the RatingViewController about the new value.
i have collectionview that contain del button and add
cell.coupon_add.tag = indexPath.row
cell.coupon_add?.layer.setValue(id, forKey: "coupon_id")
cell.coupon_add?.layer.setValue(uID, forKey: "user_id")
cell.coupon_add?.addTarget(self, action: #selector(ViewController.addItem(_:)), forControlEvents: UIControlEvents.TouchUpInside)
func addItem(sender:UIButton) {
let point : CGPoint = sender.convertPoint(CGPointZero, toView:collectionview)
let indexPath = collectionview!.indexPathForItemAtPoint(point)
let cell = collectionview.dequeueReusableCellWithReuseIdentifier("listcell", forIndexPath: indexPath!) as! ListCell
let coupon_id : String = (sender.layer.valueForKey("coupon_id")) as! String
let user_id : String = (sender.layer.valueForKey("user_id")) as! String
if user_id == "empty" {
self.login()
}else{
print("adding item**",indexPath)
cell.coupon_add.hidden = true
cell.coupon_del.hidden = true
let buttonRow = sender.tag
print(buttonRow)
}
}
i want to hide the add button when trigger. i just get the value of the indexPath but i dont know how to hide it without refresh the collectionview
Create a custom cell
class CustomCell: UICollectionViewCell {
#IBOutlet weak var label: UILabel!
#IBOutlet weak var delButton: UIButton!
#IBOutlet weak var addButton: UIButton!
#IBAction func addTapped(sender: AnyObject) {
delButton.removeFromSuperview()
addButton.removeFromSuperview()
}
}
Typical CollectionView Controller
class ViewController: UICollectionViewController {
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10;
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! CustomCell
cell.label.text = "Cell \(indexPath.row)"
return cell
}
}
And your button will gone, when you hit them
I put stepper both outlets and action into tableview cell and using protocol delegate to connect it to tableview. When i tapped stepper in first row, stepper value appear normaly in first row but its also appear in some random row. how to fix this?
TableViewCell
protocol ReviewCellDelegate{
func stepperButton(sender: ReviewTableViewCell)
}
class ReviewTableViewCell: UITableViewCell {
#IBOutlet weak var countStepper: UIStepper!
#IBOutlet weak var stepperLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
#IBAction func stepperButtonTapped(sender: UIStepper) {
if delegate != nil {
delegate?.stepperButton(self)
stepperLabel.text = "x \(Int(countStepper.value))"
}
}
ViewController
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "reviewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! ReviewTableViewCell
var imageView: UIImageView?
let photoG = self.photos[indexPath.row]
imageView = cell.contentView.viewWithTag(1) as? UIImageView
//let layout = cell.goodiesImage
let tag = indexPath.row // +1
cell.tag = tag
photoG.fetchImageWithSize(CGSize(width: 1000, height: 1000), completeBlock: { image, info in
if cell.tag == tag {
imageView?.image = image
cell.goodiesImage.image = image
}
})
func stepperButton(sender: ReviewTableViewCell) {
if let indexPath = tableView.indexPathForCell(sender){
print(indexPath)
}
}
Reset the value of stepper while loading your cell. you can reset the cell property values in cell's prepareForReuse method. add the following method in your ReviewTableViewCell class.
override func prepareForReuse()
{
super.prepareForReuse()
countStepper.value = 0.0
}
In tableViewCell VC:
1 - add these field
var cellDelegate: cellProtocol?
var index: IndexPath?
2 - then add this in the delegate:
func onStepperClick(index: Int, sender: UIStepper)
3 - when you have dragged your stepper over as an action use this:
#IBAction func cellStepper(_ sender: UIStepper) {
cellDelegate?.onStepperClick(index: (index?.row)!, sender: sender)
sender.maximumValue = 1 //for incrementing
sender.minimumValue = -1 //for decrementing
//this will make sense later
}
In ViewController
1 - add these to the tableView function that has the cellAtRow variable.
cell.cellDelegate = self
cell.index = indexPath
2 - Use this instead of your stepperButton function
func onStepperClick(index: Int, sender: UIStepper) {
print(index)
if sender.value == 1.0{
//positive side of stepper was pressed
}else if sender.value == -1.0{
//negative side of stepper was pressed
}
sender.value = 0 //resetting to zero so sender.value produce different values on plus and minus
}
Hope this works for you
As mentioned by #A-Live, your component is being reused and so need to be updated.
So in your view controller:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "reviewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! ReviewTableViewCell
var imageView: UIImageView?
let photoG = self.photos[indexPath.row]
imageView = cell.contentView.viewWithTag(1) as? UIImageView
//let layout = cell.goodiesImage
let tag = indexPath.row // +1
cell.tag = tag
photoG.fetchImageWithSize(CGSize(width: 1000, height: 1000), completeBlock: { image, info in
if cell.tag == tag {
imageView?.image = image
cell.goodiesImage.image = image
}
})
cell.countStepper.value = XXX[indexPath.row].value; //Here you update your view
cell.stepperLabel.text = "x \(Int(cell.countStepper.value))" //And here
And
func stepperButton(sender: ReviewTableViewCell) {
if let indexPath = tableView.indexPathForCell(sender){
print(indexPath)
XXX[sender.tag].value = sender.counterStepper.value //Here you save your updated value
}
NOTE:
1.MY Cell class is just normal..All changes are in viewcontroller class
2.I have taken stepper and over it added ibAddButton with same constraint as ibStepper
class cell: UITableViewCell {
#IBOutlet weak var ibAddButton: UIButton!
#IBOutlet weak var ibStepper: UIStepper!
#IBOutlet weak var ibCount: UILabel!
#IBOutlet weak var ibLbl: UILabel!
}
1.define empty int array [Int]()
var countArray = [Int]()
2.append countArray with all zeros with the number of data u want to populate in tableview
for arr in self.responseArray{
self.countArray.append(0)
}
3.in cell for row at
func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! cell
let dict = responseArray[indexPath.row] as? NSDictionary ?? NSDictionary()
cell.ibLbl.text = dict["name"] as? String ?? String()
if countArray[indexPath.row] == 0{
cell.ibAddButton.tag = indexPath.row
cell.ibStepper.isHidden = true
cell.ibAddButton.isHidden = false
cell.ibCount.isHidden = true
cell.ibAddButton.addTarget(self, action: #selector(addPressed(sender:)), for: .touchUpInside)
}else{
cell.ibAddButton.isHidden = true
cell.ibStepper.isHidden = false
cell.ibStepper.tag = indexPath.row
cell.ibCount.isHidden = false
cell.ibCount.text = "\(countArray[indexPath.row])"
cell.ibStepper.addTarget(self, action: #selector(stepperValueChanged(sender:)), for: .valueChanged)}
return cell
}
4.objc functions
#objc func stepperValueChanged(sender : UIStepper){
if sender.stepValue != 0{
countArray[sender.tag] = Int(sender.value)
}
ibTableView.reloadData()
}
#objc func addPressed(sender : UIButton){
countArray[sender.tag] = 1//countArray[sender.tag] + 1
ibTableView.reloadData()
}
I've been trying to use the custom cell in function when button is clicked. and update a label in that specific row using the function.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = myTableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CardCell
cell.startcount.tag = indexPath.row
cell.startcount.addTarget(self, action: "startcount:", forControlEvents:UIControlEvents.TouchUpInside)
}
here is the function I'm using
func startcount(sender: AnyObject){
// create a reference of CardCell
// update the counter like cell.textcount.text = "\(counter)"
// in the specific row
}
My custom cell class :
import UIKit
class CardCell: UITableViewCell {
#IBOutlet weak var textcount: UILabel!
#IBOutlet weak var startcount: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
func startcount(sender: AnyObject){
let button = sender as UIButton
let indexPath = NSIndexPath(forRow:button.tag inSection:0)
let cell = tableView.cellForRowAtIndexPath(indexPath) as CardCell
// update cell
}
For Swift 4 and above
func startCount(sender : AnyObject) {
let button = sender as! UIButton
let indexPath = IndexPath(row:button.tag ,section:0)
let cell = tableView.cellForRow(at: indexPath) as! CardCell
// update cell
}