Swift: UIPanGesture get location horizontally of view - ios

ja a gesture that is on the view that vertical black in the photo.
I want to know its location at the level of the screen, because I want to execute a function if for example I slide the view to the right at more than half of the center of the screen
code:
#objc func detectPan(recognizer: UIPanGestureRecognizer) {
switch recognizer.state {
case .began:
self.startingConstant = self.centerConstraint.constant
case .changed:
let translation = recognizer.translation(in: self.view)
self.centerConstraint.constant = self.startingConstant - translation.x
default:
break
}
}
Fist image
Second image

You can convert coordinates (points and frames) between views as long as they are on the same hierarchy. You have two methods convert(:to:) and convert(:from:).
In your case you seem to want to convert location in your view to screen which is your key window UIApplication.shared.keyWindow.
So in general the point on screen is let pointOnScreen = myView.convert(pointInMyView, to: UIApplication.shared.keyWindow).
So in your case:
let myView = self.view
let pointInMyView = recognizer.locationInView(myView)
let pointOnScreen = myView.convert(pointInMyView, to: UIApplication.shared.keyWindow)
let isViewGestureOnRightSideOfTheScreen = pointOnScreen.x > UIApplication.shared.keyWindow!.frame.midX

you can get point via
func transform(for translation : CGPoint) -> CGAffineTransform {
let moveBy = CGAffineTransform(translationX: translation.x, y: translation.y)
return moveBy
}
#IBAction func setPanGesture(_ sender: UIPanGestureRecognizer) {
switch sender.state {
case .changed:
let translation = sender.translation(in: view)
let translationView = transform(for: translation)
print(translationView)
case .ended:
let translation = sender.translation(in: view)
let translationView = transform(for: translation)
print(translationView)
default:
break
}
}

Related

Glitching When Swift UIView is Dragged

I have a view in an iOS project that is meant to be dragged within the main view. The view. I've attached a pan gesture to that view (paletteView), and moving it around works fine.
But, the "glitches" when moves begin. This doesn't happen on the first move, but on subsequent ones. I think what is happening is related to the constraints on the paletteView. It starts located at 100,100 in the main view, but of course whenever a drag ends, unless I change that, it will snap back to that location. So whenever the pan begins, I grab location of the palette, and update the constraints. This also works, but it seems to produce a flash when the next pangesture begins. Here's the main code:
#objc func palettePan(_ sender: UIPanGestureRecognizer) {
let translation = sender.translation(in: view)
switch sender.state {
case .began:
paletteLeading?.constant = paletteView!.frame.origin.x
paletteTop?.constant = paletteView!.frame.origin.y
paletteView?.setNeedsUpdateConstraints()
case .changed:
paletteView!.transform = CGAffineTransform(translationX: translation.x, y: translation.y)
case .ended:
print("done \(paletteView!.frame)")
default:
_ = true
}
}
Again, this works fine, but for the brief "flash" of the palette, out of position. I don't know where this would be coming from.
You need to reset the transform at the same time as you update the constraints, at the moment you are updating the constraints to to the new position, but the view is still transformed by the final translation of the previous pan until the first changed pan gesture event comes through.
So your palettePan function would become:
#objc func palettePan(_ sender: UIPanGestureRecognizer) {
let translation = sender.translation(in: view)
switch sender.state {
case .began:
paletteLeading?.constant = paletteView!.frame.origin.x
paletteTop?.constant = paletteView!.frame.origin.y
paletteView?.setNeedsUpdateConstraints()
paletteView?.transform = .identity // Reset the transform
case .changed:
paletteView!.transform = CGAffineTransform(translationX: translation.x, y: translation.y)
case .ended:
print("done \(paletteView!.frame)")
default:
_ = true
}
}
Though I would actually update the constraints and transform when the pan gesture ends, rather than when a new one begins:
#objc func palettePan(_ sender: UIPanGestureRecognizer) {
let translation = sender.translation(in: view)
switch sender.state {
case .changed:
paletteView?.transform = CGAffineTransform(translationX: translation.x, y: translation.y)
case .ended, .cancelled, .failed:
paletteLeading?.constant = paletteView!.frame.origin.x
paletteTop?.constant = paletteView!.frame.origin.y
paletteView?.setNeedsUpdateConstraints()
paletteView?.transform = .identity // Reset the transform
default:
_ = true
}
}

Hero animation start delayed

I am trying to implement a transition between two controllers with Hero inside a navigation controller.
I have this method to handle a pan gesture:
#objc func pannedCell(gesture: UIPanGestureRecognizer) {
guard let animatedView = gesture.view as? HotelListingCollectionViewCell else {
return
}
let translation = gesture.translation(in: nil)
let screenHeight = self.rootView.bounds.height
let progress = ((-1 * translation.y) / screenHeight) * 2
print(progress)
switch gesture.state {
case .began:
self.initialViewPostion = animatedView.center
Hero.shared.defaultAnimation = .fade
let vc = ListingSearchViewController()
vc.viewModel.hotels.value = self.viewModel.hotels.value
self.navigationController?.pushViewController(vc, animated: true)
case .changed:
Hero.shared.update(progress)
let pos = CGPoint(
x: animatedView.center.x + translation.x,
y: self.rootView.hotelResultsCollectionView.center.y + translation.y
)
Hero.shared.apply(modifiers: [
.position(pos),
.useGlobalCoordinateSpace,
.useOptimizedSnapshot
], to: animatedView)
default:
if progress > 0.8 {
Hero.shared.finish()
} else {
Hero.shared.cancel()
}
}
}
The problem here is the pushViewController method takes ~ 1 second to execute so when I start dragging my view, it moves under my finger approx. 1 second after I started moving the finger on the screen.
I am doing it wrong ?
Thanks
Ok, I think I found something !
The issue seems to be related to operation I did in .began case of the gesture handler.
I moved the instantiation of the new view controller out of this call back to avoid overloading the UI thread during the animation and everything seems to work as expected.

