tableview button creation dynamically in cell issue in swift - ios

I am trying to implement dynamic button on tableview cell .i am able to add the button but as per the requirement on last cell i don't want to add button but its getting added there also as below mention screen capture
This is the requirement
my cellForRowAt indexPath code is below.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
if let _ = cell {
print("cell is available")
} else {
cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
}
cell?.textLabel?.textAlignment = .left
cell?.textLabel?.text = list[indexPath.row]
let btn = UIButton(type: UIButtonType.custom) as UIButton
if cell?.textLabel?.text == "➕ Add Room" || list[indexPath.row] == "➕ Add Room"{
btn.isHidden = true
cell?.textLabel?.textAlignment = .center
}else{
btn.frame = CGRect(x: 146, y: 0, width: 20, height: (cell?.frame.height)!)
btn.addTarget(self, action: #selector(buttonPressed(sender: )), for: .touchUpInside)
btn.tag = indexPath.row
btn.setImage(UIImage(named: "delete.png"), for: .normal)
cellx = list[btn.tag]
cell?.contentView.addSubview(btn)
}
return cell!
}
and the result after this show below screen capture
i don't want to display the delete button in +Add Room row which is present in last row of table.
Please suggest me how to get the out as mention in the requirement . i made a condition for the text ("Add Room Text") to shown in centre and successfully displaying in center.
Thanks in advance please help me to solve this issue
2nd issue
in this given list if i select "COPENHAGEN " and try to delete this , its deleting the upper content means BARCELONA file gets deleted. i am not delete same seleted content from list .

