Recognize which View calls the Tap function - ios

I know how to add tappability to the UIImageView, however, there are 2 image views and I want to distinguish them to call the correct function. However, I can't seem to get the correct sender.
func addTappability (view imageView:UIImageView){
//add tapping function for image
let tapGestureRecognizer = UITapGestureRecognizer(target:self, action:#selector(IdCardViewController.imageTapped(_:)))
imageView.isUserInteractionEnabled = true
imageView.addGestureRecognizer(tapGestureRecognizer)
}
func imageTapped(_ sender: UIImageView) {
//Problem here, can't get correct sender
if ( sender == photoImageViewLeft) {
//do one thing
}else {
//do the other
}
}

Replace your function with this:
func imageTapped(_ sender: UITapGestureRecognizer) {
if let imageView = sender.view as? UIImageView {
if ( imageView == photoImageViewLeft ) {
print("Image1 Tapped")
}else {
print("Image2 Tapped")
}
}
}

You need to add tag where you're adding imageViews either in storyboard or in code.
then in your imageTapped() method compare them -
func imageTapped(_ sender: UIImageView) {
//Problem here, can't get correct sender
if ( sender.tag == 1) {
//do one thing
}else if(sender.tag ==2){
//do the other
}
}

Related

Add LongPressGesture to many textfields

I have many textfields to enter values for calculation.
For each textfield I also added a LongPressGestureRecognizer so that I can update my calculations with interim results that I store in the placeholders.
#IBAction func lTap1(_ sender: UILongPressGestureRecognizer) {
if sender.state == .began && lTap1.placeholder!.isNumeric
{
lTap1.text = lTap1.placeholder
Calculation()
}
}
Is there a more convenient way with less code to add the long tap function to each text field instead of repeating the #IBAction function for lTap2, lTap3, etc.?
First assign tag to all Textfields
then declare the array of TextFields there are two ways to declare
#IBOutlet var textFields: [UITextField]!
and
let textFields = [lTap1, lTap2, lTap3,...]
then
#objc func textFeildLongPressed(_ sender: UILongPressGestureRecognizer) {
guard let tag = sender.view?.tag else { return }
guard let textField = textFields[tag] else {
return
}
if sender.state == .began && textField.placeholder!.isNumeric {
textField.text = textField.placeholder
Calculation()
}
}
Now assign gesture to all textFields in viewDidLoad function
textFields.forEach {
let tap = UILongPressGestureRecognizer(target: self, action: #selector(textFeildLongPressed(_:)))
tap.view?.tag = $0.tag
$0.isUserInteractionEnabled = true
$0.addGestureRecognizer(tap)
}
Thanks.

How to disable UILongPressGestureRecognizer on UICollectionViewCell after there is a long press?

Currently, I have a collection view with a UILongPressGestureRecognizer on the cell in cellForItemAt:
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPressOnCell))
cell.addGestureRecognizer(longPress)
When the user holds down on a cell, it triggers a function to show a menu called cellDeleteAppear(). However, after the menu is on the screen, the user can then hold down on another cell which will cause the menu to pop up again.
#objc func handleLongPressOnCell(_ sender: UILongPressGestureRecognizer) {
if sender.state == .began {
cellDeleteAppear()
let gestureLocation = sender.location(in: self.trayCollectionView)
if let indexPath = self.trayCollectionView.indexPathForItem(at: gestureLocation) {
indexPathForDeletion = indexPath
trayCollectionView.allowsSelection = false
} else {
print("long press error at index path")
}
}
}
My goal is: while the menu is active, the user should not be able to hold down on another cell to trigger the menu to pop up. Any help is appreciated!
Then do
var menuShown = false
#objc func handleLongPressOnCell(_ sender: UILongPressGestureRecognizer) {
if sender.state == .began {
guard !menuShown else { return }
menuShown = true
And when you hide it do
menuShown = false

Portrait/Landscape display ios swift 3

I have an application with 2 views.
On the first view, the user enter a login, hits on search button.
The application retrieves through an api information about the login,
is redirected on the second view where it displays information about the login.There is a back button on the second view to go back on the first view to be able to search a different login
In the second view, I treat both cases (Landscape/Portrait)
What happens is the following : first time I enter the login, Portrait and landscape cases are well treated on the result view.
When I go back and search a new login, It will only display the mode in which you entered the second time (if you entered in portrait mode, it will
only display in portrait mode, the same with landscape) Is there something that I should be doing ?
here is the code for the first view :
var login = ""
class FirstViewController: UIViewController {
#IBOutlet weak var Input: UITextField!
#IBAction func sendlogin(_ sender: UIButton) {
if let text = Input.text, !text.isEmpty {
login = text
performSegue(withIdentifier: "callaffiche", sender: self)
return
} else {
return
}
}
here is the code for the second view (I will only post viewDidLoad, the back button creation along its function and the creation of the imageuser as there are many objects. But all of them are created with the same principle ) AfficheElemts calls the creation of every single object( ButtonBackAffiche, ImageAffiche are called within AfficheElements
override func viewDidLoad() {
super.viewDidLoad()
print("ds viewdidload")
let qos = DispatchQoS.userInitiated.qosClass
let queue = DispatchQueue.global(qos: qos)
queue.sync {
self.aut.GetToken(completionHandler: { (hasSucceed) in
if hasSucceed {
print("good")
DispatchQueue.main.async {
self.AfficheElements()
}
} else {
print("bad")
}
})
}
}
func ButtonBackAffiche () {
let button = UIButton()
button.restorationIdentifier = "ReturnToSearch"
button.setTitle("Back", for: .normal)
button.setTitleColor(UIColor.black, for: .normal)
button.isEnabled = true
button.addTarget(self, action: #selector(ReturnToSearch(button:)), for: .touchUpInside)
if UIDevice.current.orientation.isPortrait {
button.frame = CGRect (x:CGFloat(180),y:CGFloat(600),width:50,height:30)
} else {
button.frame = CGRect (x:CGFloat(350),y:CGFloat(360),width:50,height:30)
}
self.view.addSubview(button)
}
func ImageAffiche () {
//Get image (Data) from URL Internet
let strurl = NSURL(string: image_url)!
let dtinternet = NSData(contentsOf:strurl as URL)!
let bongtuyet:UIImageView = UIImageView ()
if UIDevice.current.orientation.isPortrait {
print("portrait ds imageview")
bongtuyet.frame = CGRect (x:10,y:80,width:30,height:30)
} else {
print("landscape ds imageview")
bongtuyet.frame = CGRect (x:CGFloat(200),y:CGFloat(80),width:90,height:120)
}
bongtuyet.image = UIImage(data:dtinternet as Data)
self.view.addSubview(bongtuyet)
}
func ReturnToSearch(button: UIButton) {
performSegue(withIdentifier: "ReturnToSearch", sender: self)
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
print("ds viewwilltransition")
if UIDevice.current.orientation.isPortrait {
print("portrait")
} else {
print("landscape")
}
while let subview = self.view.subviews.last {
subview.removeFromSuperview()
}
AfficheElements()
}
One last thing, I have put traces in the code and the creation of the objects, it goes through the right portion of the code but does not display the objects at the right locations.
Thanks #Spads for taking time to help me.
I finally figured it out. I was not calling the function AfficheElements
int the main queue.
DispatchQueue.main.async {
self.AfficheElements()
}
in viewWillTransition solved it.

How to know the sender's identifier in Swift

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)
}

Get image click inside UI table view cell

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) {
}

Resources