Zoom ImageView in UICollectionView inconsistent - ios

I have a UICollectionView with cells that are the size of the CollectionView itself, so paging is enabled and you swipe through similar to the basic Gallery app.
I want the images to be zoomable like on Instagram (snaps back on gesture end). I Implemented the Pinch and Pan gestures with the help of an article, and it does work.
But I am having a strange issue. The speed at which it zooms is very inconsistent, and it seems to get faster with every page I flip. So the first Page it zooms and pans pretty normally, the second it is much faster, and by the third it is ridiculous.
I'm very confused, since I had this exact code running on a paging ScrollView, and it worked flawlessly.
Here is the relevant code in the UICollectionViewCell.
func setup(){
imageView.isUserInteractionEnabled = true
let pinch = UIPinchGestureRecognizer(target: self, action:
#selector(pinch(sender:)))
pinch.delegate = self
imageView.addGestureRecognizer(pinch)
let pan = UIPanGestureRecognizer(target: self, action:
#selector(pan(sender:)))
pan.delegate = self
imageView.addGestureRecognizer(pan)
let tap = UITapGestureRecognizer(target: self, action:
#selector(handleTap))
imageView.addGestureRecognizer(tap)
imageView.clipsToBounds = false
}
#objc func pan(sender: UIPanGestureRecognizer) {
if(isZooming){
if self.isZooming && sender.state == .began {
self.originalImageCenter = sender.view?.center
} else if self.isZooming && sender.state == .changed {
let translation = sender.translation(in: self)
if let view = sender.view {
view.center = CGPoint(x:view.center.x +
translation.x,
y:view.center.y +
translation.y)
}
sender.setTranslation(CGPoint.zero, in:
self.imageView.superview)
}
}else{
print("blocked pan")
}
}
#objc func pinch(sender:UIPinchGestureRecognizer) {
NotificationCenter.default.post(name:
Notification.Name(rawValue: "disablePage"), object: nil)
allowedToTap = false
if sender.state == .began {
let currentScale = self.imageView.frame.size.width /
self.imageView.bounds.size.width
let newScale = currentScale*sender.scale
if newScale > 1 {
self.isZooming = true
}
} else if sender.state == .changed {
guard let view = sender.view else {return}
let pinchCenter = CGPoint(x: sender.location(in: view).x -
view.bounds.midX,
y: sender.location(in: view).y -
view.bounds.midY)
let transform = view.transform.translatedBy(x:
pinchCenter.x, y: pinchCenter.y)
.scaledBy(x: sender.scale, y: sender.scale)
.translatedBy(x: -pinchCenter.x, y: -pinchCenter.y)
let currentScale = self.imageView.frame.size.width /
self.imageView.bounds.size.width
var newScale = currentScale*sender.scale
if newScale < 1 {
newScale = 1
let transform = CGAffineTransform(scaleX: newScale, y:
newScale)
self.imageView.transform = transform
sender.scale = 1
}else {
view.transform = transform
sender.scale = 1
}
} else if sender.state == .ended || sender.state == .failed ||
sender.state == .cancelled {
guard let center = self.originalImageCenter else {return}
UIView.animate(withDuration: 0.3, animations: {
self.imageView.transform = CGAffineTransform.identity
self.imageView.center = center
}, completion: { _ in
self.isZooming = false
NotificationCenter.default.post(name:
Notification.Name(rawValue: "enablePage"), object: nil)
self.allowedToTap = true
})
}
}
I hope someone with more of an idea of how this works can help me.

I found a solution, I ll leave it here in case someone has the same issue.
The problem was that the setup function was being called multiple times, therefore multiple identical GestureRecognizers were being added.
So I just added a quick check.
func setup(){
imageView.isUserInteractionEnabled = true
playPauseImageView.alpha = 0
if(!gesturesAreAttached){
gesturesAreAttached = true
addZoom()
}
imageView.clipsToBounds = false
}
func addZoom(){
print("adding Zoom")
let pinch = UIPinchGestureRecognizer(target: self, action: #selector(pinch(sender:)))
pinch.delegate = self
imageView.addGestureRecognizer(pinch)
let pan = UIPanGestureRecognizer(target: self, action: #selector(pan(sender:)))
pan.delegate = self
imageView.addGestureRecognizer(pan)
let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap))
imageView.addGestureRecognizer(tap)
}

Related

iOS Swift: Rotate and Scale UIView without resizing

I have a UIView which i want to scale and rotate via pan and pinch gesture. But issue is when i scale view and after then when i rotate it's resizing back to initial value before scaling.
extension UIView {
func addPinchGesture() {
var pinchGesture = UIPinchGestureRecognizer()
pinchGesture = UIPinchGestureRecognizer(target: self,
action: #selector(handlePinchGesture(_:)))
self.addGestureRecognizer(pinchGesture)
}
#objc func handlePinchGesture(_ sender: UIPinchGestureRecognizer) {
self.transform = self.transform.scaledBy(x: sender.scale, y: sender.scale)
sender.scale = 1
}
}
// ROTATION
extension UIView {
func addRotationGesture() {
var rotationGesture = RotationGestureRecognizer()
rotationGesture = RotationGestureRecognizer(target: self,
action: #selector(handleRotationGesture(_:)))
self.addGestureRecognizer(rotationGesture)
}
#objc func handleRotationGesture(_ sender: RotationGestureRecognizer) {
var originalRotation = CGFloat()
switch sender.state {
case .began:
sender.rotation = sender.lastRotation
originalRotation = sender.rotation
case .changed:
let newRotation = sender.rotation + originalRotation
self.transform = CGAffineTransform(rotationAngle: newRotation) // Rotation is fine but it is resizing view
// self.transform = self.transform.rotated(by: newRotation / CGFloat(180 * Double.pi)) // NOT WORKING i.e. irregular rotation
case .ended:
sender.lastRotation = sender.rotation
default:
break
}
}
}
Before Scaling
After Scaling
After Rotation
I want it to be rotate without affecting view size. How can i achieve that?
You are resetting the scale transform of view when applying rotation transform. Create a property to hold original scale of the view.
var currentScale: CGFloat = 0
And when pinch is done, store the currentScale value to current scale. Then when rotating also use this scale, before applying the rotation.
let scaleTransform = CGAffineTransform(scaleX: currentScale, y: currentScale)
let concatenatedTransform = scaleTransform.rotated(by: newRotation)
self.transform = concatenatedTransform
You are using extension to add gesture recognizers, for that reason you cannot store currentScale. You can also get the scale values of view from current transform values. Here is how your code would look like,
extension UIView {
var currentScale: CGPoint {
let a = transform.a
let b = transform.b
let c = transform.c
let d = transform.d
let sx = sqrt(a * a + b * b)
let sy = sqrt(c * c + d * d)
return CGPoint(x: sx, y: sy)
}
func addPinchGesture() {
var pinchGesture = UIPinchGestureRecognizer()
pinchGesture = UIPinchGestureRecognizer(target: self,
action: #selector(handlePinchGesture(_:)))
self.addGestureRecognizer(pinchGesture)
}
#objc func handlePinchGesture(_ sender: UIPinchGestureRecognizer) {
self.transform = self.transform.scaledBy(x: sender.scale, y: sender.scale)
sender.scale = 1
}
}
// ROTATION
extension UIView {
func addRotationGesture() {
var rotationGesture = RotationGestureRecognizer()
rotationGesture = RotationGestureRecognizer(target: self,
action: #selector(handleRotationGesture(_:)))
self.addGestureRecognizer(rotationGesture)
}
#objc func handleRotationGesture(_ sender: RotationGestureRecognizer) {
var originalRotation = CGFloat()
switch sender.state {
case .began:
sender.rotation = sender.lastRotation
originalRotation = sender.rotation
case .changed:
let scale = CGAffineTransform(scaleX: currentScale.x, y: currentScale.y)
let newRotation = sender.rotation + originalRotation
self.transform = scale.rotated(by: newRotation)
case .ended:
sender.lastRotation = sender.rotation
default:
break
}
}
}
I used this answer as a reference for extracting the scale value.
I was facing the same issue Once I pinch from a finger, then after I rotate from a button it automatically scales down from the current but I set logic below.
#objc func rotateViewPanGesture(_ recognizer: UIPanGestureRecognizer) {
touchLocation = recognizer.location(in: superview)
let center = CGRectGetCenter(frame)
switch recognizer.state {
case .began:
deltaAngle = atan2(touchLocation!.y - center.y, touchLocation!.x - center.x) - CGAffineTrasformGetAngle(transform)
initialBounds = bounds
initialDistance = CGpointGetDistance(center, point2: touchLocation!)
case .changed:
let ang = atan2(touchLocation!.y - center.y, touchLocation!.x - center.x)
let angleDiff = deltaAngle! - ang
let a = transform.a
let b = transform.b
let c = transform.c
let d = transform.d
let sx = sqrt(a * a + b * b)
let sy = sqrt(c * c + d * d)
let currentScale = CGPoint(x: sx, y: sy)
let scale = CGAffineTransform(scaleX: currentScale.x, y: currentScale.y)
self.transform = scale.rotated(by: -angleDiff)
layoutIfNeeded()
case .ended:
print("end gesture status")
default:break
}
}

