【问题标题】:Pan, zoom, rotate UIView around anchorPoint using UIGestureRecognizer (iOS)使用 UIGestureRecognizer (iOS) 围绕锚点平移、缩放、旋转 UIView
【发布时间】:2014-03-12 17:31:27
【问题描述】:

我正在尝试使用 UIGestureRecognizers 旋转、平移和缩放 UIView。识别器被添加到超级视图中,而旋转、缩放等被应用于子视图(_baseImage),这是为了让我将来能够在子视图之上覆盖其他东西,同时仍然接收手势事件。

这个想法是 UIView 应该围绕两个触摸点下方的子视图上的“锚点”点进行缩放/旋转,因为这似乎是最自然的。我遇到的问题是设置anchorPoint后子视图的位置,而且缩放和旋转似乎没有使用设置的anchorPoint。我对重叠坐标系/CGAffine 变换缺乏了解可能会给我带来麻烦。代码来自各种示例。

这是我目前的代码:

-(void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view
{
    CGPoint oldOrigin = view.frame.origin;
    view.layer.anchorPoint = anchorPoint;
    CGPoint newOrigin = view.frame.origin;

    CGPoint transition;
    transition.x = newOrigin.x - oldOrigin.x;
    transition.y = newOrigin.y - oldOrigin.y;

    view.center = CGPointMake (view.center.x - transition.x, view.center.y - transition.y);

}
- (void) updateTransformWithOffset: (CGPoint) translation
{
    // Create a blended transform representing translation,
    // rotation, and scaling
    _baseImage.transform = CGAffineTransformMakeTranslation(translation.x + tx, translation.y + ty);
    _baseImage.transform = CGAffineTransformRotate(_baseImage.transform, theta);
    _baseImage.transform = CGAffineTransformScale(_baseImage.transform, scale, scale);
}
- (void)adjustAnchorPointForGestureRecognizer:(UIGestureRecognizer *)uigr {
    if (uigr.state == UIGestureRecognizerStateBegan) {
        UIView *piece = self.view;
        CGPoint locationInView = [uigr locationInView:_baseImage];
        myFrame = _baseImage.frame;
        CGPoint newAnchor = CGPointMake( (locationInView.x / piece.bounds.size.width), (locationInView.y / piece.bounds.size.height ));
       [self setAnchorPoint:newAnchor forView:_baseImage];
    }
}
- (void) handlePinch: (UIPinchGestureRecognizer *) uigr
{
    [self adjustAnchorPointForGestureRecognizer:uigr];
    if (uigr.state == UIGestureRecognizerStateBegan) {
        initScale = scale;
    }
    scale = initScale*uigr.scale;
    [self updateTransformWithOffset:CGPointZero];
}

【问题讨论】:

  • 是的,您会在我的代码中看到我正在使用anchorPoint……问题是我似乎无法让它按预期工作。
  • 我现在明白了。我刚刚看到了大量处理转换的代码。
  • 你的问题不是很精确,而且你的代码很复杂。我建议您尝试将其分解为运行良好的部分和有问题的部分。然后删除所有其他内容,直到您拥有最少量的代码来重现您的问题。
  • 我已经尝试简化并删除了除比例元素之外的所有内容。

标签: ios objective-c uiview uigesturerecognizer cgaffinetransform


【解决方案1】:

我找到了解决我的 anchorPoint 特定问题的方法。我试图将转换应用于在 Interface Builder 中添加的 UIView,这会导致锚点出现问题并设置新的中心点,删除然后重新添加子视图似乎可以解决它。如果有人有类似的问题,这里是我在视图控制器上使用的最终代码,它使用触摸位置作为旋转和缩放的中心在 UIView 上进行缩放、旋转和平移:

#import "ViewController.h"

@interface ViewController (){
    CGFloat tx; // x translation
    CGFloat ty; // y translation
    CGFloat scale; // zoom scale
    CGFloat theta; // rotation angle
    CGFloat initScale ;
    CGFloat initTheta ;
}
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    UIRotationGestureRecognizer *rotationGesture = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(handleRotation:)];
    [rotationGesture setDelegate:self];
    [self.view addGestureRecognizer:rotationGesture];
    UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)];
    [pinchGesture setDelegate:self];
    [self.view addGestureRecognizer:pinchGesture];
    UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
    [panGesture setDelegate:self];
    [panGesture setMinimumNumberOfTouches:1];
    [panGesture setMaximumNumberOfTouches:1];
    [self.view addGestureRecognizer:panGesture];
    _baseImage.transform = CGAffineTransformIdentity;
    tx = 0.0f; ty = 0.0f; scale = 1.0f; theta = 0.0f;
    scale = 1.0;
    //removing and adding back to the view seems to fix problems with anchor point I was having, I suspect because of IB layout/scaling and constraints etc
    UIView *mySuperView =_baseImage.superview;
    [_baseImage removeFromSuperview];
    [mySuperView addSubview:_baseImage];
}
-(void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)myview
{
    CGPoint oldOrigin = myview.frame.origin;
    myview.layer.anchorPoint = anchorPoint;
    CGPoint newOrigin = myview.frame.origin;
    CGPoint transition;
    transition.x = (newOrigin.x - oldOrigin.x);
    transition.y = (newOrigin.y - oldOrigin.y);
    CGPoint myNewCenter = CGPointMake (myview.center.x - transition.x, myview.center.y - transition.y);
    myview.center =  myNewCenter;
}
- (void) updateTransformWithOffset: (CGPoint) translation
{
    // Create a blended transform representing translation,
    // rotation, and scaling
    _baseImage.transform = CGAffineTransformMakeTranslation(translation.x + tx, translation.y + ty);
    _baseImage.transform = CGAffineTransformRotate(_baseImage.transform, theta);
    _baseImage.transform = CGAffineTransformScale(_baseImage.transform, scale, scale);
}
- (void)adjustAnchorPointForGestureRecognizer:(UIGestureRecognizer *)uigr {
    if (uigr.state == UIGestureRecognizerStateBegan) {
        tx =_baseImage.transform.tx;
        ty =_baseImage.transform.ty;
        CGPoint locationInView = [uigr locationInView:_baseImage];
        CGPoint newAnchor = CGPointMake( (locationInView.x / _baseImage.bounds.size.width), (locationInView.y / _baseImage.bounds.size.height ));
        [self setAnchorPoint:newAnchor forView:_baseImage];
    }
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    // if the gesture recognizers are on different views, don't allow simultaneous recognition
    if (gestureRecognizer.view != otherGestureRecognizer.view)
        return NO;

    if (![gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] && ![otherGestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
        return YES;
    }
    return NO;
}
- (void) handleRotation: (UIRotationGestureRecognizer *) uigr
{
    if (uigr.state == UIGestureRecognizerStateBegan) {
        initTheta = theta;
    }
    theta = initTheta+uigr.rotation;
    [self adjustAnchorPointForGestureRecognizer:uigr];
    [self updateTransformWithOffset:CGPointZero];
}
- (void) handlePinch: (UIPinchGestureRecognizer *) uigr
{
    if (uigr.state == UIGestureRecognizerStateBegan) {
        initScale = scale;
    }
    scale = initScale*uigr.scale;
    [self adjustAnchorPointForGestureRecognizer:uigr];
    [self updateTransformWithOffset:CGPointZero];

}
- (void) handlePan: (UIPanGestureRecognizer *) uigr
{
    CGPoint translation = [uigr translationInView:_baseImage.superview];
    [self adjustAnchorPointForGestureRecognizer:uigr];
    [self updateTransformWithOffset:translation];
}
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

【讨论】:

    【解决方案2】:

    OP 的原始解决方案已翻译为 Swift 2.0。我省略了解决方法,因为它似乎不再是问题了。

    class ViewController: UIViewController {
    
        var tx:CGFloat = 0.0 // x translation
        var ty:CGFloat = 0.0 // y translation
        var scale:CGFloat = 1.0 // zoom scale
        var theta:CGFloat = 0.0 // rotation angle
        var initScale:CGFloat = 1.0
        var initTheta:CGFloat = 0.0
        var transformedView: UIView = UIView()
            
        override func viewDidLoad() {
           super.viewDidLoad()
           transformedView.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
           transformedView.backgroundColor = UIColor.blueColor()
           view.addSubview(transformedView)
                
           let rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(OHPlastyViewController.handleRotation(_:)))
           rotationGesture.delegate = self
           view.addGestureRecognizer(rotationGesture)
    
           let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(OHPlastyViewController.handlePinch(_:)))
           pinchGesture.delegate = self
           view.addGestureRecognizer(pinchGesture)
                
           let panGesture = UIPanGestureRecognizer(target: self, action: #selector(OHPlastyViewController.handlePan(_:)))
           panGesture.delegate = self
           panGesture.minimumNumberOfTouches = 1
           panGesture.maximumNumberOfTouches = 1
           view.addGestureRecognizer(panGesture)
        }
    
        func setAnchorPoint(anchorPoint: CGPoint, forView myView: UIView) {
           let oldOrigin: CGPoint = myView.frame.origin
           myView.layer.anchorPoint = anchorPoint
           let newOrigin = myView.frame.origin
           let transition = CGPoint(x: newOrigin.x - oldOrigin.x, y: newOrigin.y - oldOrigin.y)
           let myNewCenter = CGPoint(x: myView.center.x - transition.x, y: myView.center.y - transition.y)
           myView.center = myNewCenter
        }
            
        func updateTransformWithOffset(translation: CGPoint) {
           transformedView.transform = CGAffineTransformMakeTranslation(translation.x + tx, translation.y + ty)
           transformedView.transform = CGAffineTransformRotate(transformedView.transform, theta)
           transformedView.transform = CGAffineTransformScale(transformedView.transform, scale, scale)
        }
            
        func adjustAnchorPointForGestureRecognizer(recognizer: UIGestureRecognizer) {
           if (recognizer.state == .Began) {
                tx = transformedView.transform.tx
                ty = transformedView.transform.ty
                let locationInView = recognizer.locationInView(transformedView)
                let newAnchor = CGPoint(x: (locationInView.x / transformedView.bounds.size.width), y: (locationInView.y / transformedView.bounds.size.height))
                setAnchorPoint(newAnchor, forView: transformedView)
            }
        }
            
        func handleRotation(recognizer: UIRotationGestureRecognizer) {
           if recognizer.state == .began {
               initTheta = theta
           }
           theta = initTheta + recognizer.rotation
           adjustAnchorPointForGestureRecognizer(recognizer)
           updateTransformWithOffset(CGPointZero)
        }
    
        func handlePinch(recognizer: UIPinchGestureRecognizer) {
            if recognizer.state == .began {
                initScale = scale
            }
            scale = initScale * recognizer.scale
            adjustAnchorPointForGestureRecognizer(recognizer)
            updateTransformWithOffset(CGPointZero)
        }
            
        func handlePan(recognizer: UIPanGestureRecognizer) {
            let translation = recognizer.translationInView(transformedView.superview)
            adjustAnchorPointForGestureRecognizer(recognizer)
            updateTransformWithOffset(translation)
        }
    }
    
    extension ViewController: UIGestureRecognizerDelegate {
            
        func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
            if gestureRecognizer.view != otherGestureRecognizer.view {
                return false
            }
                
            if !gestureRecognizer.isKindOfClass(UITapGestureRecognizer.self) && !otherGestureRecognizer.isKindOfClass(UITapGestureRecognizer.self) {
                return true
            }
            return false
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-27
      • 1970-01-01
      • 1970-01-01
      • 2019-01-14
      • 2014-02-22
      相关资源
      最近更新 更多