I have a custom UITableViewCell subclass where I am putting two buttons in the cell, I want both to be clickable, but so far I cannot even get any kind of click to trigger (besides row selection, but I disabled that on my UITableViewController subclass). Basically When one of two buttons is selected, it should remove those two buttons and update what is in the cell with a list of selectable choices (also buttons). I am doing everything programmatically (no IB).
My TableViewCell with Initial Two Buttons
I have looked around a lot and haven't found anything that handles more than ONE button in a tableViewCell. Currently I've been trying to add targets to my buttons in my UITableViewCell's awakeFromNib():
for button in initialChoiceButtons{
button.isUserInteractionEnabled = true
button.addTarget(self, action: #selector(initialChoicePressed(sender:)), for: UIControlEvents.touchUpInside)
}
One thing I've tried is in my tableView in cellForRowAt for my custom cell is to bring my buttons to the front of the cell:
for button in (cell as! FormDropDownTableViewCell).initialChoiceButtons{
cell.bringSubview(toFront: button)
}
I'm really stumped and feel like this should easy. I'm on the verge of just using a stackView inside of scrollview for everything...
Ok so I figured out a somewhat clean way to separate button taps in my tableviewcell, by creating a delegate protocol with a function that I'll call from my target in my tableviewcontroller for every button in my tableviewcell.
protocol UIButtonSelectorDelegate{
func handleTap(button:DelegatingButton) //will select different functions based on DelegatingButton.actionType
}
class DelegatingButton:UIButton{
var selectorDelegate:UIButtonSelectorDelegate?
var actionType:String = "default"
}
in my FormDropDownTableViewCell I conform to the UIButtonSelectedDelegate and implement handleTap like so:
func handleTap(button:DelegatingButton){
switch button.actionType{
case "initialChoiceSelect":
initialChoicePressed(initialChoice:button) //specific method for certain button.actionType also in my FormDropDownTableViewCell
case "cardTypeSelect":
cardTypeSelected(selectedCardType:button)
default:
break
}
}
Now I add the target-actions for every button in cellForRowAt in my tableviewcontroller like so:
button.addTarget(self, action: #selector(handleButtonTaps), for: UIControlEvents.touchUpInside)
and the handleButtonTaps func in the tableviewcontroller is simple:
func handleButtonTaps(sender: DelegatingButton){
sender.selectorDelegate?.handleTap(button: sender)
}
Enjoyed Talking to myself =P ..
Related
I'm making an app that allows a user to "pin" certain elements in a collection view I have implemented in my home ViewController class. To pin an element, the user must access a button that is part of my WordCell (UICollectionViewCell) class. However, when I try to press the button from my home view controller, nothing happens.
Here is all the relevant code and screenshots:
The star on the right hand side is the button inside the CollectionViewCell that I want the user to be able to push through the home view.
Below is all the relevant code in my ViewController class. I am using a delegate to pass the cell that was pressed into my home class ViewController. I also plan on passing more data back and forth between the cell (UICollectionCellView) class and ViewController in the future.
extension ViewController: UICollectionViewDataSource{
//......
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let wordCell = collectionView.dequeueReusableCell(withReuseIdentifier: wordCellReuseID, for: indexPath) as! WordCell
wordCell.delegate = self
wordCell.configure(word: tempWords[indexPath.item])
return wordCell
}
//......
}
extension ViewController: WordCellDelegate{
func star(wasPressedOnCell: WordCell){
print("touched")
if(wasPressedOnCell.isStarred){ //if the button has already been starred, unstar it
wasPressedOnCell.starButton.setImage(UIImage(named: "unfilled_star.png"), for: .normal)
wasPressedOnCell.isStarred = false
}
else{ //else, star the button
wasPressedOnCell.starButton.setImage(UIImage(named: "filled_star.png"), for: .normal)
wasPressedOnCell.isStarred = true
}
}
}
Here is relevant code in my class that conforms to UICollectionCellView:
//delegate setup for home class
protocol WordCellDelegate: class{
func star(wasPressedOnCell cell: WordCell) //parameter: cell that was pressed
}
//........
//button setup
starButton = UIButton()
starButton.setImage(UIImage(named: "unfilled_star.png"), for: .normal)
starButton.translatesAutoresizingMaskIntoConstraints = false
starButton.addTarget(self, action: #selector(starred), for: .touchUpInside)
contentView.addSubview(starButton)
//......
//button objective function
#objc func starred(){
print("touched")
delegate?.star(wasPressedOnCell: self)
//starredTapAction?() //chained back to main view controller
}
However, when I try to press the star on my home view controller screen, the objc function inside my UICollectionCellView class is not called. I've read from previous posts that this is most likely due to a hierarchy of classes and which view controls which objects, but I haven't been able to find a solution to this issue yet. I'm not sure what needs to be changed so the button inside the collection cell can be pressed through the view of the collection.
Please let me know if you need any more information, and thank you for reading this post!
Please disable cell selection of UIcollection view and your button touch event will fire, basically at a time you can either use collection view did select method or button action method
I had the exact same issue less than 2 weeks ago; I am still not sure about the root cause but I know the fix.
Move this line --
starButton.addTarget(self, action: #selector(starred), for: .touchUpInside)
inside your cell configuration method --
WordCell.configure (word: )
Add --
starButton.isEnabled = true
starButton.isUserInteractionEnabled = true
under your --
//button setup
only if the tap is not registering (ie if you can't see the button getting tapped)
It's really about setting the button target under cellForItemAt(); that'll do the trick.
Screenshot of the app:
I have an 'x' button to delete a TableViewCell which is in a table within a table. On click of the button, I would like to remove the cell, so I need to know the 2 indexes of the button click, the row if the first table view, and then within that tableview the row of the cell which the button was clicked. All I have is the sender.
So to be a bit clearer, in the screenshot, if someone clicks the x under the ford fiesta, I need to get indexpath 0 for the "subtableview" and 1 for the tableview, and that way I know to delete this element from the table datasource.
I do it successfully by doing:
var cell = sender.superview
while (cell != nil) && !((cell?.isKind(of: CustomCell.self))!) {
cell = cell?.superview
}
let tbl = cell?.superview as! UITableView
let indexPath = tbl.indexPath(for: (cell as? UITableViewCell)!
)
The stupid thing is I have to do it twice, once to find the index of the cell within the "sub"tableview, and then again to find the index of the "subtableview" within the tableview.
Is there a better way to do this? Isnt there a way to get the buttonClick to get the didSelectRowAt to fire and add the sender object to it (so I know that a button was clicked as opposed to the cell being selected)?
EDIT I forgot to mention that the first tableview opens and closes on click, so the main tableview has 2 different cell types, one closed (so no nested tableview) and then onselect of a row from that tableview, the cell is replaced with a detailed cell which has another tableview inside it, thats why sectioned tableview isnt a solution (to the best of my knowledge, I'm new here)
One way to do it is to use closures. You set up your cell with a closure and then call it. Pretty much like this:
class CellWithClosure: UITableViewCell {
var button: UIButton = UIButton()
var closureForButton: (Void) -> Void
func setupCell(closureForButton: #escaping (Void) -> Void) {
self.closureForButton = closureForButton
button.addTarget(self, action: #selector(buttonAction), for: UIControlEvents.touchUpInside)
}
#objc func buttonAction() {
closureForButton()
}
}
I am trying to replicate the same type of functionality as the stock iOS Mail app when a user swipes a tableview cell as currently seen here:
There are 3 options: More, Flag, and Archive. When the user taps on any one of the 3, the background color changes to indicate it's in a highlighted state. However, the icon and text do not change color.
I am trying to achieve the same effect.
I am following this tutorial guide to make a custom swipeable tableview cell using gestures:
How To Make A Swipeable Table View Cell With Actions – Without Going Nuts With Scroll Views
Currently I have this set up:
In each cell contains 3 UIViews, where each UIView contains an UIImageView and UILabel representing Dirty, Edit, Delete.
I am adding tap gestures to the 3 UIViews like so:
let dirtyButtonView = cell.viewWithTag(7)
let editButtonView = cell.viewWithTag(8)
let deleteButtonView = cell.viewWithTag(9)
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(MyVC.buttonPressed))
tapRecognizer.numberOfTapsRequired = 1
dirtyButtonView?.addGestureRecognizer(tapRecognizer)
editButtonView?.addGestureRecognizer(tapRecognizer)
deleteButtonView?.addGestureRecognizer(tapRecognizer)
However, I cannot achieve the highlight state functionality that the Mail app has when a user selects one of the 3 options.
I'm not sure if this is the correct implementation, i.e. having a UIView and adding a gesture to it?
How can I achieve something similar to the iOS Mail app swipe functionality?
Thanks
I found exactly what I was looking for, SwipeCellKit, by jerkoch. This library performs the same exact actions as the stock iOS Mail app does when swiping to the left. No need to deal with different UIViews and UIButtons.
To use, simply conform to the SwipeTableViewCellDelegate, and use it in editActionsForRowAt like so:
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? {
guard orientation == .right else { return nil }
let deleteAction = SwipeAction(style: .destructive, title: "Delete") { action, indexPath in
// handle action by updating model with deletion
}
// customize the action appearance
deleteAction.image = UIImage(named: "delete")
return [deleteAction]
}
Make sure to change the cell's class to SwipeTableViewCell and set its delegate like so: cell.delegate = self.
I would take a look at the SWTableViewCell by CEWendel. It looks like it has exactly what you're looking for.
May be late to answer this, but in case anyone else is looking - consider this read to answer the question: https://www.raywenderlich.com/62435/make-swipeable-table-view-cell-actions-without-going-nuts-scroll-views
I have a prototype cell with an UIImageView, when user tap this ImageView, app should display a Collection View where user can select an alternative icon for the cell. So, in UITableViewCell I added a Gesture Recognizer:
internal let iconTappedGR = UITapGestureRecognizer()
then I implemented it in table's cellForRowAtIndexPath:
cell.iconTappedGR.addTarget(self, action: #selector(changeIcon))
cell.iconView.gestureRecognizers = []
cell.iconView.gestureRecognizers!.append(cell.iconTappedGR)
and I added a changeIcon function
func changeIcon () {
print("imageView tapped!")
}
trouble is that it doesn't works; I tried even using storyboard but is the same...where am I wrong?
I solved using
cell.iconView.userInteractionEnabled = true
following this answer https://stackoverflow.com/a/36495864/2085352 to a question posted 30 minutes later than mine!
I would like to create an app with a list of TODO items. Each TODO should have a functional checkmark/checkbox on the left side of the cell. If I click on the right side of the TODO then I would like to show its details.
I have create a Table View. When I click on a cell I follow a segue to a UIViewController that shows the details of the cell.
I think displaying the checkmark may be possible by defining the cell's imageView. However, I can't make the imageView functional, i.e., clickable. Every time I click on it it simply shows the details.
Can you provide basic sample code to show how I can have both a segue to the details of the cell AND a checkmark that is clickable?
Thanks, Daniel
OK, I figured it out. Added this code snippet:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath:NSIndexPath) -> UITableViewCell {
//...
let singleTap = UITapGestureRecognizer(target: self, action: Selector("tapped"))
singleTap.numberOfTapsRequired = 1
cell.imageView?.userInteractionEnabled = true
cell.imageView?.addGestureRecognizer(singleTap)
//...
}
func tapped() {
println("tapped!")
}