I'm working on a UITableView.
Each cell has got a series of UIImageViews, and their urls are saved into an array.
When I tap on a certain image, the selector for my gesture recogniser should get both the tag of the image and the indexPath.row of the cell.
I'm currently getting the image tag from the sender parameters, and this is the way in which I'm trying to get the indexpath:
let point = sender.locationInView(self.tableView)
let indexPathRow = self.tableView.indexPathForRowAtPoint(point)?.row
However, this looks like it's not giving me the right row.
Is there an easy way to pass these two parameters to my gesture selector? Thanks.
You could subclass the gesture recogniser you're using and add the extra variables you want. Example below.
class CustomGestureRecognizer : UITapGestureRecognizer {
var url : NSURL?
// any more custom variables here
init(target: AnyObject?, action: Selector, url : NSURL) {
super.init(target: target, action: action)
self.url = url
}
}
Then when you want to get the url back out.
func didTap(sender : CustomGestureRecognizer) {
print(sender.url)
}
Simplest way I believe is to subclass the UIImageView to hold your NSIndexPath:
class MyImageView: UIImageView {
var indexpath:NSIndexPath!
}
And then to get that indexPath:
func tap(tap: UITapgestureRecognizer) {
let imgView = tap.view as! MyImageView
let indexPath = imgView.indexPath
let tag = imgView.tag
}
Related
I have an array with separate UIImages and I am displaying them using an UIImageView, how do I add an identifier to them so that I can switch to the view controller with the specific data related to that image.
Below is the code:
class Orders2ViewController: UIViewController, OrdersBaseCoordinated {
var orderList: [OrderInfo] = [OrderInfo( itemImage: UIImage(named: "printer.jpeg")!, itemSKU: 12567), OrderInfo(itemImage: UIImage(named: "ipad.jpeg")!, itemSKU: 34521), OrderInfo( itemImage: UIImage(named: "hoodie.jpeg")!, itemSKU: 93620)]
lazy var someImageView: UIImageView = {
let theImageView = UIImageView()
let tap = UITapGestureRecognizer(target: self, action: #selector(Orders2ViewController.tappedMe))
theImageView.addGestureRecognizer(tap)
theImageView.isUserInteractionEnabled = true
theImageView.image = orderList[selectedIndex].itemImage
theImageView.translatesAutoresizingMaskIntoConstraints = false
return theImageView
}()
#objc func tappedMe(sender: UITapGestureRecognizer)
{
coordinator?.passImageData(j: )
}
I want to pass an identifier in my tappedMe function to recognize which image was tapped on, I did find other answers on SO that mentioned gesture.view.tag but I don't want to create another view, rather navigate to the next controller with an identifier. Is there a way to do this?
Since UIImageView inherits from UIView, you can use it’s parameter called tag. In your example you can set theImageView.tag = orderedList[selectedIndex].itemSKU. Then each time tap was recognized, you can just use this itemSKU to do move to the next screen.
However it seems that you have only one UIImageView on the screen and you use selectedIndex to determine which image you need. So you can just do like that:
#objc func tappedMe(sender: UITapGestureRecognizer) {
coordinator?.passImageData(j: orderList[selectedIndex].itemImage)
}
I passed the itemImage just to show you how it can be done. You can use whatever you need.
I'm using BulletinBoard (BLTNBoard) to create dialogs in my iOS app. There's an option to embed image inside it. I would like to extend it's functionality and allow user to manipulate this image using tap gesture. But eventually when I assign a gesture to it's imageView using addGestureRecognizer nothing happens.
Here's how I initiliaze bulletin and add gesture to the image:
class ViewController: UIViewController {
lazy var bulletinManager: BLTNItemManager = {
let rootItem: BLTNPageItem = BLTNPageItem(title: "")
return BLTNItemManager(rootItem: rootItem)
}()
override func viewDidLoad() {
//etc code
let bulletinManager: BLTNItemManager = {
let item = BLTNPageItem(title: "Welcome")
item.descriptionText = "Pleas welcome to my app"
item.actionButtonTitle = "Go"
item.alternativeButtonTitle = "Try to tap here"
item.requiresCloseButton = false
item.isDismissable = false
item.actionHandler = { item in
self.bulletinManager.dismissBulletin()
}
item.alternativeHandler = { item in
//do nothing by now
}
//
item.image = UIImage(named: "welcome")
//adding gesture to its imageView
item.imageView?.isUserInteractionEnabled=true
let tap = UITapGestureRecognizer(target: self, action: Selector("tapTap:"))
item.imageView?.addGestureRecognizer(tap)
return BLTNItemManager(rootItem: item)
}()
}
#objc func tapTap(gestureRecognizer: UITapGestureRecognizer) {
print("TAPTAP!!!!!!")
}
}
and nothing happens at all (no message printed in console).
However if I assign action inside alternative button it works as expected:
item.alternativeHandler = { item in
item.imageView?.isUserInteractionEnabled=true
let tap = UITapGestureRecognizer(target: self, action: Selector("tapTap:"))
item.imageView?.addGestureRecognizer(tap)
}
I guess the only thing which can prevent me to assign the tap event to it properly is that imageView becomes available much later than the bulletin is created (for example only when it is shown on the screen).
Could you please help and correct my code. Thanks
upd.
Ok, based on Philipp's answer I have the following solution:
class myPageItem: BLTNPageItem {
override func makeContentViews(with interfaceBuilder: BLTNInterfaceBuilder) -> [UIView] {
let contentViews = super.makeContentViews(with: interfaceBuilder)
let imageView=super.imageView
imageView?.isUserInteractionEnabled=true
let tap = UITapGestureRecognizer(target: self, action: #selector(tapTap))
imageView?.addGestureRecognizer(tap)
return contentViews
}
#objc func tapTap(gestureRecognizer: UITapGestureRecognizer) {
print("TAPTAP!!!!!!")
}
}
When you're working with an open source library, it's easy to check out the source code to find the answer.
As you can see here, image setter doesn't initiate the image view.
Both makeContentViews makeArrangedSubviews (which are responsible for views initializing) doesn't have any finish notification callbacks.
Usually in such cases I had to fork the repo and add functionality by myself - then I'll make a pull request if I think this functionality may be needed by someone else.
But luckily for you the BLTNPageItem is marked open, so you can just subclass it. Override makeContentViews and add your logic there, something like this:
class YourOwnPageItem: BLTNPageItem {
override func makeContentViews(with interfaceBuilder: BLTNInterfaceBuilder) -> [UIView] {
let contentViews = super.makeContentViews(with: interfaceBuilder)
// configure the imageView here
return contentViews
}
}
I am new in swift and I want to get the value of label from tableview on button click
I am using code like this but it is getting crash
in cellforrowatindexpath
cell.btnsubmit.tag = indexPath.row
cell.btnsubmit.addTarget(self, action: #selector(buttonSelected), for: .touchUpInside)
#objc func buttonSelected(sender: UIButton){
print(sender.tag)
let cell = sender.superview?.superview as! PatientUpdateVCCell
surgery_date = cell.surgeryDateTextField.text!
discharge_date = cell.dischargeDateTextField.text!
follow_up_duration = cell.lblfolowup.text!
follow_up_date = cell.firstFollowUpTextField.text!
patient_status = cell.patientStatusTextView.text!
}
but it is getting crash. How can I achieve this
crash
Could not cast value of type 'UITableViewCellContentView' (0x11a794af0) to 'appname.PatientUpdateVCCell' (0x10ae74ae0).
According to your crash last superView is contentView then it's superView is the needed cell , so You need
let cell = sender.superview!.superview!.superview as! PatientUpdateVCCell
Target/action is pretty objective-c-ish. And view hierarchy math is pretty cumbersome.
A swiftier way is a callback closure which is called in the cell and passes the cell.
In the cell add a callback property and an IBAction. Connect the action to the button
var callback : ((UITableViewCell) -> Void)?
#IBAction func buttonSelected(_ sender: UIButton) {
callback?(self)
}
In cellForRow rather than the tag assign the closure
cell.callback = { currentCell in
self.surgery_date = currentCell.surgeryDateTextField.text!
self.discharge_date = currentCell.dischargeDateTextField.text!
self.follow_up_duration = currentCell.lblfolowup.text!
self.follow_up_date = currentCell.firstFollowUpTextField.text!
self.patient_status = currentCell.patientStatusTextView.text!
}
And delete the action method in the controller
I have a UICollectionViewcell in my code
let cell = commentSection.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! CommentCell
And in CommentCell i have got this imageview
let likeIcon: UIImageView = {
let imv = UIImageView()
imv.image = #imageLiteral(resourceName: "like")
imv.translatesAutoresizingMaskIntoConstraints = false
return imv
}()
I tried adding a tap gesture to likeIcon in CommentCell and also in cellForItemAt but none of them triggered it when clicking.How can i add gestureTapRecognizer to child element of cell?
Adding UIImageView and providing gesture recognizer will increase the complexity of your development.
If you need action then use UIButton and set target action for button in cellForRowAtIndex
You can set either background image or image to UIButton.
Better keep processed image in UIImage object and assign it to UIButton.
Look at the code:
....
let imageView = UIImageView()
imageView.isUserInteractionEnabled = true
let recognizer = UITapGestureRecognizer()
recognizer.addTarget(self, action: #selector(yourHandleMethod(tapGestureRecognizer:)))
imageView.addGestureRecognizer(recognizer)
....
func yourHandleMethod(tapGestureRecognizer: UITapGestureRecognizer) {
print("tap")
}
Call this method update() outside in cellForItem (for every cell).
...
cell.update()
...
// This method inside cell
func update() {
let recognizer = UITapGestureRecognizer()
recognizer.addTarget(self, action: #selector(yourHandleMethod(tapGestureRecognizer:)))
imageView.addGestureRecognizer(recognizer)
}
Im suffering from sending two tags to a function to show UIActivityViewController and share the cell data, well before i used to send one value at a time and within a single UIButton:
cell.sharefb.tag = indexPath.row
cell.sharefb.addTarget(self, action: "showAlert:", forControlEvents:UIControlEvents.TouchUpInside)
But now I've implemented sections in my UITableView so my array is like :
Array[indexPath.section][indexPath.row]
One of them (section or row) is not enough at a time, i need to send both to my function ? how can i do that ?
func showAlert(sender:AnyObject){
// i want to use it like :
Array[sender.sectionvalue][sender.rowvalue]
}
You could subClass UIButton and add properties for the data you need eg
class MyButton : UIButton {
var row : Int?
var section : Int?
}
you can then set those properties, and in your showAlert function you can get them back and use:
func showAlert(sender:AnyObject){
let theButton = sender as! MyButton
let section = theButton.section
let row = theButton.row
}
Edit: Added where to set the button as per comment requested:
In your StoryBoard, make sure that your button is of type MyButton (and not UIButton anymore).
and then where you used to set the tag, don't use the tag but set the properties. So replace this code :
cell.sharefb.tag = indexPath.row
cell.sharefb.addTarget(self, action: "showAlert:", forControlEvents:UIControlEvents.TouchUpInside)
with:
cell.sharefb.row = indexPath.row
cell.sharefb.section = indexPath.section
cell.sharefb.addTarget(self, action: "showAlert:", forControlEvents:UIControlEvents.TouchUpInside)