Search code examples
iosobjective-cuiviewuipangesturerecognizer

Scale and move an UIView using UIPanGestureRecognizer


I have a UIView A. I put a icon on view A and try to use pan gesture to scale and move this view A.

I have try many solution, but i can not make it.

Please suggest help me?

More detail:

I will add more detail.

I have view A, add sub on self view. And i want when i draw panGestureRegonizer on view A, view A will move follow draw.

And while moving view A will scale. View A will scale to smaller when view move to top/left/bottom/right of sreen and scale to larger when view move to center of screen.


Solution

  • Let's say you have vc - ViewController and your UIView A is a subview of vc. You can add UIPanGestureRecognizer to A. Then drag the panGestureRegonizer to your vc as an action:

    @IBAction func panGestureAction(_ sender: UIPanGestureRecognizer) {
            //your code here
    }
    

    From the sender you can check view , location and state of the action. The state might impact your code in some cases, depending on what you are trying to achieve.

    Then you need to modify the action to this:

    @IBAction func panGestureAction(_ sender: UIPanGestureRecognizer) {
            UIView.animateKeyframes(withDuration: 0.1, delay: 0, options: UIViewKeyframeAnimationOptions.calculationModeLinear, animations: {
                let location = sender.location(in: sender.view?.superview)
                sender.view?.center = location
            })
        }
    

    Here sender.view?.superview equals vc.view. This code snippet will detect the pan gesture, and then will move A so A.center is matching the gesture's location. Note that duration 0.1 is giving smooth animation effect to the movement.

    This will give you "move" functionality with pan gesture.

    EDIT for scaling:

    Logic: You have coordinate system(CS) with center, x and y. When the user uses pan gesture, he/she generates sequence of points in the CS. So our task is to measure the distance between the center of the CS and users' points. When we have the furthest distance, we can calculate scale factor for our scaling view.

    var center: CGPoint! //center of the CS
    let maxSize: CGSize = CGSize.init(width: 100, height: 100) // maximum size of our scaling view
    var maxLengthToCenter: CGFloat! //maximum distance from the center of the CS to the furthest point in the CS
    
    private func prepareForScaling() {
        self.center = self.view.center //we set the center of our CS to equal the center of the VC's view
        let frame = self.view.frame
        //the furthest distance in the CS is the diagonal, and we calculate it using pythagoras theorem
        self.maxLengthToCenter = (frame.width*frame.width + frame.height*frame.height).squareRoot() 
    }
    

    Then we need to call our setup functional to have our data ready for scaling functionality - we can do this in viewDidLoad:

    override func viewDidLoad() {
        super.viewDidLoad()  
        self.prepareForScaling()
    }
    

    Then we need a helper function to calculates the scaled size of our view, for user's pan gesture current position on the screen.

    private func scaledSize(for location: CGPoint) -> CGSize {
        //calculate location x,y differences from the center
        let xDifference = location.x - self.center.x
        let yDifference = location.y - self.center.y
    
        //calculate the scale factor - note that this factor will be between 0.0(center) and 0.5(diagonal - furthest point)    
        //It is due our measurement - from center to view's edge. Consider multiplying this factor with your custom constant.
        let scaleFactor = (xDifference*xDifference + yDifference*yDifference).squareRoot()/maxLengthToCenter
        //create scaled size with maxSize and current scale factor
        let scaledSize = CGSize.init(width: maxSize.width*(1-scaleFactor), height: maxSize.height*(1-scaleFactor))
    
        return scaledSize
    }
    

    And finally, we need to modify our pan gesture action to change the size of A:

    @IBAction func panGestureAction(_ sender: UIPanGestureRecognizer) {
        UIView.animateKeyframes(withDuration: 0.1, delay: 0, options: UIViewKeyframeAnimationOptions.calculationModeLinear, animations: {
    
            let location = sender.location(in: sender.view?.superview)
    
            sender.view?.frame = CGRect.init(origin: CGPoint.init(x: 0, y: 0), size: self.scaledSize(for: location))
    
            sender.view?.center = location
        })
    }