Adding Uigesturerecognizer to Subview Programmatically in Swift

Adding UIGestureRecognizer To Subview Programmatically in Swift

I tested your code here and it does work. However, I think you might be missing to add the UIGestureRecognizerDelegate protocol to your View Controller. See below:

class ViewController: UIViewController, UIGestureRecognizerDelegate {

var architectView = UIView()

override func viewDidLoad() {
super.viewDidLoad()
architectView = UIView(frame: self.view.bounds)
self.view.addSubview(architectView)
let gestureRecognizer = UITapGestureRecognizer(target: self, action: "handleTap:")
gestureRecognizer.delegate = self
architectView.addGestureRecognizer(gestureRecognizer)
}

func handleTap(gestureRecognizer: UIGestureRecognizer) {
let alert = UIAlertController(title: "Alert", message: "Message", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

}

How to implement a tap gesture recognizer for multiple subviews with Swift

I resolved my own issue by defining 4 separate UITapGestureRecognizer() variables as shown below and then adding the appropriate gesture recognizer to the appropriate view.

//Vars
let tapRecForHistoryView = UITapGestureRecognizer()
let tapRecForMissionView = UITapGestureRecognizer()
let tapRecForServiceAreaView = UITapGestureRecognizer()
let tapRecForServiceAreaMapView = UITapGestureRecognizer()

override func viewDidLoad() {
super.viewDidLoad()
//Tap gesture
tapRecForHistoryView.addTarget(self, action: "tappedView")
tapRecForMissionView.addTarget(self, action: "tappedView")
tapRecForServiceAreaView.addTarget(self, action: "tappedView")
tapRecForServiceAreaMapView.addTarget(self, action: "tappedView")
historyViewWrapper.addGestureRecognizer(tapRecForHistoryView)
historyViewWrapper.userInteractionEnabled = true
missionViewWrapper.addGestureRecognizer(tapRecForMissionView)
missionViewWrapper.userInteractionEnabled = true
serviceAreaViewWrapper.addGestureRecognizer(tapRecForServiceAreaView)
serviceAreaViewWrapper.userInteractionEnabled = true
serviceAreaMapWrapper.addGestureRecognizer(tapRecForServiceAreaMapView)
serviceAreaMapWrapper.userInteractionEnabled = true
}

//--------------------------------------------------------
// MARK: Local Methods
//--------------------------------------------------------
func tappedView(){
let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
}

I am now able to tap each individual view and the orientation will programmatically rotate from Portrait to Landscape.

Swift - How to add tap gesture to array of UIViews?

You need

@objc func tapCard (sender: UITapGestureRecognizer) { 
let clickedView = cardView[sender.view!.tag]
print("View tapped !" , clickedView )
}

No need to check state here as the method with this gesture type is called only once , also every view should have a separate tap so create it inside the for - loop

for index in cardView.indices  { 
let tap = UITapGestureRecognizer(target: self, action: #selector(tapCard(sender: )))

add tap gesture to a UILabel Programmatically

You're doing a couple things wrong...

// your code
label.frame.size.width = self.otherlinksStack.bounds.width
label.sizeToFit()

If you're adding the label to a stackView, there is no need to set its frame -- let the stack view handle that. If your stackView's alignment is set to .fill it will stretch the label to its width anyway. If it's not set to fill, the label will expand horizontally as needed, based on its text. So, also, no need to call .sizeToFit().

// your code
self.otherlinksStack.addSubview(label)

When add a view to a stack view, use .addArrangedSubview, otherwise it will be added overlaid on top of another view in the stack view.

This should work fine (it does in my quick test):

func createLabel() {

let label = UILabel()

label.text = "abc"
label.numberOfLines = 0
label.font = label.font.withSize(17) // my UIFont extension
label.tag = 1

// give the label a background color so we can see it
label.backgroundColor = .cyan

// enable user interaction on the label
label.isUserInteractionEnabled = true

// add the label as an Arranged Subview to the stack view
self.otherlinksStack.addArrangedSubview(label)

// create the gesture recognizer
let labelTapGesture = UITapGestureRecognizer(target:self,action:#selector(self.doSomethingOnTap))

// add it to the label
label.addGestureRecognizer(labelTapGesture)

}

@objc func doSomethingOnTap() {
print("tapped")
}

How can I get UIGestureRecognizer to work with manually added subviews

Gesture recognizers only work on views that they belong to. There is a UIView method to add gesture recognizers. Your addEventRecognizers is only adding recognizers to whatever UIImageView passed in. You should change the function call to accept UIView, since UIImageView is just a subclass of UIView, it will still work on your images. Then call

addEventRecognizers(HandView) //Pass in the view that will get set with gesture recognizer.

Alternatively, if you just want to add one gesture recognizer just call HandView.addGestureRecognizer(gesture)

UITapGestureRecognizer is not working inside the custom UIView

There are numerous issues with the example code that will stop it working, and some that will stop it even compiling (unterminated string literals, trying to set autolayout (AL) constraints without setting TAMIC to false, etc).

But I think* the issue stopping the tap gesture working is that the layout code is a mix of frame sizes and AL constraints and it isn't laying things out the way you believe it is, which means that the custom view's child view isn't receiving the gesture to respond too.

Changing the layout code so that it is all AL makes the tap gesture work without any changes to gesture recogniser or its action method.

I'm assuming the layout you're aiming for is the VC's view with a subview (your CustomView) which in turn has its own subview (your mView, which below I have renamed subView to make it clearer), in a three-tiered pyramid type of stack.

I say 'think' above because I may have corrected some other error in fixing the layout code. I've only corrected the code necessary to get it to compile and give a working UI. I've not intentionally addressed any other issue as they are not relevant to the question.

The code below works in that it lays out the three views (the VC's view, the myView (a CustomView), and the CustomView's subview and adds the tapGestureRecogniser to subView. The gestureRecogniser fires its selector method when tapped. It might not be laid out how you want as it was hard to tell the layout you were trying to achieve, but it does demonstrate that the gestureRecogniser works as expected.

class myViewController : UIViewController {

lazy var myView : MyCustomView = {MyCustomView(frame: .zero)}()

init() {
super.init(nibName: nil, bundle: nil)
view.heightAnchor.constraint(equalToConstant: 600).isActive = true

view.addSubview(myView)
myView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50).isActive = true
myView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -50).isActive = true
myView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -50).isActive = true
myView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 50).isActive = true

}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

class MyCustomView : UIView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .systemOrange
translatesAutoresizingMaskIntoConstraints = false
addSubview(subView)
subView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: -50).isActive = true
subView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -50).isActive = true
subView.topAnchor.constraint(equalTo: topAnchor, constant: 50).isActive = true
subView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -50).isActive = true
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

private lazy var subView: UIView = {
let view = UIView()
view.backgroundColor = .systemBlue
view.translatesAutoresizingMaskIntoConstraints = false
view.isUserInteractionEnabled = true

let gesture = UITapGestureRecognizer(target: self, action: #selector(tappedDropDown))
view.addGestureRecognizer(gesture)
return view
}()

@objc func tappedDropDown(sender : UITapGestureRecognizer){
print("Tapped")
}
}

Why isn't my gesture recognizer working when I programmatically create it?

It looks like you can't create a target action referencing self at initialization time of a class where self isn't available yet. I'm not sure why there's not a warning or error for this.

There are two possible workarounds:

  1. Although it's missing from the UIGestureRecognizer documentation there is a vanilla init method with no parameters. Use that and then call UIGestureRecognizer.addTarget(_:action:) sometime after initialization.

  2. Use a lazy property

private lazy var tapGestureRecognizer: UITapGestureRecognizer = { .init(target: self, action: #selector(myAction)) }()

Handle UIGestureRecognizer in multiple views

You can achieve this from UIGestureRecognizerDelegate. Assign delegate to your gesture and use the extension below. The Delegate below allows gesture to receive touch only when touched outside from the collectionView.

extension SharePathViewController: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
let point = touch.location(in: view)
return !collectionView.frame.contains(point)
}
}

If you want your gesture function to be called while the tap in cell to then simply call the function while the cell is selected.



Related Topics



Leave a reply



Submit