iOS Swift, cannot get pinch gesture to work - ios

i have a test project that takes text from a file, adds it to a textview and displays it.
i want to add some gestures but cannot seem to make it work...
here is the relevant code:
class ViewController2: UIViewController, UIGestureRecognizerDelegate {
#IBOutlet var textview1: UITextView!
var pinchGesture = UIPinchGestureRecognizer()
override func viewDidLoad() {
super.viewDidLoad()
self.textview1.userInteractionEnabled = true
self.textview1.multipleTouchEnabled = true
self.pinchGesture.delegate = self
self.pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(ViewController2.pinchRecognized(_:)))
self.view.addGestureRecognizer(self.pinchGesture)
}
#IBAction func pinchRecognized(pinch: UIPinchGestureRecognizer) {
self.textview1.addGestureRecognizer(pinchGesture)
self.textview1.transform = CGAffineTransformScale(self.textview1.transform, pinch.scale, pinch.scale)
pinch.scale = 1.0
}
any ideas? followed several tutorials but none seem to help. code is tested on actual iPhone...
thanks a lot
Edit for Solution:
#IBAction func pinchRecognized(pinch: UIPinchGestureRecognizer) {
var pinchScale = pinchGesture.scale
pinchScale = round(pinchScale * 1000) / 1000.0
if (pinchScale < 1) {
self.textview1.font = UIFont(name: self.textview1.font!.fontName, size: self.textview1.font!.pointSize - pinchScale)
pinchScale = pinchGesture.scale
} else {
self.textview1.font = UIFont(name: self.textview1.font!.fontName, size: self.textview1.font!.pointSize + pinchScale)
pinchScale = pinchGesture.scale
}
}
thanks to nishith Singh

Try adding the gesture recogniser to your textview in viewDidLoad instead of adding it in pinchRecognized. Currently you are adding the pinchGesture to your view which is behind your text view and hence will not receive the touch
var pinchGesture = UIPinchGestureRecognizer()
Use this code:
override func viewDidLoad() {
super.viewDidLoad()
self.textview1.userInteractionEnabled = true
self.textview1.multipleTouchEnabled = true
self.pinchGesture = UIPinchGestureRecognizer(target: self, action:#selector(pinchRecognized(_:)))
self.textview1.addGestureRecognizer(self.pinchGesture)
// Do any additional setup after loading the view.
}
#IBAction func pinchRecognized(_ pinch: UIPinchGestureRecognizer) {
let fontSize = self.textview1.font!.pointSize*(pinch.scale)/2
if fontSize > 12 && fontSize < 32{
textview1.font = UIFont(name: self.textview1.font!.fontName, size:fontSize)
}
}
You might have to hit and trial with the minimum and maximum font sizes as you want, right now the minimum font size is 12 and the maximum font size is 32.

