Scale group of UIViews but keeping its position

2019-07-31 19:02发布

I am trying to scale multiple UIViews at once with the pinch gesture(On the base view). Though it looked straight forward, I am facing hard time getting it working properly.

Assume I have 3 views stacked like this

----------------------------------------------
|                                            |
|    -------                      --------   |   
|    |     |                      |      |   |
|    |  V1 |   ---------------    |      |   |
|    |     | X |     v2      |  Y |   v3 |   |
|    -------   ---------------    |      |   |
|                                 --------   |
---------------------------------------------

On pinch, I am scaling each of the 3 views by calculating the transform using the scale propery

CGAffineTransform v1Transform =  CGAffineTransformScale( v1.transform,recognizer.scale, recognizer.scale);
v1.transform = v1Transform
CGAffineTransform v2Transform =  CGAffineTransformScale( v2.transform,recognizer.scale, recognizer.scale);
v2.transform = v2Transform
CGAffineTransform v3Transform =  CGAffineTransformScale( v3.transform,recognizer.scale, recognizer.scale);
v3.transform = v3Transform

And change the center of each view using the current scale, like this

   CGPoint v1Center = CGPointMake(v1.center.x * recognizer.scale,  node.imgView.center.y * recognizer.scale);
   CGPoint v2Center = CGPointMake(v2.center.x * recognizer.scale,  node.imgView.center.y * recognizer.scale);
   CGPoint v3Center = CGPointMake(v3.center.x * recognizer.scale,  node.imgView.center.y * recognizer.scale);
  v1.cente = v1Center;
  v2.center = v2Center;
  v3.center = v3Center;

Though this scales each of the 3 views uniformly, I feel the center point is too off and the scaling looks weirder. please check the below screen capture of the issue

enter image description here

As you can see, though the scale works as expected and all 3 views scale together. Center is still an issue. I want the scale to be done from all 3 views actual place, I dont want them to move like this in the gif.

Is there a way? any help is much appreciated

3条回答
乱世女痞
2楼-- · 2019-07-31 19:45

What's happening is that you are also scaling the view's x and y positions in relation to 0,0 which is the corner of the screen. You need to do it in relation to the centre of the screen or perhaps the centre of your pinch gesture. I can't remember off the top of my head but I think there's a way to specify the anchor point of a scale, if not you'll have to calculate the position adjustments manually.

查看更多
Evening l夕情丶
3楼-- · 2019-07-31 19:49

If I understood correctly, you want the usual behavior when scaling a group of elements, which is for them to scale from the center of all selected elements.

I've done this before, it's in Swift and it uses some extensions of a library I made called CGMath*, but it should be good enough as pseudocode:

let scale = recognizer.scale
//  Reset the scale of the recognizer. We'll scale the views by the change in the last tick.
recognizer.scale = 1
//  Get the center of all selected views as a CGPoint
let center = views.map { $0.center }.reduce(CGPoint.zero, +) / CGFloat(views.count)
views.forEach {
    //  Scale the view
    $0.scale *= scale
    //  Get the distance from this view to the center of all views, this is a CGPoint
    let distance = $0.center - center
    //  Multiply the distance by the scale and add it to the center of all views. Set that as the view's center.
    $0.center = center + (distance * scale)
}

*the extensions in CGMath are what make it possible to add and subtract points.

查看更多
\"骚年 ilove
4楼-- · 2019-07-31 19:49

@EmilioPelaez Answer works as expected. This is the obj-c version of his pseudocode incase anyone interested

CGPoint newCenter =CGPointZero;
for(UIView *node in self.selectedNodes){
    newCenter = CGPointMake(newCenter.x + node.center.x, newCenter.y + node.center.y);
}

newCenter = CGPointMake(newCenter.x/self.selectedNodes.count, newCenter.y/self.selectedNodes.count);
for(UIview *node in self.selectedNodes){
    CGAffineTransform newTransform =  CGAffineTransformScale( node.transform,recognizer.scale, recognizer.scale);
    node.transform = newTransform;
    CGPoint distance = CGPointMake(node.center.x - newCenter.x, node.center.y - newCenter.y);
    CGPoint delta= CGPointMake(distance.x * recognizer.scale, distance.y * recognizer.scale);
    node.center =CGPointMake(newCenter.x + delta.x, newCenter.y + delta.y);
}
查看更多
登录 后发表回答