UIPanGestureRecognizer does not work - swift

It's weird, but when I add a UIPanGestureRecognizer to a view it does not work. It's my view, that white view is a subview of the red view:
Main View:
#IBOutlet weak var redView: UIView!
#IBOutlet weak var whiteView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let panRecognizer = UIPanGestureRecognizer(target:self, action:#selector(ViewController.handlePan))
whiteView.gestureRecognizers = [panRecognizer]
}
func handlePan(recognizer:UIPanGestureRecognizer) {
//foo
}
When I tap and drag the white view it won't move.

try this:
whiteView.isUserInteractionEnabled = true

From the Apple's docs:
If the code for your pan gesture recognizer is not called, check to see if the following conditions are true, and make corrections as needed:
The isUserInteractionEnabled property of the view is set to true. Image views and labels set this property to false by default.
The number of touches is between the values specified in the minimumNumberOfTouches and maximumNumberOfTouches properties.
For a UIScreenEdgePanGestureRecognizer object, the edges property is configured and touches start at the appropriate edge.
Handling Pan Gestures

Here is the answer:
class ViewController: UIViewController {
#IBOutlet weak var redView: UIView!
#IBOutlet weak var whiteView: UIView!
var lastLocation:CGPoint = CGPointMake(0, 0)
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let panRecognizer = UIPanGestureRecognizer(target:self, action:#selector(ViewController.handlePan))
whiteView.addGestureRecognizer(panRecognizer)
}
func handlePan(recognizer:UIPanGestureRecognizer) {
let translation = recognizer.translationInView(redView)
whiteView.center = CGPointMake(lastLocation.x + translation.x, lastLocation.y + translation.y)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
// Promote the touched view
lastLocation = whiteView.center
}
}

If you are using swift language for method you can't use [].
var PanGesture = UIPanGestureRecognizer(target: self, action: "respondToPanGesture:")
whiteView.addGestureRecognizer(PanGesture)
Hope it helps.

Related

CAGradientLayer doesn't work and still showing solid color

I'm trying to add a gradient to a view but it doesn't work. This is the code:
class TermsAndConditionViewController: UIViewController, NavigationBarToggling {
#IBOutlet weak var tncContentView: UIView!
#IBOutlet weak var tncTextView: UITextView!
#IBOutlet weak var declineButton: AeonButton!
#IBOutlet weak var acceptButton: AeonButton!
#IBOutlet weak var unreadIndicatorView: UIView!
#IBOutlet weak var unreadLabel: UILabel!
let isNavigationBarHidden: Bool = false
let disposeBag = DisposeBag()
lazy var segueManager: SegueManager = {
return SegueManager(viewController: self)
}()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
segueManager.prepare(for: segue)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setupViews()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
title = "Terms & Conditions"
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
setupGradientUnreadIndicator()
}
private func setupViews() {
tncContentView.addCorner(radius: 8)
tncContentView.addBorder(width: 1, color: UIColor.black.withAlphaComponent(0.1).cgColor)
}
private func setupGradientUnreadIndicator() {
let gradient = CAGradientLayer()
gradient.type = .axial
gradient.colors = [
R.color.vcSubBgColor()!.withAlphaComponent(0),
UIColor.white
]
gradient.locations = [0, 1]
gradient.startPoint = CGPoint(x: 1, y: 0)
gradient.startPoint = CGPoint(x: 1, y: 1)
gradient.frame = unreadIndicatorView.bounds
unreadIndicatorView.layer.insertSublayer(gradient, at: 0)
}
}
The result is this:
As you can see, the view at the bottom of the Text View is still solid. I was expecting it will go from transparent to white. Can you help me?
Thanks.
PS: BTW, the reason I call the setupGradientUnreadIndicator() in viewDidAppear() is because my whole scene is using auto-layout. So I put it there when the frame of the view is already calculated.
As mentioned by #Larme in the comment, it is because I used UIColor instead of CGColor in the gradient.colors. After I changed it to CGColor it worked fine now.

touchesBegan not responding in my code