How to disable a gesture recognizer while editing a text field?

I have a UITextView that has a pan gesture recognizer so it is draggable around the screen. I am trying to disable this gesture recognizer while the text field is being edited so the text stays in the middle of the screen, and the user can't drag it below the keyboard.
I have tried answers like this and this but can't seem to get them to work. I've also tried removing the gesture recognizer using textField.removeGestureRecognizer(textField.panGestureRecognizer)
but couldn't get that to work either. Does anyone know what is going wrong here?
Here is my code;
import UIKit
class ViewController: UIViewController, UIGestureRecognizerDelegate {
let textField = UITextView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 100))
//the current center of the text being edited
var currentCenter = CGPoint()
override func viewDidLoad() {
super.viewDidLoad()
// tap gesture recognizer for keyboard hide
let HideKeyboardTap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
view.addGestureRecognizer(HideKeyboardTap)
textField.text = "hello"
textField.font = UIFont.systemFont(ofSize: 80)
textField.textContainerInset = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
textField.sizeToFit()
self.view.addSubview(textField)
//Enable multiple touch and user interaction for textfield
textField.isUserInteractionEnabled = true
textField.isMultipleTouchEnabled = true
//add pan gesture
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
panGestureRecognizer.delegate = self
textField.addGestureRecognizer(panGestureRecognizer)
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardShowNotification), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardHideNotification), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
#objc func handleKeyboardShowNotification(notification: NSNotification) {
if let keyboardFrame: NSValue = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue {
let keyboardRectangle = keyboardFrame.cgRectValue
let keyboardHeight = keyboardRectangle.height
currentCenter = textField.center
textField.center = CGPoint(x: UIScreen.main.bounds.midX , y: UIScreen.main.bounds.height - (keyboardHeight + textField.bounds.height + 10))
textField.panGestureRecognizer.isEnabled = false
}
}
#objc func handleKeyboardHideNotification() {
textField.panGestureRecognizer.isEnabled = true
textField.center = currentCenter
}
#objc func handlePan(_ gestureRecognizer: UIPanGestureRecognizer) {
if gestureRecognizer.state == .began || gestureRecognizer.state == .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.zero, in: self.view)
}
}
}
Thanks for any help!
Instead of trying to disable the gesture, how about do nothing in the gesture action while editing?
#objc func handlePan(_ gestureRecognizer: UIPanGestureRecognizer) {
if(textField.isEditing){return;}
if gestureRecognizer.state == .began || gestureRecognizer.state == .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.zero, in: self.view)
}
}
private func getPanGesture() -> [UIPanGestureRecognizer]? {
let panGesture = textField.gestureRecognizers?.filter({$0 is UIPanGestureRecognizer})
return panGesture as? [UIPanGestureRecognizer]
}
private func disablePanGesture() {
let enabledGestures = getPanGesture()?.filter({$0.isEnabled})
enabledGestures?.forEach({ (gesture) in
gesture.isEnabled = false
})
}
private func enablePanGesture() {
let disabledGestures = getPanGesture()?.filter({!$0.isEnabled})
disabledGestures?.forEach({ (gesture) in
gesture.isEnabled = true
})
}
#objc func handleKeyboardShowNotification(notification: NSNotification) {
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
..
disablePanGesture()
}
}
#objc func handleKeyboardHideNotification() {
enablePanGesture()
..
}

Disable zoom out on UIPinchGestureRecognizer

I want to disable zooming out, I have tried doing it through storyboard on UIScrollView by setting the min value but it made no difference.
let tap = UIPinchGestureRecognizer(target: self, action: #selector(self.pinchHandler(gesture:)))
tap.delegate = self as? UIGestureRecognizerDelegate
self.scrollView.addGestureRecognizer(tap)
#objc private func pinchHandler(gesture: UIPinchGestureRecognizer) {
if let view = gesture.view {
switch gesture.state {
case .changed:
self.navigationController?.isNavigationBarHidden = true
let pinchCenter = CGPoint(x: gesture.location(in: view).x - view.bounds.midX,
y: gesture.location(in: view).y - view.bounds.midY)
let transform = view.transform.translatedBy(x: pinchCenter.x, y: pinchCenter.y)
.scaledBy(x: gesture.scale, y: gesture.scale)
.translatedBy(x: -pinchCenter.x, y: -pinchCenter.y)
view.transform = transform
gesture.scale = 1
case .ended:
UIView.animate(withDuration: 0.2, animations: {
view.transform = CGAffineTransform.identity
self.navigationController?.isNavigationBarHidden = false
})
default:
return
}
}
}
So to just reiterate what Quoc mentioned in the comment, your code should be somewhat like this -
#objc private func pinchHandler(gesture: UIPinchGestureRecognizer) {
if let view = gesture.view {
switch gesture.state {
case .changed:
if(gesture.scale < 1.0) { return }
//your code
case .ended:
//your code
default:
return
}
}
}