let pinchGesture = UIPinchGestureRecognizer(target: self, action:#selector(self.pinchGesture))
func pinchGesture(sender: UIPinchGestureRecognizer){
sender.view?.transform = (sender.view?.transform)!.scaledBy(x: sender.scale, y: sender.scale)
sender.scale = 1
print("pinch gesture")
}

class ViewController2: UIViewController, UIGestureRecognizerDelegate {
#IBOutlet var textview1: UITextView!
var pinchGesture = UIPinchGestureRecognizer()
override func viewDidLoad() {
super.viewDidLoad()
self.textview1.userInteractionEnabled = true
self.textview1.multipleTouchEnabled = true
self.pinchGesture.delegate = self
self.pinchGesture = UIPinchGestureRecognizer(target: self, action: "pinchRecognized:")
self.view.addGestureRecognizer(self.pinchGesture)
}
func pinchRecognized(pinch: UIPinchGestureRecognizer) {
self.textview1.addGestureRecognizer(pinchGesture)
self.textview1.transform = CGAffineTransformScale(self.textview1.transform, pinch.scale, pinch.scale)
pinch.scale = 1.0
}

Your code is actually working. But not the way you want probably.
Initially you assigned the gesture recogniser to the view of view controller.
But then inside the method you added same gesture recogniser to the UITextView.
So it should be working on UITextView. And the gesture recogniser is removed from the view controller's view. Gesture recogniser can only have one target. Pick view controller's view or textview.

You set the delegate of self.pinchGesture before initialising.
Initialise the self.pinchGesture first.
Set the delegate.
Add self.pinchGesture to self.view
self.pinchGesture.delegate = self
self.pinchGesture = UIPinchGestureRecognizer(target: self, action:#selector(ViewController2.pinchRecognized(_:)))
self.view.addGestureRecognizer(self.pinchGesture)

Related

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

Looking to add a tap gesture to an array of UIViews - without success. Tap seems not to be recognised at this stage.
In the code (extract) below:
Have a series of PlayingCardViews (each a UIView) showing on the main view.
Brought together as an array: cardView.
Need to be able to tap each PlayingCardView independently (and then to be able to identify which one was tapped).
#IBOutlet private var cardView: [PlayingCardView]!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(tapCard(sender: )))
for index in cardView.indices {
cardView[index].isUserInteractionEnabled = true
cardView[index].addGestureRecognizer(tap)
cardView[index].tag = index
}
}
#objc func tapCard (sender: UITapGestureRecognizer) {
if sender.state == .ended {
let cardNumber = sender.view.tag
print("View tapped !")
}
}
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: )))
I will not recommend the selected answer. Because creating an array of tapGesture doesn't make sense to me in the loop. Better to add gesture within PlaycardView.
Instead, such layout should be designed using UICollectionView. If in case you need to custom layout and you wanted to use scrollView or even UIView, then the better approach is to create single Gesture Recognizer and add to the superview.
Using tap gesture, you can get the location of tap and then you can get the selectedView using that location.
Please refer to below example:
import UIKit
class PlayCardView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.red
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
backgroundColor = UIColor.red
}
}
class SingleTapGestureForMultiView: UIViewController {
var viewArray: [UIView]!
var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
scrollView = UIScrollView(frame: UIScreen.main.bounds)
view.addSubview(scrollView)
let tapGesture = UITapGestureRecognizer(target: self,
action: #selector(tapGetsure(_:)))
scrollView.addGestureRecognizer(tapGesture)
addSubviews()
}
func addSubviews() {
var subView: PlayCardView
let width = UIScreen.main.bounds.width;
let height = UIScreen.main.bounds.height;
let spacing: CGFloat = 8.0
let noOfViewsInARow = 3
let viewWidth = (width - (CGFloat(noOfViewsInARow+1) * spacing))/CGFloat(noOfViewsInARow)
let viewHeight = (height - (CGFloat(noOfViewsInARow+1) * spacing))/CGFloat(noOfViewsInARow)
var yCordinate = spacing
var xCordinate = spacing
for index in 0..<20 {
subView = PlayCardView(frame: CGRect(x: xCordinate, y: yCordinate, width: viewWidth, height: viewHeight))
subView.tag = index
xCordinate += viewWidth + spacing
if xCordinate > width {
xCordinate = spacing
yCordinate += viewHeight + spacing
}
scrollView.addSubview(subView)
}
scrollView.contentSize = CGSize(width: width, height: yCordinate)
}
#objc
func tapGetsure(_ gesture: UITapGestureRecognizer) {
let location = gesture.location(in: scrollView)
print("location = \(location)")
var locationInView = CGPoint.zero
let subViews = scrollView.subviews
for subView in subViews {
//check if it subclass of PlayCardView
locationInView = subView.convert(location, from: scrollView)
if subView.isKind(of: PlayCardView.self) {
if subView.point(inside: locationInView, with: nil) {
// this view contains that point
print("Subview at \(subView.tag) tapped");
break;
}
}
}
}
}
You can try to pass the view controller as parameter to the views so they can call a function on parent view controller from the view. To reduce memory you can use protocols. e.x
protocol testViewControllerDelegate: class {
func viewTapped(view: UIView)
}
class testClass: testViewControllerDelegate {
#IBOutlet private var cardView: [PlayingCardView]!
override func viewDidLoad() {
super.viewDidLoad()
for cardView in self.cardView {
cardView.fatherVC = self
}
}
func viewTapped(view: UIView) {
// the view that tapped is passed ass parameter
}
}
class PlayingCardView: UIView {
weak var fatherVC: testViewControllerDelegate?
override func awakeFromNib() {
super.awakeFromNib()
let gr = UITapGestureRecognizer(target: self, action: #selector(self.viewDidTap))
self.addGestureRecognizer(gr)
}
#objc func viewDidTap() {
fatherVC?.viewTapped(view: self)
}
}

