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.
Related
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)
}
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
}
}
I am trying to move a UIView on slide up gesture from its initial position to a fixed final position. The image should move with the hand gesture, and not animate independently.
I haven't tried anything as I have no clue where to start, which gesture class to use.
Finally did it like below.
let gesture = UIPanGestureRecognizer(target: self, action: Selector("wasDragged:"))
slideUpView.addGestureRecognizer(gesture)
slideUpView.userInteractionEnabled = true
gesture.delegate = self
The following function is called when the gesture is detected, (here I am restricting the view to have a maximum centre.y of 555, & I'm resetting back to 554 when the view moves past this point)
func wasDragged(gestureRecognizer: UIPanGestureRecognizer) {
if gestureRecognizer.state == UIGestureRecognizerState.Began || gestureRecognizer.state == UIGestureRecognizerState.Changed {
let translation = gestureRecognizer.translationInView(self.view)
print(gestureRecognizer.view!.center.y)
if(gestureRecognizer.view!.center.y < 555) {
gestureRecognizer.view!.center = CGPointMake(gestureRecognizer.view!.center.x, gestureRecognizer.view!.center.y + translation.y)
}else {
gestureRecognizer.view!.center = CGPointMake(gestureRecognizer.view!.center.x, 554)
}
gestureRecognizer.setTranslation(CGPointMake(0,0), inView: self.view)
}
}
You probably want to use a UIPanGestureRecognizer.
let gesture = UIPanGestureRecognizer(target: self, action: Selector("wasDragged:"))
customView.addGestureRecognizer(gesture)
gesture.delegate = self
And to drag the object only along the y-axis:
func wasDragged(gesture: UIPanGestureRecognizer) {
let translation = gesture.translationInView(self.view)
// Use translation.y to change the position of your customView, e.g.
customView.center.y = translation.y // Customize this.
}
Swift 4:
#objc func wasDragged(_ gestureRecognizer: UIPanGestureRecognizer) {
if gestureRecognizer.state == UIGestureRecognizer.State.began || gestureRecognizer.state == UIGestureRecognizer.State.changed {
let translation = gestureRecognizer.translation(in: self.view)
print(gestureRecognizer.view!.center.y)
if(gestureRecognizer.view!.center.y < 555) {
gestureRecognizer.view!.center = CGPoint(x: gestureRecognizer.view!.center.x, y: gestureRecognizer.view!.center.y + translation.y)
}else {
gestureRecognizer.view!.center = CGPoint(x:gestureRecognizer.view!.center.x, y:554)
}
gestureRecognizer.setTranslation(CGPoint(x: 0, y: 0), in: self.view)
}
}
Call
let gesture = UIPanGestureRecognizer(target: self, action: self.wasDragged(gestureRecognizer:))
customView.addGestureRecognizer(gesture)
gesture.delegate = self
Update for Swift 3.x
When assigning the selector, the syntax has changed and now requires #selector
let gesture = UIPanGestureRecognizer(target: self, action: #selector(navViewDragged(gesture:)))
self.navLayoutView.addGestureRecognizer(gesture)
self.navLayoutView.isUserInteractionEnabled = true
gesture.delegate = self
Function implementation:
func navViewDragged(gesture: UIPanGestureRecognizer){
//Code here
}
Move view anywhere in Swift 3
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(dragged(gestureRecognizer:)))
demoView.isUserInteractionEnabled = true
demoView.addGestureRecognizer(panGesture)
Function
#objc func dragged(gestureRecognizer: UIPanGestureRecognizer) {
if gestureRecognizer.state == UIGestureRecognizerState.began || gestureRecognizer.state == UIGestureRecognizerState.changed {
let translation = gestureRecognizer.translation(in: self.view)
gestureRecognizer.view!.center = CGPoint(x: gestureRecognizer.view!.center.x + translation.x, y: gestureRecognizer.view!.center.y + translation.y)
gestureRecognizer.setTranslation(CGPoint(x: 0, y: 0), in: self.view)
}
}
This is how you really do it like the news view in the Stocks app
First add 2 constraints in Storyboard to the sliding view, one for it's state when it's fully opened and one for when it's closed. Don't forget to leave one of the constraints disabled / not installed so that your view will look opened or closed when the scene is reached.
Reference them in your code
#IBOutlet weak var optionsOpenedConstraint: NSLayoutConstraint!
#IBOutlet weak var optionsVisiableConstraint: NSLayoutConstraint!
now add the UIPanGestureRecognizer to your view in the viewDidLoad function.
let gesture = UIPanGestureRecognizer(target: self, action: #selector(type(of: self).wasDragged(gestureRecognizer:)))
optionsView.addGestureRecognizer(gesture)
finally add this callback and 2 functions:
#objc func wasDragged(gestureRecognizer: UIPanGestureRecognizer) {
let distanceFromBottom = screenHeight - gestureRecognizer.view!.center.y
if gestureRecognizer.state == UIGestureRecognizer.State.began || gestureRecognizer.state == UIGestureRecognizer.State.changed {
optionsOpenedConstraint.isActive = false
optionsVisiableConstraint.isActive = false
let translation = gestureRecognizer.translation(in: self.view)
if((distanceFromBottom - translation.y) < 100) {
gestureRecognizer.view!.center = CGPoint(x: gestureRecognizer.view!.center.x, y: gestureRecognizer.view!.center.y + translation.y)
gestureRecognizer.setTranslation(CGPoint(x: 0, y: 0), in: self.view)
}
}
if gestureRecognizer.state == UIGestureRecognizer.State.ended{
if distanceFromBottom > 6{
openOptionsPanel()
}else{
closeOptionsPanel()
}
}
}
func openOptionsPanel(){
optionsOpenedConstraint.isActive = true
optionsVisiableConstraint.isActive = false
UIView.animate(withDuration: 0.5) {
self.view.layoutIfNeeded()
}
}
func closeOptionsPanel(){
optionsOpenedConstraint.isActive = false
optionsVisiableConstraint.isActive = true
UIView.animate(withDuration: 0.5) {
self.view.layoutIfNeeded()
}
}
and voalá
#IBAction func handlePanGesture(_ recognizer: UIPanGestureRecognizer) {
if MainView.bounds.contains(mainImage.frame) {
let recognizerCenter = recognizer.location(in: MainView)
mainImage.center = recognizerCenter
}
if MainView.bounds.intersection(mainImage.frame).width > 50 && MainView.bounds.intersection(mainImage.frame).height > 0 {
let recognizerCenter = recognizer.location(in: MainView)
print(recognizerCenter)
mainImage.center = recognizerCenter
}
}
#IBAction func handlePinchGesture(_ recognizer: UIPinchGestureRecognizer) {
mainImage.transform = mainImage.transform.scaledBy(x: recognizer.scale, y: recognizer.scale)
recognizer.scale = 1.0
}
#IBAction func handleRotateGesture(_ recognizer: UIRotationGestureRecognizer) {
mainImage.transform = mainImage.transform.rotated(by: recognizer.rotation)
recognizer.rotation = 0.0
}
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.
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)
}
}