I know how to set UIPanGesture events and recognizers and get the data I need when I create them programatically.
However I am trying to learn how to work with the storyboard and use IBOutlets.
I have dragged over an IBAction for 'Touch Drag Inside'.
#IBAction func dragButtonDragEvent(sender: AnyObject)
{
println(sender)
}
The problem is the sender object I get is just the button itself.
Is there a way to get the drag distance or drag event data when using an IBAction?
I tried
#IBAction func dragButtonDragEvent(sender: AnyObject)
{
var drag:UIPanGestureRecognizer = sender as UIPanGestureRecognizer
println(drag.state)
}
But I get a bad memory access error and the println is not executed as the sender is the button NOT the event.
Can you suggest how to use Touch Drag Inside events when connecting from a Storyboard?
Create the #IBAction function with an event parameter like this:
#IBAction func touchDragInsideAction(sender: AnyObject, event: UIEvent) {
// if the sender is a button
if let button = sender as? UIButton {
// get the touch inside the button
let touch = event.touchesForView(button)?.anyObject() as UITouch
// println the touch location
println(touch.locationInView(button))
}
}
This will println the user's touch location on the button as they drag their finger across the button. You can then use the first point and last point to get the distance.
Please note, if you're modifying the method signature of the existing function, then you'll have to reconnect it in the storyboard.
Change the arguments of IBAction to "Sender and Event".
Related
I have got a wide LineChart with many entries. I want to let user tap (or better longtap/3D touch) on an entry to show modal card where user could edit data entry. I tried implementing chartValueSelected but the problem is that it runs even when user taps to scroll (i e taps without releasing finger) which is not how a button should behave. Is there any way to implement tap recognizing for LineChart label?
It appears that overriding the tap gesture recognizer for the chart can work. This question has some answers based on someone who was looking for a similar solution.
You can attach your own gesture recognizer to LineChartView and use method getHighlightByTouchPoint to get information about the selected point.
override func viewDidLoad() {
// ...
let longTapRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(onLongTap))
lineChartView.addGestureRecognizer(longTapRecognizer)
// ...
}
#objc func onLongTap(recognizer: UILongPressGestureRecognizer) {
if recognizer.state == .ended {
let highlight = lineChartView.getHighlightByTouchPoint(recognizer.location(in: lineChartView))
print("\(highlight)")
}
}
To set the stage, I have a 4x5 grid of UIImageViews that I would like to flip and show a new image upon tap. I'd like to accomplish this with a single #IBaction. I'm simply having a bit of trouble referencing the selected UIIMageView when a tap is recognized. I'm sure it's something very simple. I'm just now starting to work with UITapGestureRecognizer, so I don't know all the ins and outs just yet. Here's the #IBAction I'm trying to use:
#IBAction func tileTapped(recognizer: UITapGestureRecognizer, _ sender: UIImageView) {
print("Tile Tapped: \(sender.tag)")
}
My print statement is giving me the following no matter what UIImageView is tapped:
Tile Tapped: 0
My tags are set up to reference the row and column they fall in. For example, the tags for my first row are:
00, 01, 02, 03
My biggest challenge is simply retrieving the tag for the appropriate UIIMageView. Once I figure that out, I should be solid.
Thanks in advance!
That is not a valid function for a gesture recognizer. See the documentation for UIGestureRecognizer on the possible signatures.
To access the view associated with the gesture, access the gesture's view property.
#IBAction func tileTapped(_ recognizer: UITapGestureRecognizer) {
if let view = recognizer.view as? UIImageView {
print("Tile Tapped: \(view.tag)")
}
}
I have a custom UIControl that has three subviews. Each of those subviews, I add a target:
button.addTarget(self, action: #selector(buttonTapped(clickedBtn:)), for: .touchUpInside)
Within that function buttonTapped, it does some special animations to do some transitions (It mimics the segmented control).
Now, within the ViewController that this custom UIControl exists in must know when it's touched. I created an #IBAction function that interacts with the touch events for the custom UIControl.
The problem is, that isn't possible (as far as I know). If I add a target touch event to the subviews, the parent touch events won't get called. To have the parent view called the #IBAction function, I must set all the subview's setUserInteractiveEnabledtotrue`. When I do that, the subview's touch event functions won't get called.
I need both touch event functions to be called. How can I do this? Or what's the best way to get around this?
Use delegates, add a protocol in your UIControl that needs to be implemented in your ViewController.
This way you can detect if a button is clicked in your UIControl and invoke a specific function in your VC.
For Example:
//YourUIControl.Swift
protocol YourUIControlDelegate {
func didTapFirstButton()
}
class YourUiControl : UIView { //I'm assuming you create your UIControl from UIView
var delegate : YourUIControlDelegate?
//other codes here
.
.
.
#IBAction func tapFirstButton(_ sender: AnyObject) {
if let d = self.delegate {
d.didTapFirstButton()
}
}
}
//YourViewController.Swift
extension YourViewController : UIControlDelegate {
func didTapFirstButton() {
//handle first button tap here
}
}
I see that there are a ton of these questions, and I think I'm following the accepted Swift 3 methodology, but I'm still getting nothing. I can see that the UITapGestureRecognizer has been attached. Here's my code:
let tileClick = UITapGestureRecognizer(target: self, action: #selector(GameManagement.initiateTileClick(_:)))
newView.addGestureRecognizer(tileClick)
newView.isUserInteractionEnabled = true
func initiateTileClick(_ sender: UITapGestureRecognizer) {
print("initiate tile click")
}
A few things to note:
1) The view that I'm attaching the gesture recognizer to has a two views and a label within it that each cover the entire frame of the view, however, I tried attaching the recognizer to the label, which is the topmost child item and it still doesn't work.
2) Both the function that adds the recognizer and the function that is called on the tap are contained in an NSObject file. I have a variety of interconnected functions that I want to be able to call from multiple view controllers and would prefer to keep this in the separate NSObject file. The process worked when I had everything in a UIViewController file and stopped working when I moved the functions to the NSObject file.
3) I've tried changing GameManagement.initiateTileClick to self.initiateTileClick or just initiateTileClick and none of those worked.
If you are putting your views inside NSObject subclass then these views will lose their behaviors for UIResponder which manages the UI interactions as I am not able to see how you are adding these views to interface.
As you said, it was working inside ViewController because it manages view hierarchy and responder chain.
The solution would be to write extensions to separate code or better abstractions.
extension YourViewController {
newView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(GameManagement.initiateTileClick(_:))))
newView.isUserInteractionEnabled = true
func initiateTileClick(_ sender: UITapGestureRecognizer) {
print("initiate tile click")
}
}
Giving you an idea how the tap recogniser works.
Firstly add Tap gesture recogniser to your view controller. You have to put the object here as shown in the image.
Then control+drag the tap gesture object to your view and select delegate.
Then control+drag the recogniser to your swift file and action will be like this.
#IBAction func tapGesture(_ sender: UITapGestureRecognizer) {
}
Now you must have seen when you give some input to a text field, the keyboard appears. But if you press outside the text field, that is anywhere in the view, the keyboard hides. This is because of the tap gesture recogniser.
Consider you have a text field such that if you click in that text field, keyboard is appeared. But when you tap outside the textfield, the keyboard must hide.
Add this delegate
UITextFieldDelegate
Implement this:
#IBOutlet var phoneText: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
exampleText.delegate = self
}
#IBAction func tapGesture(_ sender: UITapGestureRecognizer) {
exampleText.endEditing(true)
}
Obviously,this function is instance method.
func initiateTileClick(_ sender: UITapGestureRecognizer) {
print("initiate tile click")
}
-
UITapGestureRecognizer(target: self, action:#selector(GameManagement.initiateTileClick(_:)))
but thisGameManagement.initiateTileClick(_:) looks like a class is calling a class method!The target should be the caller of method.self can't call GameManagement.initiateTileClick(_:).
I am working with xcode to create a view that allows users to drag buttons. with the code below, I can move the button to the touch and drag from there, but I cant click the button and drag.
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for obj in touches {
let touch = obj as! UITouch
let location = touch.locationInView(self.view)
word1Button.center = location
}
}
Buttons respond to touch events, so when the user touches down within the bounds of a button the view underneath will not receive those touch events. You can get around this by using a gesture recogniser on your button instead of relying on the lower level touch delivery methods. A long press gesture recognizer would probably work best:
// Where you create your button:
let longPress = UILongPressGestureRecognizer(target: self, action: "handleLongPress:")
word1Button.addGestureRecognizer(longPress)
//...
func handleLongPress(longPress: UILongPressGestureRecognizer) {
switch longPress.state {
case .Changed:
let point = longPress.locationInView(view)
button.center = point
default:
break
}
}
Note that by default, the UILongPressGestureRecognizer needs the user to hold down for 0.5 seconds before the gesture starts recognizing (and therefore starts dragging). You can change this with the minimumPressDuration property of UILongPressGestureRecognizer. Be careful not to make it too short though - as soon as the gesture recognizes it will cancel other touches to the button, preventing the button action from being fired when the touch is lifted.