Dragging UIView loses the gesture when ended - ios

I have the following code that drags a UIView. All works fine visually.
func moveView(sender: UIPanGestureRecognizer) {
let translate = sender.translationInView(self.view)
if sender.state == UIGestureRecognizerState.Changed {
sender.view!.center = CGPoint(x:sender.view!.center.x + translate.x, y:sender.view!.center.y + translate.y)
sender.setTranslation(CGPointZero, inView: self.view)
}
if sender.state == UIGestureRecognizerState.Ended {
let newX: CGFloat = sender.view!.center.x + translate.x
let newY: CGFloat = sender.view!.center.y + translate.y
sender.view!.center = CGPoint(x:newX, y:newY)
}
}
However after completing this drag, the view seems to lose the gesture connection such that I can't drag it again or trigger any tap gesture associated with it etc.
If I add an NSLog I can see that tapping where the view used to be triggers the log but not if I tap on the actual current view location.
I establish the gesture to view thisView with the following within viewDidLoad
let moveGesture = UIPanGestureRecognizer(target: self, action: Selector("moveView:"))
thisView.addGestureRecognizer(moveGesture)
What am I missing that keeps the gestures connected to the new view location?
Thanks.

I think that when you call this sender.setTranslation(CGPointZero, inView: self.view) maybe you should set the translation relative to superview and not to the view itself. Setting the translation relative to itself change the position of the view contents but not the view area, which means that if you set the view layer to mask to its bounds you shouldn't see anything that it's outside of the view initial area.
So you should do sender.setTranslation(CGPointZero, inView: self.view.superview).

Related

How do you drag and delete ??? but only when you touch that red bin button given in https://i.stack.imgur.com/KHCtG.jpg

I have a sticker which is draggable as I drag it and while dragging it when my finger reaches below the white board and that delete imageview appears and when I have reached there location. How do I detect touch on that imageview.
I am using pan gesture for dragging and pinch and rotation gestures also at the same time. But how will I know that I have touched my delete ImageView
Here is the image which has a stickerView on the back which is draggable and has a delete imageView on the front of the stickerView which is fixed.
#objc func handlePanGesture(_ gestureRecognizer:UIPanGestureRecognizer){
switch gestureRecognizer.state {
case .began:
// panGestureInStickerViewClosure?(true)
break
case .changed:
// panGestureInStickerViewClosure?(true)
let deleteImageView = gestureRecognizer.view?.superview?.subviews.filter{$0.isKind(of: UIImageView.self)}[safe: 0]
deleteImageView?.addGestureRecognizer(BlockGestureRecognizer())
let stickerImageViewFrame = gestureRecognizer.view?.superview?.subviews.filter{$0.isKind(of: HBStickerView.self)}[safe: 0]
let translation = gestureRecognizer.translation(in: self)
print("translation=\(translation),deleteImageView?.frame=\(deleteImageView?.frame),stickerImageViewFrame?.frame=\(stickerImageViewFrame?.frame)")
break
case .ended:
break
default:
break
}
print(gestureRecognizer.numberOfTouches)
if gestureRecognizer.numberOfTouches == 1{
panGestureInStickerViewClosure?(true)
}else if gestureRecognizer.numberOfTouches > 1{
panGestureInStickerViewClosure?(false)
}
let translation = gestureRecognizer.translation(in: self.superview)
self.center = CGPoint(x: self.center.x + translation.x, y: self.center.y + translation.y)
gestureRecognizer.setTranslation(CGPoint.zero, in: self.superview)
print(gestureRecognizer.location(in: self.superview))
//print("self.frame.maxY=\(self.frame.maxY),self.superview?.frame.maxY=\(self.superview?.frame.maxY),self.center=\(self.center),gestureRecognizer.location(in: self)=\(gestureRecognizer.location(in: self))")
// panGestureInStickerViewClosure?(true)
}
I have above code written for pan gesture
I am using this gesture recognizer for panning. But getting translation location also does not work for me. If I can get the gesture location for my thumb or any finger and perform my need.
All I want to get touch event on that delete ImageView in front of all Subview while I am dragging the StickerView below that Subview.
Let me give you an example of Instagram. An Instagram has stickers which you can drag around and pinch and rotate and when it comes to deleting you have to touch on that delete button then the delete button gets scaled to max size and StickerView gets to scaled-down and finally is removed from the superView.

UIView's frame is not shown updated with UIPanGestureRecognizer

I have a subview in a viewController to which I added a UIPanGestureRecognizer. I'd like to expand such subview when the user drags it down, so I'm trying to handle the gesture recognizer status like this:
#objc func panning(_ gestureRecognizer: UIPanGestureRecognizer) {
let translation = gestureRecognizer.translation(in: view)
if gestureRecognizer.state == .began {
originalFrame = self.mySubview.frame
} else if gestureRecognizer.state == .changed {
if translation.y > 0 {
var newFrame = originalFrame
newFrame.size.height += translation.y
mySubview.frame = newFrame
}
}
}
when I set a breakpoint at mySubview.frame = newFrame, I see that newFrame is updated with the translation. However, I don't see the frame changes in the screen, the subview looks always like at the beginning.
What could I be missing?
PanGesture is a tricky, but working with it is very interesting...
I am assuming you have added pan gesture correctly to the view. So writing down the handling of the gesture only.
func handlePanGesture(_ gesture: UIPanGestureRecognizer) {
var translatedPoint: CGPoint = gesture.translation(in: self.superview)
let playerCenterX: CGFloat = (gesture.view?.center.x)! + translatedPoint.x
let playerCenterY: CGFloat = (gesture.view?.center.y)! + translatedPoint.y
translatedPoint = CGPoint.init(x: playerCenterX,
y: playerCenterY)
gesture.view?.center = translatedPoint
gesture.setTranslation(CGPoint.zero, in: self.superview)
}
Important thing is that you set its translation back to zero every time at the end of gesture movement.
As per your requirement change this self.superview and use the coordinates to update your view frame...
If your view's frame is set by the autolayout you should update constraints, not the view's frame itself.