Can't remove Gesture

I get two problem by using gestures. First I want remove a gesture called "panRecognizer" on double tap, but it doesn't work. Second Problem I use the panRecognizer to move the view but I can't move to the outer edges it stops 200 or 150 px before I reached the edge from the view.
Here is my code:
self.view.gestureRecognizers = self.pageViewController?.gestureRecognizers
let tap = UITapGestureRecognizer(target: self, action: #selector(miningCatalouge.doubleTapped(_:)))
tap.numberOfTapsRequired = 2
self.view.addGestureRecognizer(tap)
}
func doubleTapped(tap: UITapGestureRecognizer) {
let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(miningCatalouge.handlePan(_:)))
let curScale = self.view!.layer.valueForKeyPath("transform.scale.x")!.floatValue
if (curScale == 1) {
pageViewController!.view.userInteractionEnabled = false
self.view.transform = CGAffineTransformMakeScale(3, 3)
panRecognizer.minimumNumberOfTouches = 1
panRecognizer.maximumNumberOfTouches = 1
self.view.addGestureRecognizer(panRecognizer)
} else {
self.view.transform = CGAffineTransformMakeScale(1, 1)
self.view.center = CGPointMake(512, 391)
pageViewController!.view.userInteractionEnabled = true
//Don´t removed the gesture "panRecognizer"
self.view.removeGestureRecognizer(panRecognizer)
}
print("doubleTapped")
}
func handlePan(recognizer:UIPanGestureRecognizer) {
let translation = recognizer.translationInView(self.view)
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)
if recognizer.state == UIGestureRecognizerState.Ended {
// 1
let velocity = recognizer.velocityInView(self.view)
let magnitude = sqrt((velocity.x * velocity.x) + (velocity.y * velocity.y))
let slideMultiplier = magnitude / 200
// print("magnitude: \(magnitude), slideMultiplier: \(slideMultiplier)")
// 2
let slideFactor = 0.1 * slideMultiplier //Increase for more of a slide
// 3
var finalPoint = CGPoint(x:recognizer.view!.center.x + (velocity.x * slideFactor),
y:recognizer.view!.center.y + (velocity.y * slideFactor))
// 4
finalPoint.x = min(max(finalPoint.x, 0), self.view.bounds.size.width)
finalPoint.y = min(max(finalPoint.y, 0), self.view.bounds.size.height)
// 5
UIView.animateWithDuration(Double(slideFactor),
delay: 0,
// 6
options: UIViewAnimationOptions.CurveEaseOut,
animations: {recognizer.view!.center = finalPoint },
completion: nil)
}
}
for gesture in view.gestureRecognizers!
{
if let recognizer = gesture as? UITapGestureRecognizer {
view.removeGestureRecognizer(recognizer)
}
}
Hope this code could help
Here is the solution from J.Wang:
class miningCatalouge: UIViewController, UIPageViewControllerDelegate, UIGestureRecognizerDelegate, UIScrollViewDelegate {
// I created a new Variable
var panRecognizer: UIPanGestureRecognizer?
override func viewDidLoad() {
super.viewDidLoad()
self.view.gestureRecognizers = self.pageViewController?.gestureRecognizers
let tap = UITapGestureRecognizer(target: self, action: #selector(miningCatalouge.doubleTapped(_:)))
tap.numberOfTapsRequired = 2
self.view.addGestureRecognizer(tap)
//initialized action to panRecognizer
panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(miningCatalouge.handlePan(_:)))
}
func doubleTapped(tap: UITapGestureRecognizer) {
let curScale = self.view!.layer.valueForKeyPath("transform.scale.x")!.floatValue
if (curScale == 1) {
pageViewController!.view.userInteractionEnabled = false
self.view.transform = CGAffineTransformMakeScale(3, 3)
panRecognizer!.minimumNumberOfTouches = 1
panRecognizer!.maximumNumberOfTouches = 1
self.view.addGestureRecognizer(panRecognizer!)
} else {
self.view.transform = CGAffineTransformMakeScale(1, 1)
self.view.center = CGPointMake(512, 391)
pageViewController!.view.userInteractionEnabled = true
self.view.removeGestureRecognizer(panRecognizer!)
}
print("doubleTapped")
}
func handlePan(recognizer:UIPanGestureRecognizer) {
let translation = recognizer.translationInView(self.view)
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)
if recognizer.state == UIGestureRecognizerState.Ended {
let velocity = recognizer.velocityInView(self.view)
let magnitude = sqrt((velocity.x * velocity.x) + (velocity.y * velocity.y))
let slideMultiplier = magnitude / 200
// print("magnitude: \(magnitude), slideMultiplier: \(slideMultiplier)")
let slideFactor = 0.15 * slideMultiplier //Increase for more of a slide
let finalPoint = CGPoint(x:recognizer.view!.center.x + (velocity.x * slideFactor),
y:recognizer.view!.center.y + (velocity.y * slideFactor))
UIView.animateWithDuration(Double(slideFactor),
delay: 0,
// 6
options: UIViewAnimationOptions.CurveEaseOut,
animations: {recognizer.view!.center = finalPoint },
completion: nil)
}
}

Swift: move UIView on slide gesture

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
}

Resources