//
// CommentViewController.swift
// Bordy
//
// Created by Micheal Tyler on 9/22/17.
// Copyright © 2017 Bordy,LLC. All rights reserved.
//
import UIKit
import FirebaseDatabase
import FirebaseAuth
class CommentViewController: UIViewController {
#IBOutlet weak var commentTextField: UITextField!
#IBOutlet weak var sendButton: UIButton!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var constraintToBottom: NSLayoutConstraint!
let postId = "-KuXb-8C5kijYNqOOihB"
var comments = [Comment]()
var users = [Users]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.estimatedRowHeight = 77
tableView.rowHeight = UITableViewAutomaticDimension
empty()
handleTextField()
loadComments()
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
/* This is where I have the code placed, when I tap on the screen in the simulator nothing is printed, my keyboard also doesnt hide if a user clicks on the screen to exit the textField. */
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true)
print("12121")
}
Since you're using a table view here, it's very likely that your table view's scroll view absorbs the touches before your view can get them.
If you want to hide the keyboard whenever the user taps on your table view, you may consider doing this with a UITapGestureRecognizer instead :
// MARK: LifeCycle
override func viewDidLoad() {
super.viewDidLoad()
tableView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleHideKeyboard)))
}
// MARK: User Interaction
func handleHideKeyboard() {
commentTextField.resignFirstResponder()
}
Note that you can use resignFirstResponder() method to hide your keyboard:
resignFirstResponder()
Notifies this object that it has been asked to relinquish its status
as first responder in its window.

onClick Action with Label does not work [duplicate]

I would like to make a UILabel clickable.
I have tried this, but it doesn't work:
class DetailViewController: UIViewController {
#IBOutlet weak var tripDetails: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
...
let tap = UITapGestureRecognizer(target: self, action: Selector("tapFunction:"))
tripDetails.addGestureRecognizer(tap)
}
func tapFunction(sender:UITapGestureRecognizer) {
print("tap working")
}
}
Have you tried to set isUserInteractionEnabled to true on the tripDetails label? This should work.
Swift 3 Update
Replace
Selector("tapFunction:")
with
#selector(DetailViewController.tapFunction)
Example:
class DetailViewController: UIViewController {
#IBOutlet weak var tripDetails: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
...
let tap = UITapGestureRecognizer(target: self, action: #selector(DetailViewController.tapFunction))
tripDetails.isUserInteractionEnabled = true
tripDetails.addGestureRecognizer(tap)
}
#objc
func tapFunction(sender:UITapGestureRecognizer) {
print("tap working")
}
}
SWIFT 4 Update
#IBOutlet weak var tripDetails: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(GameViewController.tapFunction))
tripDetails.isUserInteractionEnabled = true
tripDetails.addGestureRecognizer(tap)
}
#objc func tapFunction(sender:UITapGestureRecognizer) {
print("tap working")
}
Swift 5
Similar to #liorco, but need to replace #objc with #IBAction.
class DetailViewController: UIViewController {
#IBOutlet weak var tripDetails: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
...
let tap = UITapGestureRecognizer(target: self, action: #selector(DetailViewController.tapFunction))
tripDetails.isUserInteractionEnabled = true
tripDetails.addGestureRecognizer(tap)
}
#IBAction func tapFunction(sender: UITapGestureRecognizer) {
print("tap working")
}
}
This is working on Xcode 10.2.
Swift 3 Update
yourLabel.isUserInteractionEnabled = true
Good and convenient solution:
In your ViewController:
#IBOutlet weak var label: LabelButton!
override func viewDidLoad() {
super.viewDidLoad()
self.label.onClick = {
// TODO
}
}
You can place this in your ViewController or in another .swift file(e.g. CustomView.swift):
#IBDesignable class LabelButton: UILabel {
var onClick: () -> Void = {}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
onClick()
}
}
In Storyboard select Label and on right pane in "Identity Inspector" in field class select LabelButton.
Don't forget to enable in Label Attribute Inspector "User Interaction Enabled"
You need to enable the user interaction of that label.....
For e.g
yourLabel.userInteractionEnabled = true
For swift 3.0 You can also change gesture long press time duration
label.isUserInteractionEnabled = true
let longPress:UILongPressGestureRecognizer = UILongPressGestureRecognizer.init(target: self, action: #selector(userDragged(gesture:)))
longPress.minimumPressDuration = 0.2
label.addGestureRecognizer(longPress)
Pretty easy to overlook like I did, but don't forget to use UITapGestureRecognizer rather than UIGestureRecognizer.
Thanks researcher
Here's my solution for programmatic user interface using UIKit.
I've tried it only on Swift 5. And It worked.
Fun fact is you don't have to set isUserInteractionEnabled = true explicitly.
import UIKit
open class LabelButon: UILabel {
var onClick: () -> Void = {}
public override init(frame: CGRect) {
super.init(frame: frame)
isUserInteractionEnabled = true
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
}
public convenience init() {
self.init(frame: .zero)
}
open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
onClick()
}
}
Uses:
override func viewDidLoad() {
super.viewDidLoad()
let label = LabelButton()
label.text = "Label"
label.onClick = {
// TODO
}
}
Don't forget to set constraints. Otherwise it won't appear on view.
On top of all of the other answers, this depends on where the label is, it might be behind some subviews. You might think you tap on the label but maybe click the top view. To solve this you can bring the label view to the front with the following line.
self.view.bringSubviewToFront(lblView)
As described in the above solution
you should enable the user interaction first and add the tap gesture
this code has been tested using
Swift4 - Xcode 9.2
yourlabel.isUserInteractionEnabled = true
yourlabel.addGestureRecognizer(UITapGestureRecognizer(){
//TODO
})