It happens because UITableView reuses previously created cells. So for the last cell it takes already created cell with button inside.
It would be better to create your own subclass of UITableViewCell and use it.
But if you don't want to do that you can use this version of your code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
if let cell = cell {
print("cell is available")
// Remove previously created button from reused cell view
if let button = cell.contentView.subviews.first(where: { (view: UIView) -> Bool in
view.isKind(of: UIButton.self)
}) {
button.removeFromSuperview()
}
} else {
cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
}
cell?.textLabel?.textAlignment = .left
cell?.textLabel?.text = list[indexPath.row]
if indexPath.row == (list.count - 1) {
cell?.textLabel?.textAlignment = .center
} else {
let btn = UIButton(type: UIButtonType.custom) as UIButton
btn.frame = CGRect(x: 146, y: 0, width: 20, height: (cell?.frame.height)!)
btn.addTarget(self, action: #selector(buttonPressed(sender: )), for: .touchUpInside)
btn.tag = indexPath.row
btn.setImage(UIImage(named: "delete.png"), for: .normal)
cellx = list[btn.tag]
cell?.contentView.addSubview(btn)
}
return cell!
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
if let _ = cell {
print("cell is available")
} else {
cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
}
cell?.textLabel?.textAlignment = .left
cell?.textLabel?.text = list[indexPath.row]
let btn = UIButton(type: UIButtonType.custom) as UIButton
if cell?.textLabel?.text == "➕ Add Room" || list[indexPath.row] == "➕ Add Room"{
for view in cell?.contentView.subviews as [UIView] {
if let button = view as? UIButton {
button.isHidden = true
}
}
cell?.textLabel?.textAlignment = .center
}else{
btn.frame = CGRect(x: 146, y: 0, width: 20, height: (cell?.frame.height)!)
btn.addTarget(self, action: #selector(buttonPressed(sender: )), for: .touchUpInside)
btn.tag = indexPath.row
btn.setImage(UIImage(named: "delete.png"), for: .normal)
cellx = list[btn.tag]
cell?.contentView.addSubview(btn)
}
return cell!
}
Try this. It is because of your program flow. btn instance is created for every table rows and you set hidden just the instance of the btn for last row. Table cell reused dequeue cell even though you did not add created btn into table cell content view for the latest cell. So, you need to search the button from the content view and set hidden.

Try this way
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! cell
if indexPath.row == (list.count - 1) {
btn.isHidden = true
cell?.textLabel?.textAlignment = .center
}else{
btn.isHidden = false
btn.frame = CGRect(x: 146, y: 0, width: 20, height: (cell?.frame.height)!)
btn.addTarget(self, action: #selector(buttonPressed(sender: )), for: .touchUpInside)
btn.tag = indexPath.row
btn.setImage(UIImage(named: "delete.png"), for: .normal)
cellx = list[btn.tag]
cell?.contentView.addSubview(btn)
}
I have not tested this above code just made some minor changes maybe it will work :)
If not so please let me know.
EDIT1:
Answer updated

Related

Don't Click button inside in TableView

I have added one Button into tableview button is not clicked. The button is not clicked on the top of TableView. What should I do to make the button clickable? I need to make the CircleMenu button clickable. The button is now on top of tableView3. Do I need to add the button to the tableView?
override func viewDidLoad() {
super.viewDidLoad()
let button = CircleMenu(
frame: CGRect(x: view.frame.width/2 - 10, y: view.frame.height - 270, width: 50, height: 50),
normalIcon:"icon_menu",
selectedIcon:"icon_close",
buttonsCount: 3,
duration: 4,
distance: 85)
button.backgroundColor = UIColor.flatSkyBlue
button.delegate = self
button.layer.cornerRadius = button.frame.size.width / 2.0
view.addSubview(button)
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == self.tableView1 {
if !chipnumber2.text!.isEmpty {
let cell:DeviceTableViewCell2 = tableView1.dequeueReusableCell(withIdentifier: cellIdNew, for: indexPath) as! DeviceTableViewCell2
let deviceItem: Device3New = itemsNew[indexPath.row]
let tap1 = UITapGestureRecognizer(target: self, action: #selector(tittleNewTapped(_:)))
let tap2 = UITapGestureRecognizer(target: self, action: #selector(tittleNewTapped2(_:)))
return cell
}
}
if tableView == self.tableView2 {
if !chipnumber.text!.isEmpty {
let cell:DeviceTableViewCell2 = tableView2.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! DeviceTableViewCell2
let deviceItem: Device3 = items[indexPath.row]
cell.backgroundColor = GradientColor(UIGradientStyle.leftToRight, frame: self.view.frame, colors: [UIColor.flatPowderBlueDark, UIColor.flatSand])
return cell
}
}
if tableView == self.tableView3 {
if !chipnumber3.text!.isEmpty {
let cell:DeviceTableViewCell2 = tableView3.dequeueReusableCell(withIdentifier: cellIdNew2, for: indexPath) as! DeviceTableViewCell2
cell.backgroundColor = GradientColor(UIGradientStyle.leftToRight, frame: self.view.frame, colors: [UIColor.flatPowderBlueDark, UIColor.flatSand])
return cell
}
}
return UITableViewCell()
}
}
Just add a target to the button:
button.addTarget(self, action: #selector(buttonClicked(_:)), for: .touchUpInside)
And then a selector method:
#objc func buttonClicked(_ sender: UIButton) {
print("clicked!")
}
(Edit: Possible duplicate?)

UICollectionViewCell change in one seen in multiple cells in collectionview

I have a uicollectionview with collectionviewcells and each cell has a boolean value associated with a favorites button. There are over 50 cells positioned vertically (four cells are viewable at a time). If the favorite button is clicked it toggles between a highlighted image and a non-highlighted image.
That functionality works, but for some reason when I click one then scroll down I see other cells with their favorite button highlighted. When I scroll back up the cell favorite button is no longer highlighted.
Is there something missing from this code?
NOTE:: As a default I set each cell's boolean value to false. It's only changed when I click on the cell's favorite button.
My code below:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SimpleDispensarySubCell
cell.backgroundColor = UIColor.init(white: 0.10, alpha: 0.25)
cell.infoLine2TextVw.text = ""
cell.infoLine3TextVw.text = ""
if let heading_name = self.dict_dict_holder[indexPath.item]["Name"]{
cell.headerTextVw.text = heading_name
cell.infoLine1TextVw.text = self.dict_dict_holder[indexPath.item]["Phone"]
}
if cell.isFavorite{
cell.isFavorite = true
cell.favorite_button.setImage(#imageLiteral(resourceName: "heart_fill_icon"), for: .normal)
}
else{
cell.isFavorite = false
cell.favorite_button.setImage(#imageLiteral(resourceName: "heart_nofill_icon"), for: .normal)
}
cell.bringSubview(toFront: cell.headerTextVw)
//cell.favorite_button.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(AddFavorite(withSender:))))
cell.favorite_button.addTarget(self, action:#selector(AddFavorite), for: .touchUpInside)
return cell
}
#objc func AddFavorite(withSender sender:UIButton){
let cell = sender.superview as! SimpleDispensarySubCell
if cell.isFavorite{
cell.isFavorite = false
cell.favorite_button.setImage(#imageLiteral(resourceName: "heart_nofill_icon"), for: .normal)
}
else{
cell.isFavorite = true
cell.favorite_button.setImage(#imageLiteral(resourceName: "heart_fill_icon"), for: .normal)
}
}
It's because you are using collectionView.dequeueReusableCell you should define an array to hold favorite state of each cell on it. It could solve your problem.
let favoriteStateArray = countOfRows;
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SimpleDispensarySubCell
cell.backgroundColor = UIColor.init(white: 0.10, alpha: 0.25)
cell.infoLine2TextVw.text = ""
cell.infoLine3TextVw.text = ""
if let heading_name = self.dict_dict_holder[indexPath.item]["Name"]{
cell.headerTextVw.text = heading_name
cell.infoLine1TextVw.text = self.dict_dict_holder[indexPath.item]["Phone"]
}
if favoriteStateArray[indexPath.row]{
cell.isFavorite = true
cell.favorite_button.setImage(#imageLiteral(resourceName: "heart_fill_icon"), for: .normal)
}
else{
cell.isFavorite = false
cell.favorite_button.setImage(#imageLiteral(resourceName: "heart_nofill_icon"), for: .normal)
}
cell.bringSubview(toFront: cell.headerTextVw)
//cell.favorite_button.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(AddFavorite(withSender:))))
cell.favorite_button.addTarget(self, action:#selector(AddFavorite), for: .touchUpInside)
return cell
}
#objc func AddFavorite(withSender sender:UIButton){
let cell = sender.superview as! SimpleDispensarySubCell
let index = collectionView.indexPath(for: cell)
if favoriteStateArray[indexPath.row]{
favoriteStateArray[indexPath.row] = false
cell.favorite_button.setImage(#imageLiteral(resourceName: "heart_nofill_icon"), for: .normal)
}
else{
favoriteStateArray[indexPath.row] = false
cell.favorite_button.setImage(#imageLiteral(resourceName: "heart_fill_icon"), for: .normal)
}
}
Since the cells are getting reused changes are affecting multiple cells.
You can use prepare for reuse method to clear changes that must affect only a particular cell and Clear the cell and prepare it for Reuse.
override func prepareForReuse() {
cell.isFavorite = false
super.prepareForReuse()
}

Can't remove view from superview on reusable tableview cell

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: EventCommentsCustom = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! EventCommentsCustom
guard let release = array[exist: indexPath.section] else { return cell }
if release.user == "condition" {
let image = UIImage()
let imageView = UIImageView(image: image)
imageView.sd_setImage(with: URL(string: "https://example.com/" + TegKeychain.get("profile_pic")!))
imageView.frame = CGRect(x: 20, y: 10, width: 50, height:50)
imageView.layer.borderWidth = 0.4
imageView.layer.masksToBounds = false
imageView.layer.borderColor = UIColor.gray.cgColor
imageView.layer.cornerRadius = 25
imageView.clipsToBounds = true
imageView.tag = 3
cell.addSubview(imageView)
let button = UIButton(frame: CGRect(x: 90, y: 10, width: 200, height: 50))
button.contentHorizontalAlignment = .left
button.setTitleColor(UIColor.lightGray, for: .normal)
button.setTitle(NSLocalizedString("Say something...", comment: ""), for: .normal)
button.addTarget(self, action: #selector(EventComments.openInput), for: .touchUpInside)
button.tag = 3
cell.addSubview(button)
} else {
if let viewWithTag = cell.viewWithTag(3) {
if viewWithTag is UIImageView {
print("DONE")
viewWithTag.removeFromSuperview()
}
}
if let viewWithTag = cell.viewWithTag(3) {
if viewWithTag is UIButton {
print("DONE")
viewWithTag.removeFromSuperview()
}
}
}
return cell
}
I am trying to remove views that I created with a tag in a reusable tableview cell.
However, I still can see UIButton and UIImageview when first reused (5. section of tableview), then It starts to remove properly
Why don't they get removed at the first reuse?
I guess that reusing in your case could mean that the image view and button get added twice for a cell. You only remove one of them though. I think you should take a different approach (like different prototype cells as #vadian stated) into consideration but for now (assuming my assumption is correct) you could try this to fix your problem:
Replace ...
if let viewWithTag = cell.viewWithTag(3) {
if viewWithTag is UIImageView {
print("DONE")
viewWithTag.removeFromSuperview()
}
}
if let viewWithTag = cell.viewWithTag(3) {
if viewWithTag is UIButton {
print("DONE")
viewWithTag.removeFromSuperview()
}
}
With ...
while let viewToRemove = cell.viewWithTag(3) {
if viewToRemove is UIImageView || viewToRemove is UIButton {
viewToRemove.removeFromSuperview()
}
}
Update -
The approach with different cell types would look something like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let release = array[exist: indexPath.section] else { return cell }
if release.user == "condition" {
let cell = tableView.dequeueReusableCell(withIdentifier: "OneIdentifier", for: indexPath) as! OneCustomCellType
// configure your cell
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "AnotherIdentifier", for: indexPath) as! AnotherCustomCellType
// configure your cell
return cell
}
}

Why custom button in CollectionView load only into last cell? (Swift)

Why my custom button in CollectionView load only into last cell?
How can i make that button load in all cells?
var editButton = UIButton()
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCellCard
switch ColorSegment.selectedSegmentIndex {
case 0:
cell.CardView.backgroundColor = ColorArray[indexPath.row]
cell.ColorName.text = ColorName[indexPath.row]
case 1:
cell.CardView.backgroundColor = CustomColorArray[indexPath.row]
cell.ColorName.text = CustomColorName[indexPath.row]
editButton.frame = CGRect(x:63, y:0, width:20,height:20)
editButton.layer.cornerRadius = 10
editButton.backgroundColor = UIColor.lightGray
editButton.layer.setValue(indexPath.item, forKey: "index")
editButton.setImage(UIImage(named: "CloseScan"), for: UIControlState.normal)
editButton.addTarget(self, action: #selector(deleteCell), for: UIControlEvents.touchUpInside)
cell.addSubview(editButton)
default: print("error with switch statement for cell data")
}
return cell
}
Edit Button Function
if EditButton.currentTitle == "Edit" {
EditButton.setTitle("Cancel", for: .normal)
//DeleteButton.isHidden = false
} else if EditButton.currentTitle == "Cancel" {
EditButton.setTitle("Edit", for: .normal)
//DeleteButton.isHidden = true
}
The problem is you are adding same editButton in all the cell, you need to create new UIButton for all the cell.
So change line
editButton.frame = CGRect(x:63, y:0, width:20,height:20)
With
//Create new button instance every time
let editButton = UIButton(frame: CGRect(x:63, y:0, width:20,height:20))
Also cellForItemAt will reuse the cell so instead of creating new UIButton each time you can try like this.
let editButton: UIButton
if let btn = cell.viewWithTag(101) as? UIButton {
editButton = btn
}
else {
editButton = UIButton(frame: CGRect(x:63, y:0, width:20,height:20))
}
editButton.layer.cornerRadius = 10
editButton.backgroundColor = UIColor.lightGray
editButton.layer.setValue(indexPath.item, forKey: "index")
editButton.setImage(UIImage(named: "CloseScan"), for: UIControlState.normal)
editButton.addTarget(self, action: #selector(deleteCell), for: UIControlEvents.touchUpInside)
//Set tag for reuse
editButton.tag = 101
cell.addSubview(editButton)
You can get indexPath of cell in button action like this.
#objc func deleteCell(_ sender: UIButton) {
let point = sender.superview?.convert(sender.center, to: self.collectionView) ?? .zero
guard let indexPath = self.collectionView.indexPathForItem(at: point) else { return }
//Use indexPath here
}
Edit: If you want to hide or show the button than its better that you just add button in your design and create one outlet of in your CustomCollectionViewCell after that in cellForItemAt just hide and show button on basis of condition. From your code you need to show the delete button if selected index of segment is 1. So make your cellForItem like this.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCellCard
cell.CardView.backgroundColor = ColorArray[indexPath.row]
cell.ColorName.text = ColorName[indexPath.row]
//Add delete button in the xib or storyboard collectionViewCell
//Now hide this button if EditButton title is not "Edit"
cell.editButton.isHidden = EditButton.currentTitle != "Edit"
//Add action of button
cell.editButton.addTarget(self, action: #selector(deleteCell), for: UIControlEvents.touchUpInside)
return cell
}
After that in your edit button action reload the collectionView after you set the button title.
if EditButton.currentTitle == "Edit" {
EditButton.setTitle("Cancel", for: .normal)
} else if EditButton.currentTitle == "Cancel" {
EditButton.setTitle("Edit", for: .normal)
}
//Reload your collectionView
self.collectionView.reloadData()
I think you could move the line:
var editButton = UIButton()
To func cellForItemAt :
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCellCard
switch ColorSegment.selectedSegmentIndex {
case 0:
cell.CardView.backgroundColor = ColorArray[indexPath.row]
cell.ColorName.text = ColorName[indexPath.row]
case 1:
cell.CardView.backgroundColor = CustomColorArray[indexPath.row]
cell.ColorName.text = CustomColorName[indexPath.row]
// create editButton in here
var editButton = UIButton()
editButton.frame = CGRect(x:63, y:0, width:20,height:20)
editButton.layer.cornerRadius = 10
editButton.backgroundColor = UIColor.lightGray
editButton.layer.setValue(indexPath.item, forKey: "index")
editButton.setImage(UIImage(named: "CloseScan"), for: UIControlState.normal)
editButton.addTarget(self, action: #selector(deleteCell), for: UIControlEvents.touchUpInside)
cell.addSubview(editButton)
default: print("error with switch statement for cell data")
}
return cell
}
move your button logic outside of switch statement like this
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCellCard
switch ColorSegment.selectedSegmentIndex {
case 0:
cell.CardView.backgroundColor = ColorArray[indexPath.row]
cell.ColorName.text = ColorName[indexPath.row]
case 1:
cell.CardView.backgroundColor = CustomColorArray[indexPath.row]
cell.ColorName.text = CustomColorName[indexPath.row]
default: print("error with switch statement for cell data")
}
var editButton = UIButton.init(frame: CGRect.init(x: 63, y: 0, width: 20, height: 20))
editButton.layer.cornerRadius = 10
editButton.backgroundColor = UIColor.lightGray
editButton.layer.setValue(indexPath.item, forKey: "index")
editButton.setImage(UIImage(named: "CloseScan"), for: UIControlState.normal)
editButton.addTarget(self, action: #selector(deleteCell), for: UIControlEvents.touchUpInside)
cell.addSubview(editButton)
return cell
}

Label with button addTarget event not fire

I have a label in the tableview cell . label have text end of the text has a button that button represent for user like . everything is fine but problem is hitForLike not fire .button click event not fire. do i miss anything
var titleLabel : UILabel = {
var label = UILabel()
label.font = UIFont.systemFont(ofSize: 21)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
func hitForLike(_ sender : UIButton){
print("i miss you ....")
}
func titleWithLikeButton(title:String,isFavorite : Bool){
titleLabel.text = title
let button = UIButton(frame: CGRect(x: titleLabel.intrinsicContentSize.width, y: 0, width: 44, height: 44))
//setup your button here
button.setTitleColor(UIColor.red, for: UIControlState.normal)
let image = UIImage(named: "heart-empty.png")
button.setImage(image, for: UIControlState.normal)
button.addTarget(self, action: #selector(hitForLike(_:)), for: UIControlEvents.touchUpInside)
//Add the button to the text label
titleLabel.addSubview(button)
}
I think you need to enable user interaction on your label, like so:
titleLabel.isUserInteractionEnabled = true
So your entire function ends up looking like this:
func titleWithLikeButton(title:String,isFavorite : Bool){
titleLabel.text = title
titleLabel.isUserInteractionEnabled = true //enable userinteraction
let button = UIButton(frame: CGRect(x: titleLabel.intrinsicContentSize.width, y: 0, width: 44, height: 44))
//setup your button here
button.setTitleColor(UIColor.red, for: UIControlState.normal)
let image = UIImage(named: "heart-empty.png")
button.setImage(image, for: UIControlState.normal)
button.addTarget(self, action: #selector(hitForLike(_:)), for: UIControlEvents.touchUpInside)
//Add the button to the text label
titleLabel.addSubview(button)
}
Hope that helps.
You have to set your cell button target in func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell, like below:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableViewForPAC.dequeueReusableCell( withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell
cell.yourButton.tag = indexpath.row
cell.yourButton.addTarget(self, action: #selector(hitForLike(_:)), for: UIControlEvents.touchUpInside)
return cell
}
func hitForLike(_ sender : UIButton){
print(sender.tag)
print("i miss you ....")
}

Resources