Bottom animation similar to Apple Maps application in iOS

Want to create an animation were user can slide the view from bottom similar to Apple Maps application in iOS. But having issues while handling gesture.
The code in my mainVC in which panVC is added as subview from bottom which works fine.(The panVC correctly comes at bottom)
func displayFromBottom {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
listVC = storyboard.instantiateViewController(withIdentifier: “PanViewContr") as! PanViewContr
var startingFrame = self.view.bounds;
startingFrame.origin.y = startingFrame.size.height; //Starts from the bottom of the parent.
startingFrame.size.height = 100; //Has a height of 100.
var finalFrame = self.view.bounds;
finalFrame.origin.y = finalFrame.size.height - 100; //100 from the bottom of the parent.
listVC.view.frame = startingFrame
listVC.willMove(toParentViewController: self)
self.addChildViewController(listVC)
self.view.addSubview(listVC.view)
listVC.didMove(toParentViewController: self)
UIView.animate(withDuration: 0.5, animations: {
self.listVC.view.frame = finalFrame
}) { complete in
print("done”)
}
}
The code for PanVC were pan gesture is handled.
func slideViewVerticallyTo(_ y: CGFloat) {
self.view.frame.origin = CGPoint(x: 0, y: y)
}
#IBAction func panGesture(_ panGesture: UIPanGestureRecognizer) {
switch panGesture.state {
case .began, .changed:
// If pan started or is ongoing then
// slide the view to follow the finger
let translation = panGesture.translation(in: view)
let y = max(0, translation.y) //what should be value of y to make it dragable smoothly
self.slideViewVerticallyTo(y)
break
case .ended:
break
}
Any hint in right direction is highly appreciated.
I had created a movable extension that adds the ability to move UIView's around a screen the code for which you can see here: https://github.com/szweier/SZUtilities/blob/master/SZUtilities/Classes/UIView/Movable/UIView%2BMovable.swift you may be able to see something in this code that you are missing in your own. I'm sorry my answer isn't more specific but hopefully it gets you moving towards the correct answer.

Set Bounds of GestureRecognizer

I am trying to set the limit the bounds of my image view, Or something to prevent the image/ view from zooming out or to the left as it looks pretty bad. Here is the problem Looks Good, Only should be able to zoom in... Zoomed out, Looks Bad
#IBAction func scaleView(_ sender: UIPinchGestureRecognizer) {
//print(self.my_new_fullimage)
self.view.transform = self.view.transform.scaledBy(x: sender.scale, y: sender.scale)
sender.scale = 1
}
#IBAction func panView(_ gestureRecognizer: UIPanGestureRecognizer) {
// Move the anchor point of the view's layer to the touch point
// so that moving the view becomes simpler.
let piece = gestureRecognizer.view
//self.adjustAnchorPoint(gestureRecognizer: gestureRecognizer)
if gestureRecognizer.state == .began || gestureRecognizer.state == .changed {
// Get the distance moved since the last call to this method.
let translation = gestureRecognizer.translation(in: piece?.superview)
// Set the translation point to zero so that the translation distance
// is only the change since the last call to this method.
piece?.center = CGPoint(x: ((piece?.center.x)! + translation.x),
y: ((piece?.center.y)! + translation.y))
gestureRecognizer.setTranslation(CGPoint.zero, in: piece?.superview)
}
}
Anything can help.
-Thanks!

Expand UIView with pan gesture in iOS Swift

I want to implement a view that stays at the bottom of the screen and can be expanded with a pan gesture just like in the Uber app for iOS?
Uber Home screen The view will be minimizable when dragged downwards
There are few third party libraries which makes your work easy. This is one of it. LNPopUpController.
Or else, if you want to customise the code and write: Take one view controller and add a UIView on top of it. Now add Pan Gesture to view like below.
func handlePan(pan : UIPanGestureRecognizer) {
let velocity = pan.velocityInView(self.superview).y //; print("Velocity : \(velocity)")
var location = pan.locationInView(self.superview!) //; print("Location : \(location)")
var movement = self.frame
movement.origin.x = 0
movement.origin.y = movement.origin.y + (velocity * 0.05) //; print("Frame.y : \(movement.origin.y)")
if pan.state == .Ended{
print("Gesture Ended")
panGestureEnded()
}else if pan.state == .Began { print("Gesture Began")
let center = self.center
offset.y = location.y - center.y //; print("Offset.y : \(offset.y)")
}else{ print("Gesture else")
animator.removeBehavior(snap)
// Apply the initial offset.
location.x -= offset.x
location.y -= offset.y
//print("location.y : \(location.y)")
// Bound the item position inside the reference view.
location.x = self.superview!.frame.width / 2
// Apply the resulting item center.
snap = UISnapBehavior(item: self, snapToPoint: location)
animator.addBehavior(snap)
}
}

Resources