Swift rotation gesture x and y off axis issue

I'm using a rotation gesture recognizer to rotate a UIView. After I've rotated the view the x and y coordinates of the pan gesture are off their axis and I can't see why.
Here is my current gesture setup:
open func handleRotate(sender: UIRotationGestureRecognizer) {
if sender.state == .began || sender.state == .changed {
sender.view?.transform = (sender.view?.transform)!.rotated(by: sender.rotation)
sender.rotation = 0
}
}
#objc fileprivate func handleDrag(sender: UIPanGestureRecognizer) {
let translation = sender.translation(in: self)
sender.view!.center = CGPoint(x: sender.view!.center.x + translation.x, y: sender.view!.center.y + translation.y)
sender.setTranslation(.zero, in: self)
}
So when I rotate the UIView the x and y panning are off (Moving up moves the uiview to the left or right depending upon the UIview's rotated state).
Thanks
There's quite a lot to unpack with the code example provided, but for starters the drag handler is setting the center point of the view, which might be a hint as to why the rotation gesture is askew.
Rather than setting the transform property of the view independently in each handler, you might get the result you're looking for if you modify the existing transforms.
The SO answer here goes into a bit more depth, and might help:
Using multiple UIGestureRecognizers simultaneously like UIRotationGestureRecognizer & UIPanGestureRecognizer in Swift 3

How to Make UIAction button Bounce Back when out of Bounds

I have a movable UIAction Button similar to Reddit's Scroll up button and the Assistive Touch Feature. Right now I'm having a hard time finding a way to either:
1. Not go out of bounds in the first place or
2. Bounce Back to the screen similar tot he way that Assistive Touch does.
Here's what I have in my UIPanGesture function:
#IBAction func dragSettingsButton(_ sender: UIPanGestureRecognizer) {
if sender.state == .began || sender.state == .changed {
if displayView.frame.contains(sender.location(in: self.view)) {
let translation = sender.translation(in: displayView)
// note: 'view' is optional and need to be unwrapped
sender.view!.center = CGPoint(x: sender.view!.center.x + translation.x, y: sender.view!.center.y + translation.y)
sender.setTranslation(CGPoint.zero, in: displayView)
}
else {
print("WOW BRO YOURE OUT OF BOUNDS")
}
}
}
Things to note: I have a ViewController where there are 3 views: A view to act as a custom navigation bar, a view to act as a custom segmented view, and a display view to display different views when the segmented view is switched.
There is also a Tab bar on the bottom
To bounce back:
On sender.state == .end use the same logic to determine if its out of bounds. If so calculate where it should be. Then animate it to that location.

Drag UIImageView and Drop in another UIImageView

I have a UIScrollView that contains n number of placeholder UIImageView's. Below that i have a StackView containing 10 images that the user will choose from and drag an image to a specific placeholder image view above. I have the PanGesture implemented and that works fine, however, i'm struggling with how to detect when the draggable image is within the bounds of a placeholder image. Especially since it is in a scroll view. I was able to get it to some what work with the center.X and center.Y of the draggable views then checking those values against their relative values of the placeholders, but it did not work as desired. And from what i can see, the X and Y coordinates from dragging, only apply to the actual parent UIView. So, when an image is dragged into the scroll view, the X values don't necessarily line up, some of those placeholder image X coordinates may be 2000+ if the list is long. Take a look at the code below and let me know your thoughts and suggestions. Thanks!
#IBAction func handlePan(recognizer:UIPanGestureRecognizer) {
let translation = recognizer.translationInView(self.view)
if recognizer.state == UIGestureRecognizerState.Began {
originalViewLocation = recognizer.view!.center
}else if recognizer.state == UIGestureRecognizerState.Ended {
for placeHolderView in playerImages{
if ((recognizer.view!.center.x < (placeHolderView.center.x + (placeHolderView.frame.width / 2)) && recognizer.view!.center.x > placeHolderView.frame.origin.x)){
if recognizer.view!.center.y < (stackView.center.y + stackView.frame.height / 2){
let chosenImage = recognizer.view! as! UIImageView
placeHolderView.image = chosenImage.image
}
}else{
UIView.animateWithDuration(0.5, delay: 0.0, options: .CurveEaseInOut, animations: {
recognizer.view!.center = self.originalViewLocation
}, completion: { finished in
})
}
}
}
if let view = recognizer.view {
view.center = CGPoint(x:view.center.x + translation.x,
y:view.center.y + translation.y)
}
recognizer.setTranslation(CGPointZero, inView: self.view)
print("X: \(recognizer.view!.center.x)")
print("Y: \(recognizer.view!.center.y)")
}
The playerImages array stores the UIImageViews in the scroll view.
Well i did solve this on my own. It had to do with the relative coordinate values vs actual coordinate values.
I ended up needing a few things...
CGRectContainsPoint()
To check whether or not the frame of the destination placeholder view contains the center point of the dragging view.
And...
convertRect()
with
convertPoint()
I had to convert the coordinate values of the dragging point to the values of the superview. Along with, converting the frame values of the placeholder views within the scroll view, to the same (superview values). Like so...
let convertedCenter = recognizer.view!.superview!.convertPoint(recognizer.view!.center, toView: self.view)
let convertedFrame = self.view.convertRect(IV.frame,fromView: IV.superview)
if CGRectContainsPoint(convertedFrame, convertedCenter) {
//Replace placeholder image with dragged image.
}
Hopefully this will help someone else.

Resources