【问题标题】:Translating a UIView after rotating旋转后翻译 UIView
【发布时间】:2014-03-23 21:27:46
【问题描述】:

我正在尝试翻译已使用用户触摸旋转和/或缩放的 UIView。我也尝试使用用户输入来翻译它:

- (void)handleObjectMove:(UIPanGestureRecognizer *)recognizer
{
    static CGPoint lastPoint;
    UIView *moveView = [recognizer view];
    CGPoint newCoord = [recognizer locationInView:playArea];

    // Check if this is the first touch
    if( [recognizer state]==UIGestureRecognizerStateBegan )
    {
        // Store the initial touch so when we change positions we do not snap
        lastPoint = newCoord;
    }

    // Create the frame offsets to use our finger position in the view.
    float dX = newCoord.x;
    float dY = newCoord.y;

    dX-=lastPoint.x;
    dY-=lastPoint.y;

    // Figure out the translation based on how we are scaled
    CGAffineTransform transform = [moveView transform];
    CGFloat xScale = transform.a;
    CGFloat yScale = transform.d;

    dX/=xScale;
    dY/=yScale;

    lastPoint = newCoord;

    [moveView setTransform:CGAffineTransformTranslate( transform, dX, dY )];

    [recognizer setTranslation:CGPointZero inView:playArea];
}

但是当我触摸并移动视图时,它会以各种不同的奇怪方式进行翻译。我可以使用旋转值应用某种公式来正确翻译吗?

【问题讨论】:

  • 换句话说,您正在尝试将来自该识别器的翻译与来自另一个手势识别器(例如,UIPinchGestureRecognizer)的旋转和/或缩放结合起来?
  • 没错。 UIView 在翻译之前已经被用户输入旋转和/或缩放。所以我需要保持旋转/缩放并翻译它。

标签: ios objective-c rotation translation cgaffinetransform


【解决方案1】:

我发现必须使用最少的数学运算的最佳解决方案是分别存储原始平移、旋转和缩放值,并在更改时重做变换。我的解决方案是使用以下属性对 UIView 进行子类化:

@property (nonatomic) CGPoint translation;
@property (nonatomic) CGFloat rotation;
@property (nonatomic) CGPoint scaling;

还有以下功能:

- (void)rotationDelta:(CGFloat)delta
{
    [self setRotation:[self rotation]+delta];
}

- (void)scalingDelta:(CGPoint)delta
{
    [self setScaling:
     (CGPoint){ [self scaling].x*delta.x, [self scaling].y*delta.y }];
}

- (void)translationDelta:(CGPoint)delta
{
    [self setTranslation:
     (CGPoint){ [self translation].x+delta.x, [self translation].y+delta.y }];
}

- (void)transformMe
{
    // Start with the translation
    CGAffineTransform transform = CGAffineTransformMakeTranslation( [self translation].x, [self translation].y );
    // Apply scaling
    transform = CGAffineTransformScale( transform, [self scaling].x, [self scaling].y );
    // Apply rotation
    transform = CGAffineTransformRotate( transform, [self rotation] );

    [self setTransform:transform];
}

- (void)setScaling:(CGPoint)newScaling
{
    scaling = newScaling;
    [self transformMe];
}

- (void)setRotation:(CGFloat)newRotation
{
    rotation = newRotation;
    [self transformMe];
}

- (void)setTranslation:(CGPoint)newTranslation
{
    translation = newTranslation;
    [self transformMe];
}

并在处理程序中使用以下内容:

- (void)handleObjectPinch:(UIPinchGestureRecognizer *)recognizer
{
    if( [recognizer state] == UIGestureRecognizerStateEnded
        || [recognizer state] == UIGestureRecognizerStateChanged )
    {
        // Get my stuff
        if( !selectedView )
            return;

        SelectableImageView *view = selectedView;

        CGFloat scaleDelta = [recognizer scale];
        [view scalingDelta:(CGPoint){ scaleDelta, scaleDelta }];

        [recognizer setScale:1.0];
    }
}

