UIPanGestureRecogniser not working properly - ios

I've got this function to handle the dragging of a view:+
func handlePan(recognizer:UIPanGestureRecognizer) {
let translation = recognizer.translationInView(self.view)
let newCenter = CGPointMake(self.view.bounds.size.width / 2,
recognizer.view!.center.y + translation.y)
print("the translation x:\(translation.x) & y:\(newCenter.y)")
if (newCenter.y >= 397 && newCenter.y <= 632) {
recognizer.view!.center = newCenter
recognizer.setTranslation(CGPointZero, inView: self.view)
}
}
The positions are right.
However, the view that I need to move often blocks while I'm dragging, so I need to stop and start dragging it again.
How can I solve this?
EDIT: My code looks like this now, but I still got the problem:
func handlePan(recognizer:UIPanGestureRecognizer) {
let translation = recognizer.translationInView(self.view)
switch recognizer.state {
case .Ended:fallthrough
case .Changed:
let newCenter = CGPointMake(self.view.bounds.size.width / 2,
recognizer.view!.center.y + translation.y)
print("the translation x:\(translation.x) & y:\(newCenter.y)")
if (newCenter.y >= 397 && newCenter.y <= 632) {
recognizer.view!.center = newCenter
recognizer.setTranslation(CGPointZero, inView: self.view)}
default : break }
}

Since I don't know the other parts of your code, this is a section assuming what yours might and should look like:
class ViewController: UIViewController {
// Gesture function
func panAround(recognizer: UIPanGestureRecognizer) {
switch recognizer.state {
...
case .Changed:
let translation = recognizer.translationInView(yourView)
let newCenter = CGPointMake(self.view.bounds.size.width / 2,
recognizer.view!.center.y + translation.y)
print("the translation x:\(translation.x) & y:\(newCenter.y)")
if (newCenter.y >= 397 && newCenter.y <= 632) {
recognizer.view!.center = newCenter
// Updating the UI whenever there is a "small change"
updateUI()
recognizer.setTranslation(CGPointZero, inView: self.view)
}
...
}
}
private func updateUI() {
yourView.setNeedsDisplay()
}
NOTE: The code above WILL NOT work directly by copy and pasting, I am merely pointing out where you can make adjustments.

Related

UIPanGestureRecognizer to rotate mapView

I have a method for panning the map using a UIPanGestureRecognizer however it doesn't rotate correctly (I want to be able to make circle motions that cause the camera to move in a circle), is there a better approach here?
#objc func panMap (sender: UIPanGestureRecognizer) {
if sender.state == .began {
print("began")
} else if sender.state == .changed {
// rotating map camera
let position = sender.translation(in: mapView)
let newDirection = mapView.camera.heading.advanced(by: (Double(position.x) + Double(position.y)))
let newCamera: MKMapCamera = MKMapCamera(lookingAtCenter: mapView.camera.centerCoordinate, fromDistance: mapView.camera.altitude, pitch: mapView.camera.pitch, heading: newDirection)
mapView.setCamera(newCamera, animated: false)
// Eugene Dudnyk's solution to help fix logic error when appending distance
sender.setTranslation(.zero, in: mapView)
} else if sender.state == .ended {
print("end")
}
}
The issue is that translation of pan gesture recognizer accumulates relative to the position at the start of panning, but you interpret it as accumulation since the last state change, and always add to the camera angle.
In case if user moved finger to afa.x = 160, and then moves finger back, afa.x changes let's say from 160 to 150, but you will append to the angle 150 / 30 = 5 degrees where angle has to be negative, - 10 / 30 degrees instead.
Try this, maybe it will make things better:
#objc func panMap (sender: UIPanGestureRecognizer) {
if sender.state == .began {
print("began")
} else if sender.state == .changed {
// rotating map camera
let translation = sender.translation(in: mapView)
let location = sender.location(in: mapView)
let bounds = mapView.bounds
let vector1 = CGVector(dx: location.x - bounds.midX, dy: location.y - bounds.midY)
let vector2 = CGVector(dx: vector1.dx + translation.x, dy: vector1.dy + translation.y)
let angle1 = atan2(vector1.dx, vector1.dy)
let angle2 = atan2(vector2.dx, vector2.dy)
mapView.camera.heading += (angle2 - angle1) * 180.0 / Double.pi
} else if sender.state == .ended {
print("end")
}
sender.setTranslation(.zero, in: mapView)
}

Swift Variables in Switch or If statement

I'm trying to learn how to use Swift and I'm just at the beginning. So this code is probably very bad. I can't find some Information about var handling in switch statements.
In the iOS app you can drag a black view (imgView) along the screen. Now if the touch ends, the imgView should animate to a CGPoint which is calculated from the beginning point and end point of the pan gesture.
#objc func handlePan(recognizer: UIPanGestureRecognizer) {
var locationOfBeganTap = CGPoint()
var locationOfEndTap = CGPoint()
let finalDestination = CGPoint(x: (locationOfBeganTap.x + locationOfEndTap.x), y: locationOfBeganTap.y + locationOfEndTap.y)
switch recognizer.state {
case .changed, .began:
locationOfBeganTap = recognizer.location(in: screenView)
print(locationOfBeganTap)
let translation = recognizer.translation(in: self.view)
if let view = recognizer.view {
view.center = CGPoint(x: view.center.x + translation.x, y: view.center.y + translation.y)
}
recognizer.setTranslation(CGPoint.zero, in: self.view)
case .ended:
locationOfEndTap = recognizer.location(in: screenView)
UIView.animate(withDuration: 2.0, animations: {
print(locationOfBeganTap)
self.imgView.center = finalDestination
})
default:
break
}
I want to know why the Variable "locationOfBeginTap" is set correctly on .began but further in the Switch statement, the Variable is set to 0 again. When and why did it change? And How could I avoid that?
It's all about variable scope. This has nothing do with switch or if. Each time handlePan is called, you create new local variables with their initial values.
Move the declaration of those variables to be outside the function so they are actually properties of your class. Then the values will remain between calls to handlePan.
Move you variables outside the method.
var locationOfBeganTap = CGPoint()
var locationOfEndTap = CGPoint()
let finalDestination = CGPoint(x: (locationOfBeganTap.x + locationOfEndTap.x), y: locationOfBeganTap.y + locationOfEndTap.y)
#objc func handlePan(recognizer: UIPanGestureRecognizer) {
switch recognizer.state {
case .changed, .began:
locationOfBeganTap = recognizer.location(in: screenView)
print(locationOfBeganTap)
let translation = recognizer.translation(in: self.view)
if let view = recognizer.view {
view.center = CGPoint(x: view.center.x + translation.x, y: view.center.y + translation.y)
}
recognizer.setTranslation(CGPoint.zero, in: self.view)
case .ended:
locationOfEndTap = recognizer.location(in: screenView)
UIView.animate(withDuration: 2.0, animations: {
print(locationOfBeganTap)
self.imgView.center = finalDestination
})
default:
break
}
}

How do I set up an UIPanGestureRecognizer using xib file

func handlePanGesture (panGesture: UIPanGestureRecognizer) {
let translation = panGesture.translation(in: panGesture.view?.superview)
if panGesture.state == .began || panGesture.state == .changed {
panGesture.view?.center = CGPoint(x: (panGesture.view?.center.x)! + translation.x, y: (panGesture.view?.center.y)! + translation.y)
let distance: Double = sqrt(pow((Double(panGesture.view!.center.x) - Double(poin1.x)),2) + pow((Double(panGesture.view!.center.y) - Double(poin1.y)),2))
if distance <= 50{
panGesture.view!.center.x = CGFloat(poin1.x)
panGesture.view!.center.y = CGFloat(poin1.y)
}
panGesture.setTranslation(CGPoint.zero, in: self.view)
}
}
I want custom func handlePanGesture using many points to move in the gesture.

Define actions for subsections of view

I have a view with a circle inside of it controlled by a PanGesture. It is the intention that when a user drags this circle around in the view that some parameters change accordingly.
Here is my viewcontroller:
import UIKit
class ViewController: UIViewController {
var circleCenter: CGPoint!
#IBOutlet weak var soundSpace: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// Add a draggable view
let circle = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 50.0, height: 50.0))
circle.center = CGPoint(x: soundSpace.bounds.width/2, y: soundSpace.bounds.height/2) //self.view.center
circle.layer.cornerRadius = 25.0
circle.backgroundColor = UIColor.black
// add pan gesture recognizer to
circle.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.dragCircle)))
self.soundSpace.addSubview(circle)
}
func dragCircle(gesture: UIPanGestureRecognizer) {
let target = gesture.view!
switch gesture.state {
case .began, .ended:
circleCenter = target.center
case .changed:
let translation = gesture.translation(in: self.soundSpace)
// Ensure circle stays within subview
if (circleCenter.x + translation.x) > self.soundSpace.bounds.minX+25 &&
(circleCenter.x + translation.x) < self.soundSpace.bounds.maxX-25 &&
(circleCenter.y + translation.y) > self.soundSpace.bounds.minY+25 &&
(circleCenter.y + translation.y) < self.soundSpace.bounds.maxY-25{
target.center = CGPoint(x: circleCenter!.x + translation.x, y: circleCenter!.y + translation.y)
// Check whether circle is in rhs or lhs
if target.center.x < (self.soundSpace.bounds.width / 2){
print("Option 1")
} else {
print("Option 2")
}
}
default: break
}
}
}
I would like to divide my view into subsections. On my x-axis I would like 2 subsections i.e. some parameter change when circle is on the lhs. of the view and same parameters change when circle is on the right hand side of the view. This has already been done (in the only manner I could think of on the top of my head).
Now I would like to divide my y-axis into 9 subsections. I could use an if-statement, but that seems a bit unclean to me. Is there any effective and more elegant way of doing that?
Looking forward to any suggestions, thanks!
To check the correct section of y
func dragCircle(gesture: UIPanGestureRecognizer) {
let target = gesture.view!
switch gesture.state {
case .began, .ended:
circleCenter = target.center
case .changed:
let translation = gesture.translation(in: self.soundSpace)
// Ensure circle stays within subview
if (circleCenter.x + translation.x) > self.soundSpace.bounds.minX+25 &&
(circleCenter.x + translation.x) < self.soundSpace.bounds.maxX-25 &&
(circleCenter.y + translation.y) > self.soundSpace.bounds.minY+25 &&
(circleCenter.y + translation.y) < self.soundSpace.bounds.maxY-25{
target.center = CGPoint(x: circleCenter!.x + translation.x, y: circleCenter!.y + translation.y)
//------ y calculation ------
let yPosition = target.center.y
let totalHeight = self.soundSpace.bounds.height
let totalYSections = 9
let ySectionHeight = totalHeight/totalYSections
let reminder = yPosition % ySectionHeight
let correction = 0
if (reminder > 0) {
correction = 1
}
let currentYSection = (yPosition / ySectionHeight) + correction
print(currentYSection)
//------ y calculation ------
// Check whether circle is in rhs or lhs
if target.center.x < (self.soundSpace.bounds.width / 2){
print("Option 1")
} else {
print("Option 2")
}
}
default: break
}
}
}

Limit view movement with UIPanGestureRecogniser

I've got this function that handles the pan gesture on one of my views.
#IBAction func handlePan(recognizer:UIPanGestureRecognizer) {
let translation = recognizer.translationInView(self.view)
if let view = recognizer.view {
view.center = CGPoint(x:view.center.x,
y:view.center.y + translation.y)
}
recognizer.setTranslation(CGPointZero, inView: self.view)
}
How can I make the view stop after it has been moved a specified distance upward(or downward)?
Same problem that I faced in my project,
Try this,
#IBAction func handlePan(recognizer:UIPanGestureRecognizer) {
let translation = recognizer.translationInView(self.view)
//set limitation for x and y origin
if(translation.x <= 100 && translation.y <= 50 )
{
if let view = recognizer.view {
view.center = CGPoint(x:view.center.x,
y:view.center.y + translation.y)
}
recognizer.setTranslation(CGPointZero, inView: self.view)
}
}

Resources