I am working on TextView as like Sticker View.I am successfully done but I can not wrap as I want. I want like this please check this clip and I am getting this output please check this output clip. From 1st clip I want like that and second video I am getting that.
In 1st video you can see when scaling the TextView It doesn't scale with center and when panning from right side of the TextView you can see it will decrease/increase width only.
In 2nd video(This is I am getting) you can see when scaling the TextView it will scale from the center that I don't want.
I want scale like 1st video. Hope you are understand.
I'm using this code
#objc func scaleGesture(_ recognizer: UIPanGestureRecognizer) {
touchLocation = recognizer.location(in: superview)
if recognizer.state == .began {
initialBounds = bounds
}
if recognizer.state == .changed {
let horizontalChange = recognizer.translation(in: self).x
let verticalChange = recognizer.translation(in: self).y
bounds = CGRect(x: initialBounds!.origin.x, y: initialBounds!.origin.y, width: initialBounds!.width + horizontalChange, height: initialBounds!.height + verticalChange)
}
}
Please help me. Thanks in advance.
Related
I want to restrict the area a UITextView can be moved to only the frame of another view. I tried to accomplish this with the following code, however, it will not properly restrict where the user can drag the UITextView:
#IBOutlet weak var canvas: UIView!
#IBAction func handleDragOfCaption(_ sender: UIPanGestureRecognizer) {
if sender.state == .began || sender.state == .changed {
guard let senderView = sender.view else {return}
let translation = sender.translation(in: sender.view)
let x = senderView.center.x + translation.x
let y = senderView.center.y + translation.y
let point = CGPoint(x: x, y: y)
if canvas.frame.contains(point) {
senderView.center = point
sender.setTranslation(CGPoint.zero, in: senderView)
}
}
}
See the following image for a visual representation of what I am trying to accomplish. I only want the user to be able to drag the UITextView within the boundaries of the UIView (which is the white square underneath the UITextView). Any help would be appreciated.
Here is another approach, using constraints...
Give your "draggable view" (your text view, in this case) a Width constraint and disable scrolling (that will allow it to auto-size its height).
Constrain the "draggable view" to Zero on all four sides - then edit those constraints to be >= 0. This will prevent the view from being dragged outside its parent view.
Also constrain the "draggable view" to Center Horizontally and Vertically - then edit those constraints to have Priority = 750. By setting the center constraints to a lower priority than the edge constraints, the centers will only take effect if the edges are fully inside the parent view.
Connect the center constraints to IBOutlets in your view controller, and add vars for "current" constant values for the center constraints.
Now add a Pan Gesture Recognizer to the "draggable view".
When you start the pan, save the center constraint constants to the "current" vars.
When you pan, get the translation X and Y (the distance moved from the start of the pan), and update the center constants based on the difference between the start and new values. This will move the "draggable view" to its new position.
When you finish the pan, update the center constants based on the final position of the "draggable view".
#IBAction func handleDragOfCaption(_ sender: UIPanGestureRecognizer) {
guard let senderView = sender.view else { return }
guard let parentView = senderView.superview else { return }
// get the current pan movement - relative to the view (the text view, in this case)
let translation = sender.translation(in: canvas)
switch sender.state {
case .began:
// save the current Center X and Y constants
currentCenterXConstant = textViewCenterXConstraint.constant
currentCenterYConstant = textViewCenterYConstraint.constant
break
case .changed:
// update the Center X and Y constants
textViewCenterXConstraint.constant = currentCenterXConstant + translation.x
textViewCenterYConstraint.constant = currentCenterYConstant + translation.y
break
case .ended, .cancelled:
// update the Center X and Y constants based on the final position of the dragged view
textViewCenterXConstraint.constant = senderView.center.x - parentView.frame.size.width / 2.0
textViewCenterYConstraint.constant = senderView.center.y - parentView.frame.size.height / 2.0
break
default:
break
}
}
To help with setting up the constraints correctly, I've posted an example project here: https://github.com/DonMag/LimitDrag
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.
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
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.
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.