Looping UITapGestureRecognizer only added to last one not all of them - swift

So I'm up to the point that I'm creating UIViews programmatically based on the amount of an array which is returned from JSON. Everything seems to be going ok except for the loop which is supposed to add the tap to each of the views but it is only adding it to one view. Only prints on the last UIView.
func addsubviews(howmany: Int) -> Void{
let tap = UITapGestureRecognizer(target: self, action: Selector("handleTap"))
for x in 1...howmany{
guard let v = UINib(nibName: "DealsView", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as? UIView else { return }
v.translatesAutoresizingMaskIntoConstraints = false
guard let lbl = v.viewWithTag(99) as? UILabel else { return }
lbl.text = "Here is a new Label for Loop: " + "\(x)"
self.allContainer.addSubview(v)
v.backgroundColor = UIColor.white
var horizontalConstratint1 = NSLayoutConstraint()
var horizontalConstratint2 = NSLayoutConstraint()
var verticalConstraint1 = NSLayoutConstraint()
heightConstraint = v.heightAnchor.constraint(equalToConstant: 100)
horizontalConstratint1 = v.leadingAnchor.constraint(equalTo: (v.superview?.leadingAnchor)!, constant: 10)
horizontalConstratint2 = v.trailingAnchor.constraint(equalTo: (v.superview?.trailingAnchor)!, constant: -10)
if prevView == nil{
verticalConstraint1 = v.topAnchor.constraint(equalTo: (v.superview?.topAnchor)!, constant: 20)
}else
{
if let pv = prevView as? UIView {
verticalConstraint1 = v.topAnchor.constraint(equalTo: (pv.bottomAnchor), constant: 20)
}
}
NSLayoutConstraint.activate([horizontalConstratint1,horizontalConstratint2,verticalConstraint1])
prevView = v
}
if let pv = prevView as? UIView {
print("final constratint")
NSLayoutConstraint.activate([
self.allContainer.bottomAnchor.constraint(equalTo: pv.bottomAnchor, constant: 20)
])
}
for views in self.allContainer.subviews{
print("adding views")
views.addGestureRecognizer(tap)
}
}
After some trial and error...
The tap recognizer added for each loop:
let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(sender:)))
the action which will be where the action goes on the tap:
func handleTap(sender: UITapGestureRecognizer) {
// You can get the view here by writing
let myv = sender.view
print(myv)
}

You have just a single UITapGestureRecognizer before your view loop starts. AFAIK, tap gesture recognizers can only be attached to one view at a time. Check out this question. To solve this, try writing this line:
let tap = UITapGestureRecognizer(target: self, action: Selector("handleTap"))
inside your view loop.
To find out what view was tapped, make your selector handleTap take an argument like so:
Selector("handleTap:")
and the method itself like this:
func handleTap(sender: UITapGestureRecognizer? = nil) {
// You can get the view here by writing
let v = sender.view
}

Related

How can I add a swipe gesture to my side menu controller?

I made a side menu with some controls and I can dismiss it when the user taps outside of the side menu or if he/she selects a row inside the side menu. Now I want to add a swipe gesture to the left so that the user can dismiss it that way too.
extension MenuViewController {
#objc func dismissControllerAnimated() {
dismiss(animated: true, completion: nil)
} }
class SlideinTransition: NSObject, UIViewControllerAnimatedTransitioning {
let menuViewController = MenuViewController()
var isPresenting = true
let dimmingView = UIView()
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.5
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let toViewController = transitionContext.viewController(forKey: .to),
let fromViewController = transitionContext.viewController(forKey: .from) else { return }
let containerView = transitionContext.containerView
let finalWidth = toViewController.view.bounds.width * 0.3
let finalHeight = toViewController.view.bounds.height
if isPresenting{
//adds a tap gesture to our dimming view
let tapGesture = UITapGestureRecognizer(target: toViewController, action: #selector(MenuViewController.dismissControllerAnimated))
dimmingView.addGestureRecognizer(tapGesture)
//adds the dimming view
dimmingView.backgroundColor = .black
dimmingView.alpha = 0.0
containerView.addSubview(dimmingView)
dimmingView.frame = containerView.bounds
//adds the menu view controller to our container
containerView.addSubview(toViewController.view)
//init frame off the screen
toViewController.view.frame = CGRect(x: -finalWidth, y: 0, width: finalWidth, height: finalHeight)
}
let transform = {
self.dimmingView.alpha = 0.5
toViewController.view.transform = CGAffineTransform(translationX: finalWidth, y: 0)
}
//applies a specific kind of transformation to our view
let identity = {
self.dimmingView.alpha = 0.0
fromViewController.view.transform = .identity
}
//animates the transition and cancels it when you click outside of the frame
let duration = transitionDuration(using: transitionContext)
let isCancelled = transitionContext.transitionWasCancelled
UIView.animate(withDuration: duration, animations: {
self.isPresenting ? transform() : identity()
}) { (_) in
transitionContext.completeTransition(!isCancelled)
if !self.isPresenting {
self.dimmingView.removeFromSuperview()
}
}
}
}
I instantiate my "MenuViewController" like this into my MainController
#IBAction func didTapMenu(_ sender: UIBarButtonItem) {
guard let menuViewController = storyboard?.instantiateViewController(withIdentifier: "MenuViewController") as? MenuViewController else { return }
menuViewController.didTapMenuType = { menuType in
self.transitionToNew(menuType)
}
menuViewController.modalPresentationStyle = .overCurrentContext
menuViewController.transitioningDelegate = self
present(menuViewController, animated: true)
}
How can I add a SwipeGestureRecognizer to my transition? I appreciate every input. Thanks in advance!

why is bringSubviewToFront() only working for panning, not tapping?

