I have a text field which can be clicked to edit, and dragged along the y-axis using a UIPanGesture.
When the user clicks the text field, I want to set the textAlignement to.left & when the user drags the text field around, I want to keep the text center aligned.
I initialise in the viewDidLoad method:
let dragText = UIPanGestureRecognizer(target: self, action: #selector(userDragged))
textOverlay.addGestureRecognizer(dragText)
textOverlay.addTarget(self, action: #selector(textClicked), for: UIControlEvents.touchDown)
Callback methods:
func textClicked() {
//let actLocation = textOverlay.frame.origin.y
textOverlay.frame.origin.y = self.finalKBH
self.textOverlay.textAlignment = .left
print("Text clicked")
}
func userDragged(gesture: UIPanGestureRecognizer) {
let loc = gesture.location(in: self.view)
self.textOverlay.frame.origin.y = loc.y
self.textOverlay.textAlignment = .center
print("Text dragged")
}
The problem I'm having, is differentiating between an actual 'click-to-edit' and drag gesture.
While dragging the text field, the textClicked function is also called and the text is aligned to the left.
Is there a way I can differentiate between these two actions?
I've attempted to change the textClicked targer to UIControlEvents.touchUpInside but then my function isn't being registered in the console.
Many thanks in advance
For detecting when the textfield enters editing mode, implement the textFieldDidBeginEditing(_:) method of UITextFieldDelegate
override func viewDidLoad()
{
super.viewDidLoad()
// ...
textField.delegate = self
}
func textFieldDidBeginEditing(_ textField: UITextField)
{
print("Text clicked")
}
Maybe, long press if you have to use an overlay for some reason?
let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: "longPressed:")
textOverlay.addGestureRecognizer(longPressRecognizer)
Related
I created a floating action button which i would like to dismiss all the action button when a user either taps the FAB when it's open or when the user taps anywhere on the screen and FAB is open. It is also important to note that the FAB is being displayed over a tableview and i want to retain the ability to select tableview cells.
In my implementation of the FAB i added a target to the FAB button which i use to open and close the FAB and i also implemented a tapGesture on the viewController with the tableview such that when a tap gesture is invoked i can close the FAB if open.
To make this work i did a bit of research and found out that i have to set
tap.cancelsTouchesInView = false
so that the tableView events continue working. However the side effect is that when i tap on the fab to close it two events are fired one from the tapGesture of the FAB and another from the button target which results in the FAB not closing when u tap on it if its open.
Is there a more elegant way of making the FAB be able to close when tapped whilst its open and also have the tap Gesture on the viewController close the fab when its open and a user taps anywhere on the screen.
Here is some of my code:
ViewController:
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self,
action: #selector(self.dismissActionButtons(_:)))
self.view.addGestureRecognizer(tap)
tap.cancelsTouchesInView = false
self.floatingActionButton.isHidden = true
}
#objc func dismissActionButtons(_ sender: UIButton) {
if !floatingActionButton.actionButtonsCarousel.isHidden {
self.floatingActionButton.animateActionButtonsDisappering()
}
}
Custom FAB View :
override init(frame: CGRect) {
super.init(frame: .zero)
actionButtonsCarousel.isHidden = true
self.translatesAutoresizingMaskIntoConstraints = false
self.mainButton.addTarget(self,
action: #selector(ACTFAB.fabButtonAction(_:)),
for: .touchUpInside)
}
#objc func fabButtonAction(_ sender: UIButton) {
if self.actionButtonsCarousel.isHidden {
self.animateActionButtonsAppering()
} else {
self.animateActionButtonsDisappering()
}
}
func animateActionButtonsAppering() {
self.actionButtonsCarousel.alpha = 0.0
self.actionButtonsCarousel.isHidden = false
UIView.transition(with: self, duration: 0.5, options: .preferredFramesPerSecond60, animations: {
self.actionButtonsCarousel.alpha = 1.0
})
self.mainButton.setImage(UIImage(named: "fab-open-icon"), for: .normal)
}
func animateActionButtonsDisappering() {
self.actionButtonsCarousel.alpha = 1.0
self.actionButtonsCarousel.isHidden = true
UIView.transition(with: self, duration: 0.3, options: .transitionCrossDissolve, animations: {
self.actionButtonsCarousel.alpha = 0.0
})
self.mainButton.setImage(UIImage(named: "fab-closed-icon"), for: .normal)
}
Two valid scenarios:
1 FAB is open -> click FAB -> FAB closes
2 FAB is open -> click anywhere other than FAB -> FAB closes
Scenario number 1 fails with my current code.
If I understand your question correctly, the issue is that tapping the FAB causes both the button's action to be fired but also, as you are passing the event through to the underlying viewController, the gestureRecogniser to fire too.
I'm assuming the button action is the primary event, and that when this fires you need to stop the gestureRecogniser. A gestureRecogniser has a .location(in:) method which allows you to get the first tap location (for a tapGestureRecogniser) in terms of any view, and a UIView has a .point(inside: with:) method that checks whether a CGPoint (in terms of its own coordinate space) is inside it bounds. Therefore you should be able to do something like this (from memory and not compiled, so may need some tweaking but hopefully it should get you started):
#objc func dismissActionButtons(_ sender: UIButton) {
let tapPoint = sender.location(in: customFABview)
if customFABview.point(inside: tapPoint, with: nil) &&
!floatingActionButton.actionButtonsCarousel.isHidden {
self.floatingActionButton.animateActionButtonsDisappering()
}
}
Continuing with #flanker's answer i created a boolean in the fab to check if the event was coming from tapGesture then i added it to the check conditional statement as follows:
var isComingFromGestureEvent: Bool = false
#objc func fabButtonAction(_ sender: UIButton) {
if self.actionButtonsCarousel.isHidden && !isComingFromGestureEvent {
self.animateActionButtonsAppering()
} else {
self.animateActionButtonsDisappering()
}
}
In the ViewController i then just used flanker's answer to set the boolean state as follows :
var locationInView: CGPoint = CGPoint(x: 0, y: 0)
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self,
action: #selector(self.dismissActionButtons(_:)))
self.view.addGestureRecognizer(tap)
pointInView = tap.location(in: floatingActionButton)
tap.cancelsTouchesInView = false
self.floatingActionButton.isHidden = true
}
#objc func dismissActionButtons(_ sender: UIButton) {
let tapPoint = pointInView
if floatingActionButton.point(inside: tapPoint, with: nil) &&
!floatingActionButton.actionButtonsCarousel.isHidden {
self.floatingActionButton.isComingFromGestureEvent = true
self.floatingActionButton.animateActionButtonsDisappering()
} else {
self.floatingActionButton.isComingFromGestureEvent = false
}
}
I'm building an application in Swift 3, so I want to call a function if I click on a particular UILabel, so I'm write this code but not works:
let tap = UITapGestureRecognizer(target: self, action: #selector(ViewController.tapFunction))
self.labelTemp.isUserInteractionEnabled = true
self.labelTemp.addGestureRecognizer(tap)
How can I render UILabel clickable ?
Set user interaction enabled for the UILabel and add the below code in the viewDidLoad()
self.label.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(self.labelTapped))
self.label.addGestureRecognizer(tap)
Add the tap action function as below :
#objc func labelTapped(_ gestureRecognizer: UITapGestureRecognizer) {
print("Label clicked")
}
Please make user that there is no other transparent view overlapping the UILabel in the view. If the UILabel is a part of another view then please make sure that the container View's user interaction is enabled.
Hope this helps.
Your selector should be an #objc func within self.
<#YourLabel#>.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleLabelTap)))
And when the user taps the label it will trigger:
#objc func handleLabelTap() {
// handle label tap here
}
You are lacking a function to trigger when the gesture touch is recognized. You need to add following:
let tap = UITapGestureRecognizer(target: self, action: #selector(tapFunction(_:)))
self.labelTemp.isUserInteractionEnabled = true
self.labelTemp.addGestureRecognizer(tap)
#objc func tapFunction(_ gestureRecognizer: UITapGestureRecognizer) {
// handle label tap here
}
Please ensure that You have connected the outlet to UILabel because I have created simple demo code by copy-paste your code and it is worked as expected.
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(tapFunction))
self.labelTemp.isUserInteractionEnabled = true
self.labelTemp.addGestureRecognizer(tap)
}
#objc func tapFunction() {
print("tapFunction")
}
I suggest, Please remove UILabel from UIViewController and add it again.
Download sample code
Note: - Please ensure that user-interaction of UILabelis enabled
First you need to add Tap Gesture into storyboard
Create Action of that particular gesture
override func viewDidLoad() {
super.viewDidLoad()
let tapOnLabel = UITapGestureRecognizer(target: self, action: #selector(self.tapGestireAction))
self.labelTemp.isUserInteractionEnabled = true
self.labelTemp.addGestureRecognizer(tapOnLabel)
}
#IBAction func tapGestureAction(_ sender: UITapGestureRecognizer) {
//Perform action
}
I'm trying to add a tap gesture to an outlet collection of labels [UILabel], like this:
#IBOutlet var subLabels: [UILabel]!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(HomePageViewController.selectSubLabel(tap:)))
tap.numberOfTapsRequired = 1
tap.cancelsTouchesInView = false
for i in (0..<(subLabels.count)) {
subLabels[i].addGestureRecognizer(tap)
}
}
func selectSubLabel(tap: UITapGestureRecognizer) {
print("Gesture Is WORKING!")
}
and i tried to add it on a single label in storyboard; but NONE are working.
Firstly, you need to allow user interaction on a label (it is turned off by default):
for i in (0..<(subLabels.count)) {
subLabels[i].isUserInteractionEnabled = true
subLabels[i].addGestureRecognizer(tap)
}
but gesture recognizer can observe for gestures only in one view.
So, there are two options:
I. Dedicated gesture recognizer for every label
for i in (0..<(labels.count)) {
let tap = UITapGestureRecognizer(target: self, action: #selector(selectSubLabel(tap:)))
labels[i].isUserInteractionEnabled = true
labels[i].addGestureRecognizer(tap)
}
II. One gesture recognizer for the parent view of the labels
override func viewDidLoad() {
super.viewDidLoad()
for i in (0..<(labels.count)) {
subLabels[i].isUserInteractionEnabled = true
}
let tap = UITapGestureRecognizer(target: self, action: #selector(selectSubLabel(tap:)))
view.addGestureRecognizer(tap)
}
func selectSubLabel(tap: UITapGestureRecognizer) {
let touchPoint = tap.location(in: view)
guard let label = subLabels.first(where: { $0.frame.contains(touchPoint) }) else { return }
// Do your stuff with the label
}
Please check the User Interaction Enabled Attribute of your UIlabel's in Attribute inspector of Xcode. User Interaction Enabled must be ticked for detecting the tap. Please see the picture below,
I read a lot try to find a way to handle uitextfield click but nothing working for me
What I want:
I want add tap on UITextField and open dialog
What I tried:
class CreateMessagesViewController: UIViewController {
#IBOutlet weak var testField: UITextField!
#IBOutlet weak var testView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
// testField.addGestureRecognizer(tap)
testView.addGestureRecognizer(tap)
testField.addTarget(self, action: #selector(onTapped(_:)), for: .touchDown)
}
func onTapped(_ sender : UITextField){
print("Hello World")
}
func handleTap(_ sender: UITapGestureRecognizer) {
print("Hello World")
}
}
As you can see I tried 2 ways:
UITapGestureRecognizer
selector
Just for the test I add simple UIView to make sure tap is working and it worked.
Why it doesn't work on uiTextField? Is there anything I have missed?
Instead of adding gesture recognizer you can write the code for opening dialog in
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool{
openDialog()//Function which will open the dialog
return false
}
I think it should have the same effect as you want.
1)add gesture recognizer:
let tapGR = UITapGestureRecognizer(target: self, action: #selector(someFunc))
tapGR.delegate = self
textField.addGestureRecognizer(tapGR)
2)make sure that you allow your tap handler when it is necessary:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true //or add your custom check here if you need
}
Without of the second part you get a buggy textField where sometimes tap gesture doesn't work, sometimes default gestures not handled!
There is a simpler way to achieve this. You should conform your view controller class to UITextFieldDelegate and implement textFieldDidBeginEditingin your class.
func textFieldDidBeginEditing(_ textField: UITextField){
//show popup here
}
If you are having multiple text fields, assign a tag to your text field in the storyboard(or in the code) to recognize the text field.
func textFieldDidBeginEditing(_ textField: UITextField){
// check your tag here
if textField.tag == 0 {
//show your popup here
}
}
Add GestureRecognizer to UITextField superview and check if help:
testField.superview.addGestureRecognizer(tap)
I created sample one for your question both Swift and Objective C but it not working.I used the Tap Gesture recognizer code for textField but it not even called.
Not Worked below Code in Swift and Objective C
Swift
let tap = UITapGestureRecognizer(target: self,action: #selector(handleTaponTextField(_:)))
tap.numberOfTapsRequired = 1
tap.delegate = self
txtFldTapMe.isUserInteractionEnabled = true
txtFldTapMe.addGestureRecognizer(tap)
func handleTaponTextField(_ sender: UITapGestureRecognizer)
{
print("Tap is handled here!")
}
Objective C
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
tapGesture.numberOfTapsRequired = 1;
tapGesture.delegate = self;
txtfldTapME.userInteractionEnabled = YES;
[txtfldTapME addGestureRecognizer:tapGesture];
func handleTaponTextField(_ sender: UITapGestureRecognizer)
{
print("Tap is handled here!")
}
Then I tried with below addTarget action method for textField and it works fine.
Worked below Code in Swift and Objective C
Swift
txtFldTapMe.addTarget(self, action: #selector(textFieldEditDidBegin(_:)), for: .editingDidBegin)
func textFieldEditDidBegin(_ textField: UITextField) {
print("Text editing begin here!")
}
See the methods
You should use above methods of textField
Objective C
[txtfldTapME addTarget:self action:#selector(editingDidBeginStarted:) forControlEvents: UIControlEventEditingDidBegin];
-(void)editingDidBeginStarted:(UITapGestureRecognizer *)sender{
NSLog(#"Editing started here");
}
See the below textField methods
Try below code
yourTextField.addTarget(self, action: #selector(didChangeText), for: .editingChanged)
addSubview(view)
#objc func didChangeText(textField:UITextField) {
let str = textField.text
//your functionality here
}
It's not possible. Because Textfield delegate method call.
I want to trigger two action on button click and button long click. I have add a UIbutton in my interface builder. How can i trigger two action using IBAction can somebody tell me how to archive this ?
this is the code i have used for a button click
#IBAction func buttonPressed (sender: UIButton) {
....
}
can i use this method or do i have to use another method for long click ?
If you want to perform any action with single tap you and long press the you can add gestures into button this way:
#IBOutlet weak var btn: UIButton!
override func viewDidLoad() {
let tapGesture = UITapGestureRecognizer(target: self, #selector (tap)) //Tap function will call when user tap on button
let longGesture = UILongPressGestureRecognizer(target: self, #selector(long)) //Long function will call when user long press on button.
tapGesture.numberOfTapsRequired = 1
btn.addGestureRecognizer(tapGesture)
btn.addGestureRecognizer(longGesture)
}
#objc func tap() {
print("Tap happend")
}
#objc func long() {
print("Long press")
}
This way you can add multiple method for single button and you just need Outlet for that button for that..
#IBOutlet weak var countButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
addLongPressGesture()
}
#IBAction func countAction(_ sender: UIButton) {
print("Single Tap")
}
#objc func longPress(gesture: UILongPressGestureRecognizer) {
if gesture.state == UIGestureRecognizerState.began {
print("Long Press")
}
}
func addLongPressGesture(){
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(longPress(gesture:)))
longPress.minimumPressDuration = 1.5
self.countButton.addGestureRecognizer(longPress)
}
Why not create a custom UIButton class, create a protocol and let the button send back the info to delegte. Something like this:
//create your button using a factory (it'll be easier of course)
//For example you could have a variable in the custom class to have a unique identifier, or just use the tag property)
func createButtonWithInfo(buttonInfo: [String: Any]) -> CustomUIButton {
let button = UIButton(type: .custom)
button.tapDelegate = self
/*
Add gesture recognizers to the button as well as any other info in the buttonInfo
*/
return button
}
func buttonDelegateReceivedTapGestureRecognizerFrom(button: CustomUIButton){
//Whatever you want to do
}