- (void)handleObjectMove:(UIPanGestureRecognizer *)recognizer
{
    static CGPoint lastPoint;
    SelectableImageView *moveView = (SelectableImageView *)[recognizer view];
    CGPoint newCoord = [recognizer locationInView:playArea];

    // Check if this is the first touch
    if( [recognizer state]==UIGestureRecognizerStateBegan )
    {
        // Store the initial touch so when we change positions we do not snap
        lastPoint = newCoord;
    }

    // Create the frame offsets to use our finger position in the view.
    float dX = newCoord.x;
    float dY = newCoord.y;

    dX-=lastPoint.x;
    dY-=lastPoint.y;
    lastPoint = newCoord;

    [moveView translationDelta:(CGPoint){ dX, dY }];

    [recognizer setTranslation:CGPointZero inView:playArea];
}

- (void)handleRotation:(UIRotationGestureRecognizer *)recognizer
{
    if( [recognizer state] == UIGestureRecognizerStateEnded
       || [recognizer state] == UIGestureRecognizerStateChanged )
    {
        if( !selectedView )
            return;

        SelectableImageView *view = selectedView;

        CGFloat rotation = [recognizer rotation];
        [view rotationDelta:rotation];

        [recognizer setRotation:0.0];
    }
}

【讨论】:

  • 我认为结构上看起来不错。另一种选择是在 setter 方法中设置一个标志,将转换标记为无效,然后在绘制视图之前更新一次转换(如果无效)。此外,我通常更希望将缩放更改为乘法而不是向量加法,但如果它适用于您的应用程序,那就太好了。
  • 我更喜欢你的想法,让缩放变化是乘法而不是加法。它在乘法中运行得更好。我更新了我的帖子以反映这些变化。谢谢!
【解决方案2】:

尝试直接更改 moveView.center 而不是 Set (x,y) 或“CGAffineTransformTranslate”

【讨论】:

    【解决方案3】:

    这是用于可转换 UIView 的 Swift 4/5 版本

    class TransformableImageView: UIView{
    
    var translation:CGPoint = CGPoint(x:0,y:0)
    
    var scale:CGPoint = CGPoint(x:1, y:1)
    
    var rotation:CGFloat = 0
    
    
    override init (frame : CGRect) {
       super.init(frame: frame)
    }
    
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func rotationDelta(delta:CGFloat) {
        rotation = rotation + delta
    }
    
    func scaleDelta(delta:CGPoint){
        scale = CGPoint(x: scale.x*delta.x, y: scale.y * delta.y)
    }
    
    func translationDelta(delta:CGPoint){
        translation = CGPoint(x: translation.x+delta.x, y: translation.y + delta.y)
    }
    
    
    
    func transform(){
        self.transform = CGAffineTransform.identity.translatedBy(x: translation.x, y: translation.y).scaledBy(x: scale.x, y: scale.y ).rotated(by: rotation )
    }
    
    }
    

    【讨论】:

      【解决方案4】:

      我将把它留在这里,因为我也遇到了同样的问题。以下是在 swift 2 中的操作方法:

      将顶视图作为子视图添加到底视图:

      self.view.addSubview(topView)
      

      然后添加一个 PanGesture 识别器以在触摸时移动:

      //Add PanGestureRecognizer to move
          let panMoveGesture = UIPanGestureRecognizer(target: self, action: #selector(YourViewController.moveViewPanGesture(_:)))
          topView.addGestureRecognizer(panMoveGesture)
      

      以及移动的功能:

      //Move function
      func moveViewPanGesture(recognizer:UIPanGestureRecognizer)
      {
          if recognizer.state == .Changed {
              var center = recognizer.view!.center
              let translation = recognizer.translationInView(recognizer.view?.superview)
              center = CGPoint(x:center.x + translation.x,
                                   y:center.y + translation.y)
              recognizer.view!.center = center
              recognizer.setTranslation(CGPoint.zero, inView: recognizer.view)
          }
      }
      

      基本上,您需要根据底视图来翻译您的视图,底视图是它的父视图而不是它本身。像这样:recognizer.view?.superview 或者,如果您还旋转底视图,您可以添加一个不会有任何转换的视图,并将您的底视图添加到该未转换视图(非常底视图),并将顶视图相应地添加到底视图作为子视图。然后你应该根据最底视图翻译你的顶视图。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-09-01
        • 2014-08-27
        • 1970-01-01
        • 2012-01-11
        • 2011-11-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多