view.bringSubviewToFront(tappedView)
is working when I drag (pan) views but not when I tap them. My issue is that when I have one view layered over the other, I want the bottom view to come to the front when tapped. Currently it will only come to the front when dragged. Do I need some additional code to do this? Thanks.
Here's an excerpt from my code for more context:
#objc func didPan(_ recognizer: UIPanGestureRecognizer) {
let location = recognizer.location(in: self.view)
switch recognizer.state {
case .began:
currentTappedView = moviePieceView.filter { pieceView -> Bool in
let convertedLocation = view.convert(location, to: pieceView)
return pieceView.point(inside: convertedLocation, with: nil)
}.first
currentTargetView = movieTargetView.filter { $0.pieceView == currentTappedView }.first
case .changed:
guard let tappedView = currentTappedView else { return }
let translation = recognizer.translation(in: self.view)
tappedView.center = CGPoint(x: tappedView.center.x + translation.x, y: tappedView.center.y + translation.y)
recognizer.setTranslation(.zero, in: view)
view.bringSubviewToFront(tappedView)
```
I had this same problem, I managed to solve it this way:
Add a gestureRecognizer to your view and put this in your code. Don't forget to set the delegate as well when attaching this to the code!
#IBAction func tappedView1(recognizer: UITapGestureRecognizer) {
view.bringSubviewToFront(myView)
}
Put this in your viewDidLoad:
let tap = UITapGestureRecognizer(target: self, action: #selector(tappedView1))
tap.cancelsTouchesInView = false
self.view.addGestureRecognizer(tap)
If you want to do that from your panGesture check my question.
Thank you all. It worked when I wrote a second function in addition to didPan().
This is the additional code:
override func viewDidLoad() {
super.viewDidLoad()
configureLabel()
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(didPan(_:)))
view.addGestureRecognizer(panGestureRecognizer)
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTap(_:)))
view.addGestureRecognizer(tapGestureRecognizer)
}
#objc func didTap(_ recognizer: UITapGestureRecognizer) {
let location = recognizer.location(in: self.view)
currentTappedView = moviePieceView.filter { pieceView -> Bool in
let convertedLocation = view.convert(location, to: pieceView)
return pieceView.point(inside: convertedLocation, with: nil)
}.first
guard let tappedView = currentTappedView else { return }
view.bringSubviewToFront(tappedView)
}
HOWEVER, I found out you can also just tick the "User Interaction Enabled" box in the Image View if you don't want to do it programmatically.

Passing parameters in a clickable UIabel

I'm trying to make a clickable UILabel by following with this code:
let tap_plato = UITapGestureRecognizer(target: self, action: #selector(ViewController.ale_plato1))
plato1.isUserInteractionEnabled = true
plato1.addGestureRecognizer(tap_plato)
...
#objc
func ale_plato1(sender: UITapGestureRecognizer){
let label = sender.view
print ("tapped!")
}
This works well. But I want to pass parameters to the function. Something like this:
let tap_plato = UITapGestureRecognizer(target: self, action: #selector(MenuController.ale_plato1("parameter")))
plato1.isUserInteractionEnabled = true
plato1.addGestureRecognizer(tap_plato)
#objc
func ale_plato1(sender: UITapGestureRecognizer, parameterRecived: String){
}
But I don't know how do that in swift 3...
Any help? Thanks you
Wherever you make your multiple UILabel set each label to a different tag.
for i in 0..<3 {
let label = UILabel()
label.tag = i
let tap = UITapGestureRecognizer(target: self, action: #selector(tap))
label.isUserInteractionEnabled = true
label.addGestureRecognizer(tap_plato)
}
#objc func tap(sender: UITapGestureRecognizer) {
let label = sender.view as! UILabel // if you are SURE that your tap will be a UILabel
if(label.tag == 0) {
label.text = "This is label 0"
if(label.tag == 1) {
label.text = "This is label 1"
}
}
Threw this up in a jiffy, hop over the syntax issues if there are any.
Here we can create labels with tags and apply the same tap to them. From there, we can check what tag it is inside the tap method and do something from there.
You could use the tag attribute for Int values, but if you want anything other than that:
Create a custom UILabel Class with a value variable.
class LabelWithValue : UILabel {
var value : String = ""
}
Then you can use it as a normal Label
let label = LabelWithValue()
label.text = "bla"
label.value = "Anything"
label.isUserInteractionEnabled = true
label.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleGetText)))
Then you can get it's text and it's value.
#objc func handleGetText(_ sender : UITapGestureRecognizer) {
if let label = sender.view as? LabelWithValue {
print(label.text)
print(label.value)
}
}

swift uitapgesturerecognizer pass parameters

I have a lot of Buttons created programmatically and that can change anytime. my tap gesture :
let apriVideoGesture = UITapGestureRecognizer(target: self, action: #selector(PrincipaleController.apriVideo(_:)))
cont.addGestureRecognizer(apriVideoGesture)
func apriVideo(sender : UITapGestureRecognizer){
}
how can i pass parameters? somethig like this :
let apriVideoGesture = UITapGestureRecognizer(target: self, action: #selector(PrincipaleController.apriVideo(stringa : "ciao")))
cont.addGestureRecognizer(apriVideoGesture)
func apriVideo(sender : UITapGestureRecognizer, stringa : String){
}
sorry for bad english, i'm italian
First of all, if you are using button then why are you adding tap gesture? You can add target to it as
btn.addTarget(self, action: #selector(self.btnPressed(_:)), forControlEvents: .TouchDragInside)
But still you can achieve you goal using tap gesture as
Using UIView as u have insisted
class ViewController: UIViewController {
let arrayOfSongsURL: [String] = []
let startingTag = 100
override func viewDidLoad() {
super.viewDidLoad()
let height : CGFloat = 100
let width : CGFloat = 100
(arrayOfSongsURL as NSArray).enumerateObjectsUsingBlock { (url, index, finished) -> Void in
let v = UIView(frame: CGRectMake(0, CGFloat(index) * height, width, height))
v.tag = self.startingTag + index
v.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleTapGesture(_:))))
self.view.addSubview(v)
}
// Do any additional setup after loading the view, typically from a nib.
}
#objc func handleTapGesture(gesture : UITapGestureRecognizer)
{
let v = gesture.view!
let tag = v.tag
let songURL = arrayOfSongsURL[tag - startingTag]
//Do what you want to do with songURL
}
}

Increment Variable on horizontal UIScrollView slide

How would I increment a variable when the user swipes to the right or left in my horizontal UIScrollView.
For example;
var pageNumber = 1
When the user swipes right it increments by 1 and when swipes left it decrements by 1 -
Here is my scrollView code that is initiated in the viewDidLoad function.
for (var i : Int = 0; i < numberOfQuestions ;i++)
{
//Construct the view by using the Template XIB file
var array : NSArray = NSBundle.mainBundle().loadNibNamed("QuestionView", owner: self, options: nil);
var view : QuestionView = array.objectAtIndex(0) as! QuestionView
// Set the scroll view to global variable
scrollViewQuiz = view
questionLabel.text = questions[currentQuestionIndexView].question
questionViews.addObject(view);
view.setTranslatesAutoresizingMaskIntoConstraints(false);
view.backgroundColor = UIColor.clearColor();
//Add to the scrollView
scrollView.addSubview(view);
//Add the top Constraints, they need to fit the superview
let views : NSDictionary = ["view" : view,"scrollView" : scrollView];
let constraints : NSArray = NSLayoutConstraint.constraintsWithVisualFormat("V:|[view(==scrollView)]|", options: NSLayoutFormatOptions.allZeros, metrics: nil, views: views as [NSObject : AnyObject]);
scrollView.addConstraints(constraints as! [AnyObject]);
// Increments the question
currentQuestionIndexView++
}
contentView.backgroundColor = UIColor.clearColor();
var viewsDictionary : NSMutableDictionary = NSMutableDictionary(dictionary: ["scrollView" : scrollView]);
var visualFormat : NSMutableString = ("H:|").mutableCopy() as! NSMutableString;
//With all the views created, create the Layout Constraints for the horizontal logic
for (var i : Int = 0; i < numberOfQuestions; i++)
{
viewsDictionary.setValue(self.questionViews[i], forKey: NSString(format: "view%d", i) as String);
visualFormat.appendFormat("[view%d(==scrollView)]", i);
}
visualFormat.appendString("|");
constraints = NSLayoutConstraint.constraintsWithVisualFormat(visualFormat as String, options: NSLayoutFormatOptions.allZeros, metrics: nil, views: viewsDictionary as [NSObject : AnyObject]);
scrollView.addConstraints(constraints as! [AnyObject]);
}
View Did Load UPDATE
override func viewDidLoad() {
// QUIZ LOGIC START
shuffleQuestions()
// UI CONSTRAINTS AND VIEW GENERATION
//Example of using 3 questions
var scrollView = self.scrollView;
scrollView.setTranslatesAutoresizingMaskIntoConstraints(false);
self.view.setTranslatesAutoresizingMaskIntoConstraints(false);
self.contentView.setTranslatesAutoresizingMaskIntoConstraints(false);
//Constraints
var constraints : NSArray;
for (var i : Int = 0; i < numberOfQuestions ;i++)
{
//Construct the view by using the Template XIB file
var array : NSArray = NSBundle.mainBundle().loadNibNamed("QuestionView", owner: self, options: nil);
var view : QuestionView = array.objectAtIndex(0) as! QuestionView
// Set the scroll view to global variable
scrollViewQuiz = view
questionLabel.text = questions[currentQuestionIndexView].question
questionViews.addObject(view);
view.setTranslatesAutoresizingMaskIntoConstraints(false);
view.backgroundColor = UIColor.clearColor();
//Add to the scrollView
scrollView.addSubview(view);
//Add the top Constraints, they need to fit the superview
let views : NSDictionary = ["view" : view,"scrollView" : scrollView];
let constraints : NSArray = NSLayoutConstraint.constraintsWithVisualFormat("V:|[view(==scrollView)]|", options: NSLayoutFormatOptions.allZeros, metrics: nil, views: views as [NSObject : AnyObject]);
scrollView.addConstraints(constraints as! [AnyObject]);
let leftSwipeRecognizer = UISwipeGestureRecognizer(target: self, action: "handleLeftSwipe:")
leftSwipeRecognizer.direction = .Left
scrollViewQuiz!.addGestureRecognizer(leftSwipeRecognizer)
let rightSwipeRecognizer = UISwipeGestureRecognizer(target: self, action: "handleRightSwipe:")
rightSwipeRecognizer.direction = .Right
scrollViewQuiz!.addGestureRecognizer(rightSwipeRecognizer)
// Increments the question
currentQuestionIndexView++
}
contentView.backgroundColor = UIColor.clearColor();
var viewsDictionary : NSMutableDictionary = NSMutableDictionary(dictionary: ["scrollView" : scrollView]);
var visualFormat : NSMutableString = ("H:|").mutableCopy() as! NSMutableString;
//With all the views created, create the Layout Constraints for the horizontal logic
for (var i : Int = 0; i < numberOfQuestions; i++)
{
viewsDictionary.setValue(self.questionViews[i], forKey: NSString(format: "view%d", i) as String);
visualFormat.appendFormat("[view%d(==scrollView)]", i);
}
visualFormat.appendString("|");
constraints = NSLayoutConstraint.constraintsWithVisualFormat(visualFormat as String, options: NSLayoutFormatOptions.allZeros, metrics: nil, views: viewsDictionary as [NSObject : AnyObject]);
scrollView.addConstraints(constraints as! [AnyObject]);
}
Update 3 ##
//Add to the scrollView
scrollView.addSubview(view);
scrollView.addSubview(scrollViewQuiz!)
Update 4
I'm assuming you can't use the paging property of UIScrollView.
Contrary to the previous answer, you will actually need two different swipe recognizers. See https://stackoverflow.com/a/7760927/5007059
I understand that you want to detect swipes on the scroll view. To accomplish this, you could add two UISwipeGestureRecognizers to your scrollView, one for left swipes and one for right swipes like so:
// add to viewDidLoad
let leftSwipeRecognizer = UISwipeGestureRecognizer(target: self, action: "handleLeftSwipe:")
leftSwipeRecognizer.direction = .Left
scrollView.addGestureRecognizer(leftSwipeRecognizer)
let rightSwipeRecognizer = UISwipeGestureRecognizer(target: self, action: "handleRightSwipe:")
rightSwipeRecognizer.direction = .Right
scrollView.addGestureRecognizer(rightSwipeRecognizer)
...
// add to your view controller subclass
func handleLeftSwipe(sender: UISwipeGestureRecognizer) {
self.pageNumber = min(PageCount, self.pageNumber + 1)
}
func handleRightSwipe(sender: UISwipeGestureRecognizer) {
self.pageNumber = max(0, self.pageNumber - 1)
}
create swipe gesture
add gesture to the view
var pageNumber = 1
override func viewDidLoad() {
super.viewDidLoad()
///////
let swipeGesture = UISwipeGestureRecognizer(target: self, action: "swipeHandler:")
swipeGesture.direction = .Left | .Right
scrollViewQuiz.addGestureRecognizer(swipeGesture)
}
func swipeHandler( recognizer:UISwipeGestureRecognizer){
switch(recognizer.direction){
case UISwipeGestureRecognizerDirection.Left:
pageNumber--
case UISwipeGestureRecognizerDirection.Right:
pageNumber++
default: break
}
}