how to enable swipe gestures on imageview when it is embedded in scroll view

in this I had implemented the swipe gestures on image view and it is embedded in scroll view but gestures are not working here is my code any solution for this ?
collectionView.delegate = self
collectionView.dataSource = self
imageView.isUserInteractionEnabled = true
let swipeLeft = UISwipeGestureRecognizer(target: self, action:#selector(swiped(gesture:)))
swipeLeft.direction = .left
self.imageView.addGestureRecognizer(swipeLeft)
swipeLeft.cancelsTouchesInView = false
let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(swiped(gesture:)))
swipeRight.direction = .right
self.imageView.addGestureRecognizer(swipeRight)
swipeRight.cancelsTouchesInView = false
imageView.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(imageTapped(_:)))
self.imageView.addGestureRecognizer(tap)
If you want swipe and scrolling both at work concurrently you have to implement gesture recognizer delegate.
// here are those protocol methods with Swift syntax
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
return true
}
May be you could try this.Don't forget to delegate as self. :)
For your case, you want to have UI like Gallery View. Just Left and right swipe to change photo and tap/DoubleTap to zoom.
Don't need Swipe Gestures.
Just use Collection view with paging enabled. Each cell will display a single image. when Paging enabled, only a single cell will be shown at a time.
So just enable Paging enabled in collection view and try running.
If you want to zoom, when tapping a cell then, add TapGesture to CollectionView's parent UIScrollView and do write actions correspondingly.
For this case, I've used custom collection view cell.
I've just added my custom cell code for your reference as given below.
class ImageViewerCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
override func awakeFromNib() {
super.awakeFromNib()
self.scrollView.minimumZoomScale = 1.0
self.scrollView.maximumZoomScale = 6.0
self.scrollView.delegate = self
self.scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true)
self.scrollView.zoom(to: CGRect(origin: CGPoint.zero, size: scrollView.frame.size), animated: true)
let doubleTap = UITapGestureRecognizer(target: self, action: #selector(doubleTapAction(_:)))
doubleTap.numberOfTapsRequired = 2
self.scrollView.addGestureRecognizer(doubleTap)
}
func setURL(imageURL : URL, needLoader : Bool) {
DispatchQueue.main.async {
self.scrollView.setZoomScale(self.scrollView.minimumZoomScale, animated: true)
self.scrollView.zoom(to: CGRect(origin: CGPoint.zero, size: self.scrollView.frame.size), animated: true)
if needLoader {
self.activityIndicator.isHidden = false
self.activityIndicator.startAnimating()
self.imageView.pin_setImage(from: imageURL, completion: { (completed) in
self.activityIndicator.stopAnimating()
self.activityIndicator.isHidden = true
})
}
else {
self.activityIndicator.isHidden = true
self.imageView.pin_setImage(from: imageURL, placeholderImage: placeHolderImage)
}
self.imageView.pin_updateWithProgress = true
}
}
#IBAction func doubleTapAction(_ sender: Any) {
if scrollView.zoomScale == scrollView.minimumZoomScale {
let touchPoint = (sender as! UITapGestureRecognizer).location(in: scrollView)
let scale = min(scrollView.zoomScale * 3, scrollView.maximumZoomScale)
let scrollSize = scrollView.frame.size
let size = CGSize(width: scrollSize.width / scale,
height: scrollSize.height / scale)
let origin = CGPoint(x: touchPoint.x - size.width / 2,
y: touchPoint.y - size.height / 2)
scrollView.zoom(to:CGRect(origin: origin, size: size), animated: true)
}
else {
scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true)
scrollView.zoom(to: CGRect(origin: CGPoint.zero, size: scrollView.frame.size), animated: true)
}
}
}
extension ImageViewerCollectionViewCell : UIScrollViewDelegate {
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return self.imageView
}
}
As given above, I've used a imageview inside UIScrollView. So when you double tapped that scroll view, scrollview will zoom in & out.
Hope you understand and hope it helps for sure.