Dismiss UITextField keyboard?

I have multiple textfields
#IBOutlet var emailLogin: UITextField!
#IBOutlet var passLogin: UITextField!
#IBOutlet var emailSignUp: UITextField!
#IBOutlet var passSignUp: UITextField!
as of now these aren't really needed, because of how I am dismissing it by tapping anywhere on the screen, however I also want it to dismiss when I press the return key.
override func viewDidLoad() {
super.viewDidLoad()
self.emailLogin.delegate = self
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(LoginViewController.dismissKeyboard))
view.addGestureRecognizer(tap)
}
First question: When I need to dismiss with only one I would set the delegate like so, but how do I handle this when I have multiple views that need to be dismissed on return key?
Also, there are two separate views, but both use the same class. Is this a problem for what I am trying to do?
From here
func dismissKeyboard() {
view.endEditing(true)
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
This dismisses the keyboard but only for the textfield that I set as self.
Any info is appreciated!
EDIT:
Change your code to the following and it should work:
#IBOutlet var emailLogin: UITextField?
#IBOutlet var passLogin: UITextField?
#IBOutlet var emailSignUp: UITextField?
#IBOutlet var passSignUp: UITextField?
emailLogin?.delegate = self
passLogin?.delegate = self
emailSignUp?.delegate = self
passSignUp?.delegate = self
The IBOutlets from other class were not initialized when your LoginViewController loads, thus end up with unwrapping nil objects, which is not allowed in Swift. (You should be able to see that in your console output in Xcode.) Use optional variables will prevent that from happening.
Use ViewControllerDelegate to handle tap outside of any textField and textView. It will dismiss keyboard when you tap on outside of textField. Put below code in your view controller:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesBegan(touches, withEvent:event)
self.view.endEditing(true)
}
You don't need to add TapGesture now to handle this.

Coordinates of image area tapped in swift programming

I'm new to Swift app development and I would like to know how to add touch events based on where an Image is clicked using Swift. I need to get the coordinates of the area the image is tapped.
You'll need a tap gesture recognizer on the image view, you also need to set the user interaction property to enabled. Then you can get the point from the gesture recognizer's method. Here's some quick code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: Selector("tapAction:"))
self.imageView.userInteractionEnabled = true
self.imageView.addGestureRecognizer(tapGestureRecognizer)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tapAction(sender: UITapGestureRecognizer) {
let touchPoint = sender.locationInView(self.imageView) // Change to whatever view you want the point for
}
}
UPDATE 2017:
Now Selector(String) is deprecated. One can use the new syntax #selector.
Also, the colon at the end is not needed.
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapAction))