I have a view that added some UIButton.
given a CGPoint how can I get the button laid on.
sample code
let location = tapRecognizer.location(in: view)
let tapView = view.hitTest(location, with: nil)
the code below won't work how can I get the UIbutton form tapView
if let button = tapView as? UIButton {
print("text")
}
help appreciated
While adding button to the view add target to the button and assign a different tag to each button and perform action according to the tag
For the this purpose you can use the gesture delegate like
Example:-
func addGestureOnContentView () -> Void {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ExploreViewController.hideTableView))
tapGesture.delegate = self
self.view.addGestureRecognizer(tapGesture)
}
Now use gesture delegate like :-
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if gestureRecognizer is UITapGestureRecognizer {
if (touch.view?.isDescendant(of: UIButton))! {
return false //Perform your task in this block
}
}
return true
}
Related
I have one main view and 4 subviews of the mainview they all have their UITapGestureRecognizer, when I tap on one subview how can it be triggered both views. Example below,
if I tap to subview 1 desired log would be:
subview1 is clicked
MainView is clicked
My Code
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let mainGesture = UITapGestureRecognizer(target: self, action: #selector(mainGestureActivated))
self.view.addGestureRecognizer(mainGesture)
let subGesture = UITapGestureRecognizer(target: self, action: #selector(subViewGestureActivated))
self.subview1.addGestureRecognizer(subGesture)
}
#objc func mainGestureActivated(){
print("MainView Clicked")
}
#objc func subViewGestureActivated(){
print("Subview Clicked")
}
it prints only subview clicked! Is it possible to trigger both gestureRecognizers since main is encapsulating other.
First you should conform to UIGestureRecognizerDelegate in your VC, and then implement the delegate func of shouldRecognizeSimultaneouslyWith. Inside the function, you should detect if the gestureRecognizer, and the otherGestureRecognizer are the wants you want, and if so, you should allow them to work simultaneously,
Conform to delegate, and Declare gesture recognizers outside of viewDidLoad (because you need to access them in the delegate method later.)
var mainGestureRecognizer = UITapGestureRecognizer()
var subGestureRecognizer = UITapGestureRecognizer()
Initialize your recognizers, and set your VC as their delegate:
mainGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(mainGestureActivated))
subGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(subViewGestureActivated))
mainGestureRecognizer.delegate = self
subGestureRecognizer.delegate = self
Implement the delegate function mentioned above to allow simultaneous recognition for subView and mainView:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer == subGestureRecognizer && otherGestureRecognizer == mainGestureRecognizer {
return true
}
return false
}
And if you want it to work for 4 different subviews, then you should check with else if statements inside the delegate method.
So I am trying to create an interface in which there are essentially 3 levels of views possible.
The UIView in charge of deciding the meta-data of new cells
The cells themselves
The background UIScrollView
Everything is a child of the UIScrollView however 1 is displayed on top of 2.
The goal is that all the cells are draggable and the scroll view is scrollable. However when the scroll view is tapped that should trigger the creation of a new node and thus the appearance of view 1 which will disappear upon the press of a submit button.
I have set up just about everything except I have one huge problem. If I tap on a cell I still get the creation view meaning that the TapGestureRecognizer isnt excluding subviews. So here is roughly what is set up:
class Node: UILabel {
var recognizer:UIPanGestureRecognizer?
init() {
self.isExclusiveTouch = true
self.isUserInteractionEnabled = true
recognizer = UIPanGestureRecognizer(target: self, action: #selector(drag))
self.addGestureRecognizer(recognizer!)
}
#objc
func drag(rec: UIPanGestureRecognizer) {
goToPoint(point: rec.location(in: self.superview!))
}
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
var shouldReceive = false
if let clickedView = touch.view {
if clickedView == self {
shouldReceive = true;
}
}
return shouldReceive
}
}
class NodeCreator: UIView {
var submit: UIButton = UIButton(type: UIButton.ButtonType.roundedRect)
init() {
super.init(frame: CGRect.zero)
submit.setTitle("Submit", for: .normal)
submit.translatesAutoresizingMaskIntoConstraints = false
submit.addTarget(self, action: #selector(press), for: .touchUpInside)
self.addSubview(submit)
}
#objc func press() {
if let d = delegate {
if let t = inputField.text {
d.created(title: t, pt: storeP)
self.removeFromSuperview()
}
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class TaskView: UIScrollView, CreationDelecate{
func created(title: String, pt: CGPoint) {
let temp = Node()
view.addNode(temp, at: pt)
}
var tapRecognizer:UITapGestureRecognizer?
var nodeSet:[Node] = []
func addNode(_ n1: Node, at: CGPoint = CGPoint.zero) {
nodeSet.append(n1)
n1.goToPoint(point: at)
self.addSubview(n1)
self.bringSubviewToFront(n1)
self.isScrollEnabled = true
self.isUserInteractionEnabled = true
self.isExclusiveTouch = false
self.canCancelContentTouches = false
if (tapRecognizer == nil) {
tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(tap))
self.addGestureRecognizer(tapRecognizer!)
}
}
#objc func tap(rec: UITapGestureRecognizer) {
let pos = rec.location(in: self)
let creator = NodeCreator(pt: pos)
creator.delegate = self
self.addSubview(creator)
}
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
var shouldReceive = false
if let clickedView = touch.view {
if clickedView == self {
shouldReceive = true;
}
}
return shouldReceive
}
}
Why is the TaskView still receiving tap gestures when the Nodes are all exclusive touch.
Code does not work because you did not assigned delegate for gesture recognizers. Delegate functions are implemented in Node and TaskView classes. But you did not declared classes as delegates i.e.
class TaskView: UIScrollView, CreationDelecate, UIGestureRecognizerDelegate
Than assign delegate to gesture recognizer:
recognizer.delegate = self as! UIGestureRecognizerDelegate
Should work
I want to add a gesture recognizer to one of my views to detect taps,
here is my code:
class DateTimeContainer: UIView, UIGestureRecognizerDelegate {
override func awakeFromNib() {
let gesture = UITapGestureRecognizer(target: self, action: #selector(self.onTap))
gesture.delegate = self
self.addGestureRecognizer(gesture)
}
func onTap(_ gestureRecognizer: UITapGestureRecognizer) {
openDatePicker()
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if touch.view?.tag != self.datePickerTag && !self.isDatePickerOpen() {
return true
}
return false
}
}
When I tap on my view the code enters into the gestureRecognizer shouldReceive method and enters the condition for which it returns true.
But the onTap method is never called, can someone tell me why?
EDIT
Adding self.isUserInteractionEnabled = true I managed to get it working.
But it had a strange behaviour: it was like it received the tap only in the main view and not in subviews.
So to make it simple I solved by adding a button inside my view and by using:
self.button?.addTarget(self, action: #selector(onTap), for: .touchUpInside)
You may have forgotten to do this:
self.isUserInteractionEnabled = true
Also, typically the UIViewController subclass is usually the target and contains the method used for the tap gesture.
You don't need to set a delegate for a tap gesture (usually)
You can You Gesture
let recognizer = UITapGestureRecognizer(target: self,action:#selector(self.handleTap(recognizer:)))
userImage.isUserInteractionEnabled = true
recognizer.delegate = self as? UIGestureRecognizerDelegate
userImage.addGestureRecognizer(recognizer)
And method call selector
func handleTap(recognizer:UITapGestureRecognizer) {
// do your coding here on method
}
Try Writing the codes
let gesture = UITapGestureRecognizer(target: self, action: #selector(self.onTap))
gesture.delegate = self
self.addGestureRecognizer(gesture)
inside init block not in awakeFromNib()
I noticed that you are adding your gesture on a UIView. As such, at that ViewController where you have added this ContainerView, inside the viewDidLoad method, add this line of code.
override func viewDidLoad() {
super.viewDidLoad()
self.YourContainerView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(YourCurrentViewController.openDatePicker())))
self.YourContainerView.isUserInteractionEnabled = true
}
Note. Do not forget to add isUserInteractionEnabled to true.
I am using this library for SearchText Field
https://github.com/apasccon/SearchTextField
Here is my Code :
override func viewDidLoad() {
super.viewDidLoad()
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: Selector("dismissKeyboard")))
}
func dismissKeyboard()
{
ClientList.resignFirstResponder()
self.view.endEditing(true)
}
Dismiss of keyboard is working but the didSelectRowAtIndexPath is not getting called so i am not able to select anything
Image for Reference :
Partial Solution I Done:
func hideKeyboard(gestureRecognizer: UIGestureRecognizer) {
let point = gestureRecognizer.locationInView(ClientList.tableView)
let indexPath = ClientList.tableView!.indexPathForRowAtPoint(point)
if indexPath != nil {
return
}
if ClientList.becomeFirstResponder() {
ClientList.resignFirstResponder()
}
}
But now issue is i have other list too , how can i implement for other list ?
You can solve this issue like this
ViewDidLoad
self.tap = UITapGestureRecognizer(target: self, action: "viewTapped:")
self.tap.delegate = self
self.view.addGestureRecognizer(self.tap)
Function
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if touch.view != nil && touch.view!.isDescendantOfView(self.tableView) {
return false
}
return true
}
Who's the target of the gesture? I think it should be the list since it is what you tap and it is above the view
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "dismissKeyboard")
view.addGestureRecognizer(tap)
Add this to viewDidLoad
And add this function
func dismissKeyboard() {
//Causes the view (or one of its embedded text fields) to resign the first responder status.
view.endEditing(true)
}
In my ViewController I have a UIScreenEdgeGestureRecognizer for switching views but also a UITableView with a custom UITableViewCell. In this custom UITableViewCell is a UIPanGestureRecognizer for swiping the cells.
I added gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: so both of the gestures are working and gestureRecognizerShouldBegin to prevent the conflict of vertical scrolling.
The question is, how can I give the UIScreenEdgeGestureRecognizer priority? When I swipe at the edge of the screen, I want to switch the views without panning the cells. I figured I should be using the delegate methods, but so far the more I read the more confused I'm getting.
Code in custom UITableViewCell:
override func viewDidload() {
var recognizer = UIPanGestureRecognizer(target: self, action: "handlePan:")
recognizer.delegate = self
addGestureRecognizer(recognizer)
}
override func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
override func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
if let panGesture = gestureRecognizer as? UIPanGestureRecognizer {
let velocity = panGesture.velocityInView(superview!)
if fabs(velocity.x) > fabs(velocity.y) { return true }
return false
}
return false
}
ViewController:
override func viewDidLoad() {
let rightScreenEdgeRecognizer = EdgePanGesture(target: self, action: "changeView:")
rightScreenEdgeRecognizer.edges = .Right
view.addGestureRecognizer(rightScreenEdgeRecognizer)
}
I also tried identifying the recognizers with:
gestureRecognizer.isKindOfClass(UIScreenEdgePanGestureRecognizer)
and
gestureRecognizer as? UIScreenEdgePanGestureRecognizer
But all were failed.
Turns out I was looking at the right direction with gestureRecognizer as? UIScreenEdgePanGestureRecognizer but instead of gestureRecognizer I should have used otherGestureRecognizer.
override func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOfGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if let recognizer = otherGestureRecognizer as? UIScreenEdgePanGestureRecognizer {
return true
}
return false
}