I have a tap gesture on a UIImageView within a class that extends UITableViewCell. This code should work, I don't see why it doesn't. The only thing I am iffy on is what the "target" should be - should it be the profileImage, or the overall ViewController that things are in?
#IBOutlet weak var profileImage: UIImageView!
var vc: TweetsViewController? = nil
override func awakeFromNib() {
super.awakeFromNib()
let tapGester = UITapGestureRecognizer(target: vc, action: Selector("handleTapGester:"))
tapGester.delegate = self
profileImage.addGestureRecognizer(tapGester)
}
func handleTapGester(tapGesture: UITapGestureRecognizer) {
print("*******hi*******")
vc?.performSegueWithIdentifier("showProfile", sender: nil)
}
And for the record, as this may seem like a relevant error, I initialize vc when the table cell loads.
The target should be the object that will handle the tap gesture and the handleTapGester function should be inside the object class you specified as the target, not inside the UITableViewCell subclass.
You also need to enable user interaction on the UIImageView by saying:
imageView.userInteractionEnabled = true
Why not just add a tap gesture recogniser to the view, then when called query indexPathForRowAtPoint to find out which cell is being tapped?
If you know the cell you can then determine if the UIImage is being tapped and make your call to performSegueWithIdentifier from there.
If it's tapped on a cell that you're not interested in let it fall through and be handled by the table by calling cancelsTouchesInView on the recogniser.
Related
We want Lyft button touch event because I am working in analytics, so, I need how many people choose Lyft but I can't put UIView click event. I try below code.
let gesture = UITapGestureRecognizer(target: self, action: #selector(self.checkAction))
cell.lyftButton.addGestureRecognizer(gesture)
How can i achieve this?
You can directly assign a selector method to lyftButton e.g
lyftButton.addTarget(self, action: #selector(lyftButtonAction(_:)), for: .touchUpInside)
#objc
func lyftButtonAction(_sender: UIButton) {
//Do your action
}
To retrieve the LyftButton, you'll need to fetch the button inside the Lyft view, after retrieving it, I tried to add another target to it which was your 'checkAction' method, but for some reason it is not being called. One workaround solution is:
On Auto Layout, created a transparent button on top of the Lyft Button View, let's callet it 'Transparent Lyft Button': Example (I've embeded in another view because it was on a stackView);
On the code, retrieved the button with the above method, held it in a variable, let's call it 'requestLyftButton' and disabled it.
Created an IBAction for the 'Transparent Lyft Button' that triggers the method 'self.checkAction' that you've created and also calls requestLyftButton.sendActions(for: .touchUpInside), which triggers the original Lyft SDK action.
To Retrieve Lyft UIButton:
#IBOutlet weak var lyftButton: LyftButton!
#IBOutlet weak var transparentLyftButton: UIButton!
var requestLyftButton: UIButton?
func retrieveLyftButton(in view: UIView) {
for view in view.subviews {
if let lyftBtn = view as? UIButton {
lyftBtn.isEnabled = false
requestLyftButton = lyftBtn
} else {
retrieveLyftBtn(in: view)
}
}
}
transparentLyftButton IBAction to trigger your method + lyft sdk original action:
#IBAction func requestLyft(_ sender: UIButton) {
if let lyftBtn = requestLyftButton {
checkAction() // Your method
lyftBtn.sendActions(for: .touchUpInside)
}
}
I hope that you can understand what was done, if you have any questions, just let me know.
In a collection view, I create a gesture recognizer at class init time. In the viewDidLoad method, I then add the gesture recognizer to the collection view.
class ViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongGesture(gesture:)))
#objc func handleLongGesture(gesture: UILongPressGestureRecognizer) {
// some code
}
override func viewDidLoad() {
super.viewDidLoad()
collectionView.addGestureRecognizer(longPressGesture)
}
}
With this, the gesture recognizer does not work.
The fix is easy: it suffices to move the line with let longPressGesture to the viewDidLoad method and everything works as expected. However, I find it a bit surprising that the first version would not work.
Can anyone explain why the first version is not working? Is it because, when the gesture recognizer is created, the collection view is not yet ready to have gestures? So, what must a gesture recognizer know about its target in order to be created?
Good question. That's because you are trying to use self when not fully initialized.
Now, how to make that work with the way you wanted? Perhaps declare it lazily, like so:
private lazy var longPressGesture: UILongPressGestureRecognizer! = {
let gesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongGesture(gesture:)))
return gesture
}()
Edit: Quoting giorashc's answer from this question:
Due to swift's 2-phase initialization you need to initialize the
parent class before you can use self in the inheriting class.
In your implementation self is yet to be initialized by the parent
class so as you said you should move it to the init method of your
view controller and create the button after calling the parent's
initialization method
2 Phase Initialization SO Question & Answer.
I see that there are a ton of these questions, and I think I'm following the accepted Swift 3 methodology, but I'm still getting nothing. I can see that the UITapGestureRecognizer has been attached. Here's my code:
let tileClick = UITapGestureRecognizer(target: self, action: #selector(GameManagement.initiateTileClick(_:)))
newView.addGestureRecognizer(tileClick)
newView.isUserInteractionEnabled = true
func initiateTileClick(_ sender: UITapGestureRecognizer) {
print("initiate tile click")
}
A few things to note:
1) The view that I'm attaching the gesture recognizer to has a two views and a label within it that each cover the entire frame of the view, however, I tried attaching the recognizer to the label, which is the topmost child item and it still doesn't work.
2) Both the function that adds the recognizer and the function that is called on the tap are contained in an NSObject file. I have a variety of interconnected functions that I want to be able to call from multiple view controllers and would prefer to keep this in the separate NSObject file. The process worked when I had everything in a UIViewController file and stopped working when I moved the functions to the NSObject file.
3) I've tried changing GameManagement.initiateTileClick to self.initiateTileClick or just initiateTileClick and none of those worked.
If you are putting your views inside NSObject subclass then these views will lose their behaviors for UIResponder which manages the UI interactions as I am not able to see how you are adding these views to interface.
As you said, it was working inside ViewController because it manages view hierarchy and responder chain.
The solution would be to write extensions to separate code or better abstractions.
extension YourViewController {
newView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(GameManagement.initiateTileClick(_:))))
newView.isUserInteractionEnabled = true
func initiateTileClick(_ sender: UITapGestureRecognizer) {
print("initiate tile click")
}
}
Giving you an idea how the tap recogniser works.
Firstly add Tap gesture recogniser to your view controller. You have to put the object here as shown in the image.
Then control+drag the tap gesture object to your view and select delegate.
Then control+drag the recogniser to your swift file and action will be like this.
#IBAction func tapGesture(_ sender: UITapGestureRecognizer) {
}
Now you must have seen when you give some input to a text field, the keyboard appears. But if you press outside the text field, that is anywhere in the view, the keyboard hides. This is because of the tap gesture recogniser.
Consider you have a text field such that if you click in that text field, keyboard is appeared. But when you tap outside the textfield, the keyboard must hide.
Add this delegate
UITextFieldDelegate
Implement this:
#IBOutlet var phoneText: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
exampleText.delegate = self
}
#IBAction func tapGesture(_ sender: UITapGestureRecognizer) {
exampleText.endEditing(true)
}
Obviously,this function is instance method.
func initiateTileClick(_ sender: UITapGestureRecognizer) {
print("initiate tile click")
}
-
UITapGestureRecognizer(target: self, action:#selector(GameManagement.initiateTileClick(_:)))
but thisGameManagement.initiateTileClick(_:) looks like a class is calling a class method!The target should be the caller of method.self can't call GameManagement.initiateTileClick(_:).
I am new to swift, please kindly advise the best way to achieve this.
Let say if there are 3 to 4 (custom) UIView(s) added under a parent view
when user select/highlighted a particular one (e.g. the 2nd UIView), and this will get removed and the whole layout will re-render immediately. Any idea?
Connect all views to one IBOutletCollection, add gesture recognizer for tap and in recognizer callback just get the touch point and check if the point is contained in one of the views from the outlet collection.
#IBOutlet var views: [UIView]!
override func viewDidLoad() {
super.viewDidLoad()
let tapGesture = UITapGestureRecognizer(target: self, action: Selector("viewTapped:"))
self.view.addGestureRecognizer(tapGesture)
}
func viewTapped(tapGesture: UITapGestureRecognizer) {
let locationInView = tapGesture.locationInView(view)
for v in views {
if CGRectContainsPoint(v.frame, locationInView) {
v.removeFromSuperview()
}
}
}
Make sure you have your autolayout setup for the state in which each of the view is not there.
I have a webview inside view controller "A", is it possible to tap on web view and seaque to view controller "B"?
I have tried do following
#IBOutlet weak var mainWebView: UIWebView!
let tapUIWebView = UITapGestureRecognizer()
override func viewDidLoad() {
super.viewDidLoad()
tapUIWebView.addTarget(self, action: "webViewTapped")
mainWebView.userInteractionEnabled = true
mainWebView.addGestureRecognizer(tapUIWebView)
func webViewTapped(){
println("Tapped")
}
}
Thank you
1) Use a UITapGestureRecognizer, create the instance.
2) Attach it to the webview.
3) Implement the action method that handles the gesture.
4) SinceUIWebView already recognises a touch, you need to also take care that your recognizer does the job first. And you decide what to do with the touch.
5) If it suits you, you can disable handling the tap in the built-in recogniser completely.