UIGestureRecognizer on only one part of the screen

If you look into the image, you'll see a textview, button and a slider.
I have completed all the steps except for the last one which is, when I tap on the slider, it constantly disappear. I know it disappear because I implemented UITapGestureRecognizer.
I guess my question is, I want the slider to disappear everytime I tap anywhere on the screen but when I am using the slider, I dont want the slider to disappear which is happening now every time I release my tap.
I have tried implementing one more UITapGestureRecognizer in sizeRefont with a function to keep sizeRefont.isHidden false but when I do that, the slider will not disappear whenever I tap on the screen.
I tried putting sizeRefont.isHidden = false in sizeRefont action and it doesnt work either.
class ResizeController: UIViewController {
#IBOutlet weak var sizeRefont: UISlider!
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissRefontSize(_:)))
view.addGestureRecognizer(tap)
}
#IBAction func sizeRefont(_ sender: AnyObject) {
let fontSize = CGFloat(sizeRefont.value)
textView.font = UIFont(name: textView.font!.fontName, size: fontSize * 30.0)
}
#IBAction func showSlider(_ sender: Any) {
sizeRefont.isHidden = false
}
func dismissRefontSize(_ sender: UITapGestureRecognizer) {
if sender.location(in: sizeRefont){
sizeRefont.isHidden = false
} else {
sizeRefont.isHidden = true
}
}
}
There is an error on if sender.location(in: sizeRefont) where it says CGPoint is not convertible to Bool
Image
First thing you need to do is you need to Adjust the dismissRefontSize() method to to the following :
func dismissRefontSize(_ sender: UITapGestureRecognizer) {
let location = sender.location(in: view)
If sizeReFont.frame.contains(location) {
// do nothing
}else {
sizeReFont.isHidden = true
}
}
The other thing you need to adjust is creating the tap recognized in your viewDidLoad() to the following:
let tap = UITapGestureRecognizer(target: self, action : #selector(dismissRefontSize(_:)))

UIImageView UITapGestureRecognizer not working

Tap gesture event not gettint called. I may be doing something wrong, but please have a look (I tried adding recognizer to self.view but still no luck) :
LoginViewController
class LoginViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let container = UIView()
container.heightAnchor.constraintEqualToConstant(50).active = true
let myVC1 = MyViewController()
let myVC2 = MyViewController()
let myVC3 = MyViewController()
let myVC4 = MyViewController()
let myStackView = UIStackView(arrangedSubviews: [myVC1.view, myVC2.view, myVC3.view, myVC4.view])
myStackView.spacing = 10
myStackView.alignment = .Fill
myStackView.distribution = .EqualSpacing
container.addSubview(myStackView)
view.addSubview(container)
}
}
MyViewController
class MyViewController: UIViewController, UIGestureRecognizerDelegate {
let ImageView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
ImageView.backgroundColor = UIColor.blueColor()
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(pressedSocialMediaItem(_:)))
tapGestureRecognizer.delegate = self
tapGestureRecognizer.numberOfTapsRequired = 1
ImageView.userInteractionEnabled = true
ImageView.addGestureRecognizer(tapGestureRecognizer)
view.addSubview(ImageView)
}
func pressedSocialMediaItem(sender : UITapGestureRecognizer) {
print("PRESSED ! ")
}
}
I suggest using MyViewController.pressedSocialMediaItem, so your code should work like this.
class MyViewController: UIViewController, UIGestureRecognizerDelegate {
let ImageView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
ImageView.backgroundColor = UIColor.blueColor()
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(MyViewController.pressedSocialMediaItem(_:)))
tapGestureRecognizer.delegate = self
tapGestureRecognizer.numberOfTapsRequired = 1
ImageView.userInteractionEnabled = true
ImageView.addGestureRecognizer(tapGestureRecognizer)
view.addSubview(ImageView)
}
func pressedSocialMediaItem(sender : UITapGestureRecognizer) {
print("PRESSED ! ")
}
}
I have tested the following on my machine and it works:
import UIKit
class ViewController: UIViewController, UIGestureRecognizerDelegate {
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
imageView.backgroundColor = UIColor.blueColor()
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.pressedSocialMediaItem(_:)))
tapGestureRecognizer.delegate = self
tapGestureRecognizer.numberOfTapsRequired = 1
imageView.userInteractionEnabled = true
imageView.addGestureRecognizer(tapGestureRecognizer)
view.addSubview(imageView)
}
func pressedSocialMediaItem(sender : UITapGestureRecognizer) {
print("PRESSED ! ")
}
}
Okey, so I got it working somehow. All I need to do was to add view controllers inside my LoginViewController like so:
self.addChildViewController(myVC1)
Instead of using view.addSubview(container)
Notice: I'm not sure it is the correct way of doing this, and please comment if it's not. Meanwhile it solves my problem.

