Swipe gesture in tableview - ios

I want to add Swipe gesture on my cell. It work fine.but the Problem is when i swipe on first cell but my fifth cell also swipe. I know this indexpath problem. Please help.But i am stuck from few hour.
let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(self.handleLeftSwipe))
swipeLeft.direction = UISwipeGestureRecognizerDirection.left
table.addGestureRecognizer(swipeLeft)
let swipeLeftd = UISwipeGestureRecognizer(target: self, action: #selector(self.handleLeftSwipes))
swipeLeftd.direction = UISwipeGestureRecognizerDirection.right
table.addGestureRecognizer(swipeLeftd)
#objc func handleLeftSwipe(sender: UITapGestureRecognizer) {
print("rigt called")
let location = sender.location(in: self.table)
let indexPath = self.table.indexPathForRow(at: location)
let cell = self.table.cellForRow(at: indexPath!) as! TableViewCell
print("swipe")
cell.myView.frame.origin.x = -94
}
#objc func handleLeftSwipes(sender: UITapGestureRecognizer) {
print("labelSwipedLeft called")
let location = sender.location(in: self.table)
let indexPath = self.table.indexPathForRow(at: location)
let cell = self.table.cellForRow(at: indexPath!) as! TableViewCell
print("swipe")
cell.myView.frame.origin.x = 80
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "TableViewCell"
var cell: TableViewCell! = tableView.dequeueReusableCell(withIdentifier: identifier) as? TableViewCell
if cell == nil {
var nib : Array = Bundle.main.loadNibNamed("TableViewCell",owner: self,options: nil)!
cell = nib[2] as? TableViewCell
}
return cell!
}

The reason for that is reusing your cells. I assume you do not set anything in your prepareForReuse(). So, your problem is in line cell.myView.frame.origin.x = 80, right? Just set it to default in prepareForReuse or in cellForRowAt indexPath.

Related

How to avoid adding more than one tap gesture to any cell?

I have a weird problem. When scrolling down, cells disappear if Tap Gesture happened.
Looks like I need to stop adding Tap Gesture to cells. I've done testing of this condition in function but it didn't work.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ToDoItemsCell
...
cell.textField.delegate = self
cell.textField.isHidden = true
cell.toDoItemLabel.isUserInteractionEnabled = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(toDoItemLabelTapped))
tapGesture.numberOfTapsRequired = 1
cell.addGestureRecognizer(tapGesture)
return cell
}
And here is my function:
#objc func toDoItemLabelTapped(_ gesture: UITapGestureRecognizer) {
if gesture.state == .ended {
let location = gesture.location(in: self.tableView)
if let indexPath = tableView.indexPathForRow(at: location) {
if let cell = self.tableView.cellForRow(at: indexPath) as? ToDoItemsCell {
cell.toDoItemLabel.isHidden = true
cell.textField.isHidden = false
cell.textField.becomeFirstResponder()
cell.textField.text = cell.toDoItemLabel.text
}
}
}
}
Tapping works, but it keeps adding to other cells and makes them disappear. What can be the issue?
Gesture should be added once to each cell. In your code gesture will be added every time cellForRowAt will be called and it will be called many times especially when you scroll down to list.
Move you gesture add code to ToDoItemsCell class and than you can use delegates to inform your view controller when cell gets tapped.
protocol ToDoItemsCellDelegate {
toDoItemsCellDidTapped(_ cell: ToDoItemsCell)
}
class ToDoItemsCell : UITableViewCell {
weak var delegate: ToDoItemsCellDelegate?
var indexPath: IndexPath!
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// code common to all your cells goes here
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(toDoItemLabelTapped))
tapGesture.numberOfTapsRequired = 1
self.addGestureRecognizer(tapGesture)
}
#objc func toDoItemLabelTapped(_ gesture: UITapGestureRecognizer) {
delegate?.toDoItemsCellDidTapped(self)
}
}
In function cellForRowAt you can just select the delegate and set indexPath.
Note:
If you just wanted to perform action when user taps any cell you can use didSelectRowAt method of UITableViewDelegate.

How to get the get a index.row and index section from a UITableViewCell with an UIStepper programmatically using Swift 4 [duplicate]

