i tried to add gesture recognizer in my UIImageView
let rc = UITapGestureRecognizer(target: self, action: "foo:")
rc.numberOfTapsRequired = 1
rc.numberOfTouchesRequired = 1
cell.bar.tag = indexPath.row
cell.bar.addGestureRecognizer(rc)
but didn't call my foo function
func foo(sender: UIImageView! ) {
self.performSegueWithIdentifier("VC", sender: sender)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "VC" {
let vc = segue.destinationViewController as! VC
vc.item = items[sender.tag]
}
}
So, as I mentioned before your problem is UIImageView by default has property userInteractionEnabled set to false. You can change this in you storyboard, or add line cell.bar.userInteractionEnabled = true.
Next your problem is in your foo: method implementation: you specify sender as UIImageView!, but it should be UITapGestureRecognizer. This is why it crashes - it cannot be UIImageView, so when it unwraps (!) it is nil.
Solution: change your foo method declaration to foo(recognizer: UITapGestureRecognizer). If you need access your imageView inside this method you can use code below:
if let imageView = recognizer.view as? UIImageView {
...
}
or with new guard keyword (Swift 2.0)
guard let imageView = recognizer.view as? UIImageView
else { return }
...
You can change
foo(recognizer: UITapGestureRecognizer) {
}
Related
I am in a quandary about how to position the popup on the supplied screenshot to point to the word tapped. The word in the text view is the one that is grey. I would show some code but I have none that is relevant other than the code that handles the tap.
Here is some code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
if(segue.identifier == "popOverSegue") {
let destinationViewController: PopoverViewController = segue.destination as! PopoverViewController
destinationViewController.modalPresentationStyle = UIModalPresentationStyle.popover
destinationViewController.popoverPresentationController!.delegate = self
destinationViewController.popoverPresentationController?.permittedArrowDirections = .down
destinationViewController.tappedWord = tappedWord
destinationViewController.definitionText = "This is a test description"
let x = meaningText.frame.minX
let y = meaningText.frame.minY
destinationViewController.popupOrigin = CGPoint(x: x + pointOfTap.x,y: y + pointOfTap.y)
destinationViewController.popupSize = CGSize(width: 200, height: 200)
debugPrint(meaningText)
}
}
Not sure if these are applicable:
If you are using a UIButton() for the highlighted text, you could pretty easily get the button's location using something like the following:
let buttonLocation = theButton.frame.origin
print("Buttons location is \(buttonLocation)")
Alternatively, if you're using a UIGestureRecognizer to open the popover, you could use location(in: UIView) to get the tap location (which would be somewhere on/near the highlighted text.
That would go something like this:
override func viewDidLoad() {
let tapView = UITapGestureRecognizer(target: self, action: #selector(TheVC.popover(recognizer:)))
self.view.isUserInteractionEnabled = true
self.view.addGestureRecognizer(tapView)
}
#objc func popover(recognizer: UIGestureRecognizer) {
let tapLocation = recognizer.location(in: self.view)
print("Tap occurred at location \(tapLocation)")
}
Once you have the location of the tap or the UI element that triggers the tap, you could calculate the popover position from there.
My UIImageView is always blank. When the image view is loaded I have it print the image and image view out. This is the output from the print in first section of code: UIImage: 0x170898010>, {4032, 3024}
UIImageView: 0x14bb077e0; frame = (0 64; 375 554); This is the output from the print in second section of code: autoresize = RM+BM; userInteractionEnabled = NO; layer = CALayer: 0x170a31240 It seems to be storing the image but it does not appear.
Here is where the image, which is saved correctly to CloudKit is downloaded and converted:
if let asset = record["Picture"] as? CKAsset,
let data = NSData(contentsOf: asset.fileURL),
let image1 = UIImage(data: data as Data)
{
let eventPageViewController:EventPageViewController = self.storyboard?.instantiateViewController(withIdentifier: "EventPage") as! EventPageViewController
eventPageViewController.toPass = image1
print(image1)
}
Here is the code for the ViewController that displays the UIImageView:
class EventPageViewController: UIViewController {
#IBOutlet weak var eventPic: UIImageView!
var toPass: UIImage!
override func viewDidLoad() {
super.viewDidLoad()
eventPic.image = toPass
print(eventPic)
}
here is where the eventPageViewController appears:
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
let eventPageViewController:EventPageViewController = storyboard?.instantiateViewController(withIdentifier: "EventPage") as! EventPageViewController
self.present(eventPageViewController, animated: true, completion: nil)
}
From Apple docs about image property:
This property is set to the image you specified at initialization time. If you did not use the init(image:) or init(image:highlightedImage:) method to initialize your image view, the initial value of this property is nil.
https://developer.apple.com/reference/uikit/uiimageview/1621069-image
The OP isn't using segues, but I was asked if this would solve things. Here's my description and code that works using a segue.
Baseline:
My app has two view controllers (select and edit) with a segue between them (ShowEditView). That is all I've defined in IB. Everything else is in code.
There is one difference that appears to not be at issue - I use a UIImagePickerController to get the image where the OP pulls it from the assets. This doesn't appear to be at issue because the source VC has the image. So picking it up from here (two VCs with a segue defined in IB, the image in a UIImage instance) here's how I might code things based on the OP's code.
In the source VC:
func imageAttained() {
// here's where you move the image into image1
// this is the segue - make sure you've named it in IB
self.performSegue(withIdentifier: "ShowEventView", sender: self)
}
// this is an override in the source VC and passes the image
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowEventView" {
if let vc = segue.destination as? EventPageViewController {
vc.toPass = image1
}
}
}
And in the destination VC (EventPageViewController):
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
eventPic.image = toPass
}
I have two UILabels with two UITapGestureRecognizers in a UITableViewCell.
cell.Username.tag = indexPath.row
cell.SharedUser.tag = indexPath.row
let tapGestureRecognizer2 = UITapGestureRecognizer(target:self, action:"GoToProfil:")
let tapGestureRecognizer3 = UITapGestureRecognizer(target:self, action:"GoToProfil:")
cell.Username.userInteractionEnabled = true
cell.Username.addGestureRecognizer(tapGestureRecognizer2)
cell.SharedUser.userInteractionEnabled = true
cell.SharedUser.addGestureRecognizer(tapGestureRecognizer3)
func GoToProfil (sender: AnyObject!) {
self.performSegueWithIdentifier("GoToProfilSegue", sender: sender)
}
I'm using a Segue to push another UIViewController, and I'm overriding the PrepareSegue function to send the needed information corresponding to the Sender tag.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
let ProfilView = segue.destinationViewController as! Profil
ProfilView.hidesBottomBarWhenPushed = true
ProfilView.title = posts[sender.view!.tag].User?.objectForKey("Name") as? String
ProfilView.User = posts[sender.view!.tag].User
}
My problem is that I want to know which UILabel was pressed, knowing that I'm already using tag.
Your GoToProfile: function should be written properly. The parameter isn't the "sender", it's the gesture recognizer.
func GoToProfil (gestureRecognizer: UITapGestureRecognizer) {
}
From there, you can determine the label by using the view property of the gesture recognizer.
But you seem to have two conflicting requirements. You want to know which of the two labels was tapped and you want to know which row the label is in.
Normally you would use the label's tag to know which of the two labels was tapped. But you are using their tags to track the row.
The solution I recommend is to use the tag to differentiate the two labels. Then you can calculate the row based on the frame of the label.
See the following answer for sample code that translates the frame of a cell's subview to the cell's indexPath.
Making the following assumptions:
You are trying to uniquely identify the label using UIView.tag
You want different behaviour for Username & SharedUser
I recommend the following, first define your tags below your #imports
#define kUsername 1
#define kSharedUser 2
Then assign them to your views
cell.Username.tag = kUsername
cell.SharedUser.tag = kSharedUser
Then in your prepareSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
int tag = [sender.view!.tag]
if (tag == kUsername) {
//Username logic
} else if(tag == kSharedUser) {
//Shared User Logic
}
}
This way you can easily and simply determine tap, Note this might have different results if you have more then 1 Username & SharedUser labels. Then you will either need more #defines or change how you generate your tags.
You can add a property to UILabel to track the label's type. (I used an enum since there's just 2 cases, but it could be a string, etc.)
enum LabelDest : String
{
case Username = "Username"
case SharedUser = "SharedUser"
}
extension UILabel
{
struct Static {
static var key = "labelDest"
}
var labelDest:LabelDest? {
set { objc_setAssociatedObject( self, &Static.key, newValue?.rawValue, .OBJC_ASSOCIATION_COPY_NONATOMIC )
}
get {
guard let val = objc_getAssociatedObject( self, &Static.key ) as? String else { return nil }
return LabelDest( rawValue:val )
}
}
}
Now you can just do this:
let label = UILabel()
label.labelDest = .Username
Later:
switch label.labelDest
{
case .Some(.Username):
// handle user name
break
...
If you want to use the .tag field on your labels you can use a different technique to find the table row associated with a label: (again using class extensions)
extension UIView
{
var enclosingTableViewCell:UITableViewCell? {
return superview?.enclosingTableViewCell
}
var enclosingTableView:UITableView? {
return superview?.enclosingTableView
}
}
extension UITableViewCell
{
var enclosingTableViewCell:UITableViewCell? {
return self
}
}
extension UITableView
{
var enclosingTableView:UITableView? {
return self
}
}
extension UIView {
var tableRow:Int? {
guard let cell = self.enclosingTableViewCell else { return nil }
return self.enclosingTableView?.indexPathForCell( cell )?.row
}
}
Now, from your gesture recognizer action:
func goToProfil( sender:UIGestureRecognizer! )
{
guard let tappedRow = sender.view?.tableRow else { return }
// handle tap here...
}
You can access the sender data, and read the tag of the object that send you, like in this sample code.
To uniquely identify each row and each label, you can use something like this:
cell.Username.tag = (indexPath.row*2)
cell.SharedUser.tag = (indexPath.row*2)+1
With this, if you have a even tag, its the Username, odd will be the SharedUser. Dividing by the floor of 2 you can have the row back.
#IBOutlet weak var test1: UILabel!
#IBOutlet weak var test2: UILabel!
override func viewWillAppear(animated: Bool) {
test1.tag = 1
test2.tag = 2
test1.userInteractionEnabled = true
test2.userInteractionEnabled = true
self.test1.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "handleSingleTap:"))
self.test2.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "handleSingleTap:"))
}
func handleSingleTap(sender: UITapGestureRecognizer) {
print(sender.view?.tag)
}
I have a Table View with basic label of Table View Cell. I labelled the cell as "January", "February", "March", etc. When user tap on "January", an image with file name "jan.jpeg" will be showed using the following Swift code:
override func viewDidLoad() {
super.viewDidLoad()
let image = UIImage(named: "jan.jpeg")!
imageView = UIImageView(image: image)
imageView.frame = CGRect(origin: CGPointMake(0.0, 0.0), size:image.size)
}
My question is, is it possible when user tap on "February", "feb.jpeg" will be showed, whereas "mar.jpeg will be showed if user tap on "Mar"? How to implement this?
When a cell is selected, the tableView:didSelectRowAtIndexPath: delegate method is called. If you have a predefined list of cells, you can test which cell is being tapped and load the correct image. For example:
have a variable var imgName: String outside of all functions in the viewcontroller with your tableView.
Also put this function in the viewcontroller with your tableView:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 0 {
imgName = "jan.jpeg"
}
else if indexPath.row == 1 {
imgName = "feb.jpeg"
}
else if indexPath.row == 2 {
imgName = "mar.jpeg"
}
else {
// Handle else
}
self.performSegueWithIdentifier("showImageSegue", sender: self)
}
You will need to go to interface builder and name the segue between your viewcontrollers to "showImageSegue".
Additionally, implement the prepareForSegue: function:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
let destinationVC = segue.destinationViewController as ImageViewController // Replace ImageViewController with whatever the name of your destination viewcontroller is.
destinationVC.imageName = imgName
}
Finally, inside the ImageViewController class, add this:
var imageName: String
override func viewDidLoad() {
let image = UIImage(named: imageName)
imageView = UIImageView(image: image)
imageView.frame = CGRect(origin: CGPointMake(0,0, 0,0), size: image.size)
}
I have 77 buttons on one view. These 77 buttons are in a collection outlet. The buttons are wired to trigger the same segue. The segue presents a detailViewController with information passed to it from the button. I need to know what button triggered the segue so that I know what data to pass to the detail controller.
I set the tag in the viewDidLoad method:
override func viewDidLoad() {
super.viewDidLoad()
self.containerView.backgroundColor = UIColor.blackColor()
var count = 0
for item in buttonOutlets {
item.layer.cornerRadius = 2.0
item.layer.borderWidth = 2.0
item.tag = count
item.layer.borderColor = UIColor.yellowColor().CGColor
item.addTarget(self, action: Selector("handleButtonPress"), forControlEvents: UIControlEvents.TouchUpInside)
count = count + 1
println(item.tag) // prints correct tag numbers
}
self.fetchAllObjects()
}
This is my prepareForSegue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var upcoming: itemDetail = segue.destinationViewController as! itemDetail
if (segue.identifier == "loadDetailView") {
println(buttonOutlets[1].tag) // prints correct tag number
let objectPlace = sender?.tag
upcoming.parseObject = collectionObjects[objectPlace!] as? PFObject
}
The answer lies in item.addTarget. The action selector calls handleButtonPress. My original button press handler was:
func handleButtonPress(sender: UIButton) {
self.performSegueWithIdentifier("loadDetailView", sender: self)
}
What I was missing was sender: sender:
func handleButtonPress(sender: UIButton) {
self.performSegueWithIdentifier("loadDetailView", sender: sender)
}
Then add a colon to handleButtonPress call:
item.addTarget(self, action: Selector("handleButtonPress:"), forControlEvents: UIControlEvents.TouchUpInside)