Pass Parameter with UITapGestureRecognizer

Is there any way i can pass parameters with UITapGestureRecognizer?
I've seen this answered for objective-c but couldn't find an answer for swift
test.userInteractionEnabled = true
let tapRecognizer = UITapGestureRecognizer(target: self, action: Selector("imageTapped4:"))
// Something like text.myParamater
test.addGestureRecognizer(tapRecognizer)
And then receive myParameter under func imageTapped4(){}
One approach would be to subclass UITapGestureRecognizer and then set a property, I've posted an example below. You could also do some check on the sender and check if equal to some tag, class, string, e.t.c
class ViewController: UIViewController {
#IBOutlet weak var label1: UILabel!
#IBOutlet weak var image: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
image.userInteractionEnabled = true;
let tappy = MyTapGesture(target: self, action: #selector(self.tapped(_:)))
image.addGestureRecognizer(tappy)
tappy.title = "val"
}
func tapped(sender : MyTapGesture) {
print(sender.title)
label1.text = sender.title
}
}
class MyTapGesture: UITapGestureRecognizer {
var title = String()
}
There are lots of examples on SO, have a look, good luck.
For Swift 4
In Viewdidload
let label = UILabel(frame: CGRect(x: 0, y: h, width: Int(self.phoneNumberView.bounds.width), height: 30))
label.textColor = primaryColor
label.numberOfLines = 0
label.font = title3Font
label.lineBreakMode = .byWordWrapping
label.attributedText = fullString
let phoneCall = MyTapGesture(target: self, action: #selector(self.openCall))
phoneCall.phoneNumber = "\(res)"
label.isUserInteractionEnabled = true
label.addGestureRecognizer(phoneCall)
Function as
#objc func openCall(sender : MyTapGesture) {
let number = sender.phoneNumber
print(number)
}
Write Class as
class MyTapGesture: UITapGestureRecognizer {
var phoneNumber = String()
}
Follow Step Properly and make change according to your variable , button ,label . It WORKS PROPERLY
Without subclassing, you can also add the parameter in the view layer (where the gesture is attached):
let tappy = UITapGestureRecognizer(target: self, action: #selector(self.tapped(_:)))
image.addGestureRecognizer(tappy)
image.layer.setValue(title, forKey: "anyKeyName")
Later in the handler
#objc private func tapped(_ sender: UIGestureRecognizer) {
guard let title = sender.view?.layer.value(forKey: "anyKeyName") as? String else { return }
// Do something with the title
}
The best way is to determind the parameter when the func imageTapped64is fired. You can get you params via the view (take a look to #Developer Sheldon answer)
or in many other ways.

Resources