I have table view cells like quiz. And in each cell I have a buttons And how can I identify in which cell button was pressed. Maybe by IndexPath???
This is how I connected button to
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionCell")!
variant1 = cell.contentView.viewWithTag(1) as! UIButton
variant2 = cell.contentView.viewWithTag(2) as! UIButton
variant3 = cell.contentView.viewWithTag(3) as! UIButton
variant4 = cell.contentView.viewWithTag(4) as! UIButton
variant1.addTarget(self, action: #selector(self.variant1ButtonPressed), for: .touchUpInside)
variant2.addTarget(self, action: #selector(self.variant2ButtonPressed), for: .touchUpInside)
variant3.addTarget(self, action: #selector(self.variant3ButtonPressed), for: .touchUpInside)
variant4.addTarget(self, action: #selector(self.variant4ButtonPressed), for: .touchUpInside)
return cell
}
func variant1ButtonPressed() {
print("Variant1")
variant1.backgroundColor = UIColor.green
}
func variant2ButtonPressed() {
print("Variant2")
variant2.backgroundColor = UIColor.green
}
func variant3ButtonPressed() {
print("Variant3")
variant3.backgroundColor = UIColor.green
}
func variant4ButtonPressed() {
print("Variant4")
variant4.backgroundColor = UIColor.green
}
This is how it looks like in Storyboard:
You should use delegate pattern, basic example:
protocol MyCellDelegate {
func didTapButtonInside(cell: MyCell)
}
class MyCell: UITableViewCell {
weak var delegate: MyCellDelegate?
func buttonTapAction() {
delegate?.didTapButtonInside(cell: self)
}
}
class ViewController: MyCellDelegate {
let tableView: UITableView
func didTapButtonInside(cell: MyCell) {
if let indexPath = tableView.indexPath(for: cell) {
print("User did tap cell with index: \(indexPath.row)")
}
}
}
Use this line to get indexPath, Where you have to pass UIButton on target selector
func buttonTapped(_ sender:AnyObject) {
let buttonPosition:CGPoint = sender.convert(CGPointZero, to:self.tableView)
let indexPath = self.tableView.indexPathForRow(at: buttonPosition)
}
Since actions need to be inside the view controller, ctrl + drag from your button to the view controller - this will use the responder chain.
Basically you need to convert the view (button) to the coordinate system of the table view in order to tell what is the IndexPath and if you have the IndexPath you have the object that corresponds to the button inside the cell that was tapped:
#IBAction func buttonTapped(_ sender: Any) {
if let indexPath = indexPath(of: sender) {
// Your implementation...
}
}
private func indexPath(of element:Any) -> IndexPath? {
if let view = element as? UIView {
// Converting to table view coordinate system
let pos = view.convert(CGPoint.zero, to: self.tableView)
// Getting the index path according to the converted position
return tableView.indexPathForRow(at: pos) as? IndexPath
}
return nil
}
It is important to mention that there many solutions for your question. But you should know that in Apple's sample projects they also use this technic.
This is how you add tag to a UIButton inside UITableView, add below lines of code in
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
cell.yourButton.tag = indexPath.row
cell.yourButton.addTarget(self, action:#selector(btnPressed(sender:)), for: .touchUpInside)
Add this function in your ViewController
func btnPressed(sender: UIButton)
{
print("Button tag \(sender.tag)")
}
Hope this helps...
Simple Subclass button just like JSIndexButton
class JSIndexButton : UIButton {
var indexPath : IndexPath!
}
Now at cellForRowAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ItemCell
let itemCategory = dataList[button.indexPath.section];
let item = itemCategory.items[button.indexPath.row];
cell.imgView.setImageWithURL(item.photoUrl);
cell.btnBuy.indexPath = indexPath;
cell.btnBuy.addTarget(self, action: #selector(JSCollapsableTableView.btnBuyPressed(_:)), for: UIControlEvents.touchUpInside)
return cell;
}
Check Button Action
#IBAction func btnBuyPressed(_ button: JSIndexButton) {
let itemCategory = dataList[button.indexPath.section];
let item = itemCategory.items[button.indexPath.row];
}
#objc func ItemsDescription(_ sender: UIButton?,event: AnyObject?) {
let touches: Set<UITouch>
touches = (event?.allTouches!)!
let touch:UITouch = (touches.first)!
let touchPosition:CGPoint = touch.location(in: self.tableView)
let indexPath:NSIndexPath = self.tableView.indexPathForRow(at: touchPosition)! as NSIndexPath
}
adding target
cell.ItemsDescription.addTarget(self, action: #selector(ItemsDescription(_:event:)), for: UIControlEvents.touchUpInside)

Swift Change label text color on tap from within TableViewCell

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

Selector to get indexPath UICollectionView Swift 3.0

I'm trying to get indexPath on the cell when it is tapped twice.
I'm passing arguments in Selector like this but it is giving error.
What is the correct format for this ?
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let subOptioncell : SubOptionsCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: subOptionsCVReuseIdentifier, for: indexPath) as! SubOptionsCollectionViewCell
let imageNamed = "\(customizeOptionSelected[indexPath.row])"
subOptioncell.subOptionsImage.image = UIImage(named: imageNamed)
let tap = UITapGestureRecognizer(target: self, action: #selector(doubleTapped(sender: indexPath)))
tap.numberOfTapsRequired = 2
collectionView.addGestureRecognizer(tap)
return subOptioncell
}
}
func doubleTapped(sender: IndexPath) {
print("Double Tap")
}
First of all you are adding tapGesture to collectionView instead of subOptioncell.
It should be:
subOptioncell.addGestureRecognizer(tap)
Instead of:
collectionView.addGestureRecognizer(tap)
You cannot pass other instance with selector of UIGestureRecognizer, the only instance you can pass is UI(Tap)GestureRecognizer. If you want the indexPath of that cell you can try like this. First of all set your selector of TapGesture like this.
let tap = UITapGestureRecognizer(target: self, action: #selector(doubleTapped(sender:)))
Now method should be like:
func doubleTapped(sender: UITapGestureRecognizer) {
if let cell = sender.view as? SubOptionsCollectionViewCell, let indexPath = self.collectionView.indexPath(for: cell) {
print(indexPath)
}
}
Edit: If you want to show/hide image on cell double tap then you need to handle it using indexPath of cell, for that first declare one instance of IndexPath and use it inside cellForItemAt indexPath.
var selectedIndexPaths = IndexPath()
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
//Your code
//Now add below code to handle show/hide image
cell.subOptionSelected.isHidden = self.selectedIndexPaths != indexPath
return cell
}
Now on doubleTapped action of UITapGestureRecognizer set the selectedIndexPath.
func doubleTapped(sender: UITapGestureRecognizer) {
if let cell = sender.view as? SubOptionsCollectionViewCell, let indexPath = self.collectionView.indexPath(for: cell) {
if self.selectedIndexPaths == indexPath {
cell.subOptionSelected.isHidden = true
self.selectedIndexPaths = IndexPath()
}
else {
cell.subOptionSelected.isHidden = false
self.selectedIndexPaths = indexPath
}
}
}
The correct selector in your case is doubleTapped:. That is
let tap = UITapGestureRecognizer(target: self, action: #selector(doubleTapped:))
You can not fire arbitrary parameter when the target method is called. You can set target on subOptioncell by
let tap = UITapGestureRecognizer(target: subOptioncell, action: #selector(doubleTapped:))
And you can set whatever arbitrary object.parameter you want in subOptioncell
You need to add Selector like this way
let tap = UITapGestureRecognizer(target: self, action: #selector(YourViewControllerName.doubleTapped(_:)))
subOptioncell.addGestureRecognizer(tap)
Change your code to.
let tap = UITapGestureRecognizer(target: self, action: #selector(doubleTapped(_:)))
and the function to.
func doubleTapped(_ sender: AnyObject) {
print("Double Tap")
}
At your Datasource method
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let subOptioncell : SubOptionsCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: subOptionsCVReuseIdentifier, for: indexPath) as! SubOptionsCollectionViewCell{
//... your code
subOptioncell.addGestureRecognizer(tap)
return subOptioncell
}
}
Then at the function cellTapped()
func cellTapped(sender: UITapGestureRecognizer){
let tapLocation = sender.location(in: yourCollectionView)
let indexPath : IndexPath = yourCollectionView.indexPathForItem(at: tapLocation)!
var currentIndex = 0
if let cell = yourCollectionView.cellForItem(at: indexPath){
currentIndex = cell.tag
}
print("Your Selected Index : \(currentIndex)")
}
Happy Coding!!!

Display button when UICollectionView Cell Tapped

I'm having an Image and when user taps twice on that image then I show a button which has a tick sign as if like user has ticked that Image. I have set the button hidden at first from Storyboard.
I'm getting cell tap using this
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if collectionView == self.subOptionsCollectionView{
let imageNamed = "\(customizeOptionSelected[indexPath.row])"
shirtImage.image = UIImage(named: imageNamed)
let tap = UITapGestureRecognizer(target: self, action: #selector(doubleTapped))
tap.numberOfTapsRequired = 2
collectionView.addGestureRecognizer(tap)
}
}
func doubleTapped() {
print("Double Tap")
}
But how do I display that tick/button ?
put your code in cellForRowAtIndexPath instead of didSelect and disable the userInteraction of collectionView then you can set the isHidden property of the button to true in doubleTapped, but you have to change the function like this(Swift3):
func doubleTapped(selectedIndex: IndexPath) {
print("Double Tap")
}
and change the selector like this:
UITapGestureRecognizer(target: self, action: self.doubleTapped(selectedIndex: indexPath))
There is another solution:
put your code in cellForRowAtIndexPath instead of didSelect then you can set the isHidden property of the button to true in doubleTapped, but you have to change the function like this(Swift2):
func doubleTapped(sender: AnyObject) {
let buttonPosition: CGPoint = sender.convertPoint(CGPointZero, toView: self.collectionView)
let indexPath: NSIndexPath = self.collectionView.indexPathForRowAtPoint(buttonPosition)!
//you have the selected cell index
let cell = self.collectionView.cellForItemAtIndexPath(indexPath)
//now you have the cell and have access to the button
}
and add the gesture like this:
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.doubleTapped(_:)))
cell.addGestureRecognizer(tapGesture)
Swift 4 Update :
#IBAction func doubleTap(_ sender: UITapGestureRecognizer) {
let buttonPosition: CGPoint = sender.location(in: self.collectionView)
guard let indexPath = self.collectionView?.indexPathForItem(at: buttonPosition) else { return }
print ("doubleTap on cell at: ", indexPath)
let cell = self.collectionView.cellForItem(at: indexPath)
// now you have the cell and have access to the button
}

Resources