On my tableview I have a UIImageView, and I need change the image when swipe action is executed. I have a class to row of tableview, so I changed the code to try do it. This is the code but not worked. How can I do it?
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) -> CellTableViewFriends{
// you need to implement this method too or you can't swipe to display the actions
let cell = self.tableViewFriends.dequeueReusableCellWithIdentifier("CellTableViewFriends") as! CellTableViewFriends;
cell.imgArrow.image = UIImage(named: "myImage")
return cell
}
in your cell class, you can add the following methods:
func addSwipeGesture() {
let gestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(self.swipedLeft))
gestureRecognizer.direction = .Left
self.addGestureRecognizer(gestureRecognizer)
}
func swipedLeft(gesture: UIGestureRecognizer!) {
//Change picture here
imgArrow.image = UIImage(named: "myImage")
}
You can change the direction or do the same for adding multiple directions.
Then you should make sure you call the addSwipeGesture command when creating the cell, you can do that in cellForRowAtIndexPath.
Hope this helps!
If that's what you want to do, you should change:
let cell = self.tableViewFriends.dequeueReusableCellWithIdentifier("CellTableViewFriends") as! CellTableViewFriends;
to cellForRowAtIndexPath(_:)
let cell = self.tableViewFriends.cellForRowAtIndexPath(indexPath)
You should add swipegesture recognozer to custom cell class and and handle swipe from that class i think. commitEditingStyle is for editing cell like delete, so you can't use it to swape images in cell.
Hope this will help :)
Related
I have two images inside one cell of uitableview, these images shows to images from an external server and each tag of them has an id of item which this image represent, I need if I clicked on this image move user to new view controller which show details of this item, I force a problem, where user need to double click to show details instead of one click, the following my code:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = self.tableView.cellForRow(at: indexPath as IndexPath) as! prodctCell
Id1stMove = cell.image1st.tag
let tapGesture = UITapGestureRecognizer (target: self, action: #selector(ItemsController.imgTap))
cell.image1st.addGestureRecognizer(tapGesture)
cell.image1st.isUserInteractionEnabled = true
let cell1 = self.tableView.cellForRow(at: indexPath as IndexPath) as! prodctCell
Id2ndMove = cell1.image2nd.tag
let tapGesture1 = UITapGestureRecognizer (target: self, action: #selector(ItemsController.imgTap1))
cell1.image2nd.addGestureRecognizer(tapGesture1)
}
func imgTap()
{
let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: "testViewController") as? testViewController
let navController = UINavigationController(rootViewController: secondViewController!)
navController.setViewControllers([secondViewController!], animated:true)
self.revealViewController().setFront(navController, animated: true)
revealViewController().pushFrontViewController(navController, animated: true)
secondViewController?.movmentId = Id1stMove
updateCount(itemId: Id1stMove)
}
Yesterday itself I created sample and tried.I got the solution.But I could not post my answer immediately as I had some work.
Now I will give you my answer.I don't expect reputation for my below answer.
When you click or tap the image first time,it navigates.
You don't need to add TapGestureRecognizer for imageView in didSelectRowAt method.You need to add TapGestureRecognizer for image View in cellForRowAt method.
import UIKit
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
let mobiles: [String] = ["iPhone", "Android"]
let images: [String] = ["iPhone.png", "android.png"]
#IBOutlet var tableViewImageTapping: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// number of rows in table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.mobiles.count
}
// create a cell for each table view row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var cell:UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: "cell")
if (cell == nil) {
cell = UITableViewCell(style:UITableViewCellStyle.default, reuseIdentifier:"cell")
}
cell?.textLabel?.text = self.mobiles[indexPath.row]
let strImageName = images[indexPath.row]
cell?.imageView?.image = UIImage(named: strImageName)
cell?.imageView?.tag = indexPath.row
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTapped(tapGestureRecognizer:)))
tapGestureRecognizer.numberOfTapsRequired = 1
cell?.imageView?.isUserInteractionEnabled = true
cell?.imageView?.addGestureRecognizer(tapGestureRecognizer)
return cell!
}
// method to run when table view cell is selected
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped table view cell index is \(indexPath.row).")
}
// method to run when imageview is tapped
func imageTapped(tapGestureRecognizer: UITapGestureRecognizer)
{
let imgView = tapGestureRecognizer.view as! UIImageView
print("your taped image view tag is : \(imgView.tag)")
if (imgView.tag == 0) //Give your image View tag
{
//navigate to next view
}
else{
}
}
}
Output Screenshot
Printed results are
When you click the first image in first click
Then when you click the second image in first click
You need to execute this code in cellForRowAt indexPath instead did select.
As you said you want to use image id from the tag value I suggest below change in addition of adding code in cellForRowAt indexPath:
Change tapGesture code as below:
let tapGesture = UITapGestureRecognizer (target: self, action: #selector(imgTap(tapGesture:)))
And imgTap function:
func imgTap(tapGesture: UITapGestureRecognizer) {
let imgView = tapGesture.view as! UIImageView
let idToMove = imgView.tag
//Do further execution where you need idToMove
}
You are assigning the gesture recognizers in the "did select row at index path" method, this means that the user must select (tap) a cell for the gesture recognizers to be assigned to the images, and then the user must tap the image for those recognizers to react by calling "imgTap()", those are the two taps.
What you should do instead is assign the gesture recognizers in the "cell for row at index path" method, so when you create each cell you also create the gesture recognizers, that way when the user taps an image for the first time the tap is recognized and "imgTap()" is called.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) as! CustomCell
// Configure the cell...
let tapGesture1 = UITapGestureRecognizer (target: self, action: #selector(ItemsController.imgTap))
cell.image1st.addGestureRecognizer(tapGesture1)
let tapGesture2 = UITapGestureRecognizer (target: self, action: #selector(ItemsController.imgTap))
cell.image2nd.addGestureRecognizer(tapGesture2)
return cell
}
I also recommend changing the code a little so you can call "imgTap()" with a parameter (the id of what was tapped), instead of having 2 methods "imgTap()" and "imgTap1()".
Please don't use force unwrap as! prodctCell
Please use name swift naming conventions
Don't use .tag cell is usually reused it might break in some edge cases
Now back to your question, you already have a custom cell that holds the image.
you have several possibilities
Add the gesture recogniser to the imageView
you can change that to a button
Add a button over the image view (no title or image for it)
Then create an #IBAction for the button/gesture recogniser, and a delegate method that will call the main viewController where you can pass all the data you need from the cell to instantiate the second VC
Update
I would advice against adding the logic of handling the tap in the cellForRow the viewController is not meant to handle and manage his child logic.
I have a UITableViewDataSource with the following
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: OutletDetails.cellIdentifier) as! OutletDetails
cell.selectionStyle = .none
cell.isUserInteractionEnabled = true
cell.location = "Some location will be here"
let tap = UITapGestureRecognizer(target: self, action: #selector(locationClicked))
tap.cancelsTouchesInView = false
tap.numberOfTapsRequired = 1
cell.location.addGestureRecognizer(tap)
}
where cell.location is a UILabel object. What I'm trying to do here is to detect tap events on the UILabel. I looked all over the Internet and everyone is suggesting this method, however, this code is not working in my case. The method locationClicked is not being called at all. Can anyone tell me what's wrong with my code?
Edit
One more thing, is it a good idea to do it this way memory-wise? I mean if we have a long list, then many UIGestureRecognizer objects will be generated for each cell. This is because the method will be called a lot while scrolling the items.
Add tap gesture to the object and enable its user interaction. Yes you can take button as well.
//Adding tap gesture
let cellNameTapped = UITapGestureRecognizer(target: self, action: #selector(nameTapped))
nameLabel.isUserInteractionEnabled = true// UILabel made available for touch interaction
nameLabel.addGestureRecognizer(cellNameTapped) //gesture added
//Method called on touch of nameLabel
#objc func nameTapped(tapGestureRecognizer: UITapGestureRecognizer){
//print(tapGestureRecognizer.view)
}
Since you're dequeuing a cell, you will need to somehow get a reference to the UITapGestureRecognizer and either remove it or reuse it. Otherwise every time you reuse a cell, you will be laying another recognizer onto the one that is already on the cell. If you're subclassing the UITableViewCell you can just add the recognizer as a property.
However, using the code you posted I'm suggesting you use a UIButton and add a tag so you can get a reference to it later. You can set the bounds of the button equal to the bounds of the label. Try something like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: OutletDetails.cellIdentifier) as! OutletDetails
cell.selectionStyle = .none
cell.isUserInteractionEnabled = true
cell.location = "Some location will be here"
// If we don't already have a button on our cell, create one and set the tag
if cell.viewWithTag(103) as? UIButton == nil {
let newButton = UIButton(frame: cell.location.bounds)
newButton.tag = 103
newButton.addTarget(target: self, action: #selector(locationClicked), for: .touchUpInside)
}
}
I have a label I want to tap on using addGestureRecognizer. I put it in cellForRowAtIndexPath but when I do print(label.text), it prints a label from another cell. But when I put it in didSelectRowAtIndexPath, it prints out the right label for that cell.
What is the best way to fix this?
Here is the code:
var variableToPass: String!
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell : MainCell! = tableView.dequeueReusableCellWithIdentifier("MainCell") as! MainCell
variableToPass = label1.text
cell.label1.userInteractionEnabled = true
let tapLabel = UITapGestureRecognizer(target: self, action: #selector(ViewController.tapLabel(_:)))
cell.label1.addGestureRecognizer(tapLabel)
return cell as MainCell
}
func tapCommentPost(sender:UITapGestureRecognizer) {
print(variableToPass)
}
I think you forget to set the tap.tag = indexPath.row for identify which cell you tabbed for Find, for example
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell : MainCell! = tableView.dequeueReusableCellWithIdentifier("MainCell") as! MainCell
variableToPass = label1.text
cell.label1.userInteractionEnabled = true
let tapLabel = UITapGestureRecognizer(target: self, action: #selector(ViewController.tapLabel(_:)))
cell.label1.tag = indexPath.row
tapLabel.numberOfTapsRequired = 1
cell.label1.addGestureRecognizer(tapLabel)
return cell as MainCell
}
func tapLabel(sender:UITapGestureRecognizer) {
let searchlbl:UILabel = (sender.view as! UILabel)
variableToPass = searchlbl.text!
print(variableToPass)
}
There are several issues with your current code: (1) You're setting variableToPass in cellForRowAtIndexPath:, so assuming label1.text is the label belonging to the cell, as the table loads, the variableToPass will always contain the label text of the last loaded cell. (2) cellForRowAtIndexPath: can be called multiple times for each cell (for example, as you scroll) so you could be adding multiple gesture recognizers to a single cell.
In order to resolve issue #1, remove the variableToPass variable entirely and instead directly access the gesture's label view. In order to resolve issue #2, I'd recommend adding the gesture recognizer to your custom MainCell table view cell, but if you don't want to do that, at least only add a gesture recognizer if one isn't already there.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MainCell") as! MainCell
if cell.label1.gestureRecognizers?.count == 0 {
cell.label1.userInteractionEnabled = true
let tapLabel = UITapGestureRecognizer(target: self, action: #selector(ViewController.tapCommentPost(_:))) // I assume "tapLabel" was a typo in your original post
cell.label1.addGestureRecognizer(tapLabel)
}
return cell
}
func tapCommentPost(sender:UITapGestureRecognizer) {
print((sender.view as! UILabel).text) // <-- Most important change!
}
I have a list of images within a cell, within a UITableView. For reasons I won't go (too much) into, I can't use didSelectRowAtIndexPath to know which one was selected due to the fact that I am using a third party module that adds its own parent Gestures, and I cannot set cancelsTouchesInView = false (which could technically fix my problem).
In either case, is there a way to add arbitrary info to a view, so that when I receive it in as the sender, I could introspect it.
Eg: if this were HTML & JavaScript, you could do this.
$(myImage).data('foo', 'bar')
$(anotherImage.data('foo', 'thunk')
$('img').on('click', function () {
console.log($(this).data('foo')) // could be "foo" or "thunk"
})
In Swift
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = MyCustomTableViewCell()
cell.userInteractionEnabled = true
let tapped = UITapGestureRecognizer(target: self, action: Selector("myCallback:"))
cell.addGestureRecognizer(tapped)
// my imaginary world...
cell.foo = self.extraData[indexPath.row]
return cell
}
func myCallback(sender: AnyObject?) {
println(sender.foo)
}
Obviously, the above doesn't work, but is there a way to achieve what I'm trying to do?
Although I personally don't recommend using that much but you can make use of objc_setAssociatedObject if you want to attach extra data to objects at runtime.
Here is one good resource about how to do it in Swift:
http://nshipster.com/swift-objc-runtime/
Alternatively, UIView classes have a property named tag to where you can assign indexPath.row for getting the cell that was tapped on later:
cell.tag = indexPath.row
BTW, you better not be working on cells. Instead, always operate on its contentView property when you want to add gesture or another sub view etc.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
...
cell.contentView.userInteractionEnabled = true
// Always remove previously added tap gestures because cells are reused
// as you scroll up and down so you'll end up having multiple
// recognizers on the same cell otherwise.
for recognizer in cell.contentView.gestureRecognizers {
cell.contentView.removeGestureRecognizer(recognizer)
}
cell.contentView.addGestureRecognizer(
UITapGestureRecognizer(target: self, action: "myCallback:"))
cell.contentView.tag = indexPath.row
...
return cell
}
It is fairly straightforward to get the cell in call back function:
(Presuming you have only one section so that indexPath.section = 0)
func myCallback(sender: UIGestureRecognizer) {
let indexPath = NSIndexPath(forRow: sender.view.tag , inSection: 0)
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
print("Cell \(cell) has been tapped.")
}
}
I'd like to know how to get cell number(indexPath.row) in the following tapPickView function. topView is on the UITableViewCell, and pickView is on the topView. If pickView is tapped, tapPickView is activated.
override func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(
"QuestionAndAnswerReuseIdentifier",
forIndexPath: indexPath) as! QuestionAndAnswerTableViewCell
cell.topView.pickView.userInteractionEnabled = true
var tap = UITapGestureRecognizer(target: self, action: "tapPickView")
cell.topView.pickView.addGestureRecognizer(tap)
return cell
}
func tapPickView() {
answerQuestionView = AnswerQuestionView()
answerQuestionView.questionID = Array[/*I wanna put cell number here*/]
self.view.addSubview(answerQuestionView)
}
First of all, you need to append : sign to your selector upon adding gesture recognizer in order for it to get the pickView as its parameter.
var tap = UITapGestureRecognizer(target: self, action: "tapPickView:")
Besides that, cells are reusable objects, so you should prevent adding same gesture again and again to the same view instance by removing previously added ones.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("QuestionAndAnswerReuseIdentifier", forIndexPath: indexPath) as! QuestionAndAnswerTableViewCell
cell.topView.pickView.userInteractionEnabled = true
cell.topView.pickView.tag = indexPath.row
for recognizer in cell.topView.pickView.gestureRecognizers ?? [] {
cell.topView.pickView.removeGestureRecognizer(recognizer)
}
cell.topView.pickView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "tapPickView:"))
return cell
}
While populating the cell, you can set tag value of the pickView as indexPath.row so you can easily query that by cellForRowAtIndexPath(_:).
cell.topView.pickView.tag = indexPath.row
Assuming you already know the section of the cell you tap on. Let's say it is 0.
func tapPickView(recognizer: UIGestureRecognizer) {
let indexPath = NSIndexPath(forRow: recognizer.view.tag, inSection: 0)
if let cell = self.tableView.cellForRowAtIndexPath(indexPath) {
print("You tapped on \(cell)")
}
}
Hope this helps.
Assuming that this was not as simple as didSelectRowAtIndexPath, which I strongly recommend to first look into, passing the information to your method could look like this:
#IBAction func tapPickView:(sender: Anyobject) {
if let cell = sender as? UITableViewCell {
let indexPath = self.tableView.indexPathForCell(cell: cell)
println(indexPath)
}
}
Use didSelectRowAtIndexPath delegate method.