【问题标题】:Zoom a rotated image inside scroll view to fit (fill) frame of overlay rect缩放滚动视图内的旋转图像以适合(填充)覆盖矩形的框架
【发布时间】:2015-01-05 14:50:24
【问题描述】:

通过this question and answer,我现在有了一种有效的方法来检测任意旋转的图像何时不完全位于裁剪区域之外。

下一步是弄清楚如何正确调整它包含滚动视图缩放,以确保裁剪矩形内没有空格。为了澄清,我想放大(放大)图像;裁剪矩形应保持未转换。

布局层次如下所示:

containing UIScrollView
    UIImageView (this gets arbitrarily rotated)
        crop rect overlay view

... UIImageView 也可以在 scrollView 内进行缩放和平移。

需要考虑发生的 4 个手势事件:

  1. 平移手势(完成):通过检测是否平移错误并重置 contentOffset 来完成。
  2. 旋转 CGAffineTransform
  3. 滚动视图缩放
  4. 裁剪矩形覆盖框的调整

据我所知,我应该能够对 2、3 和 4 使用相同的逻辑来调整滚动视图的 zoomScale 以使图像适合。

如何正确计算使旋转后的图像完全适合裁剪区域所需的缩放比例?

为了更好地说明我想要完成的工作,这里是一个不正确大小的示例:

我需要计算使其看起来像这样所需的缩放比例:

这是我到目前为止使用 Oluseyi 的解决方案获得的代码。它在旋转角度较小(例如小于 1 弧度)时有效,但任何超过此值的情况都会变得非常不稳定。

CGRect visibleRect = [_scrollView convertRect:_scrollView.bounds toView:_imageView];
CGRect cropRect = _cropRectView.frame;

CGFloat rotationAngle = fabs(self.rotationAngle);

CGFloat a = visibleRect.size.height * sinf(rotationAngle);
CGFloat b = visibleRect.size.width * cosf(rotationAngle);
CGFloat c = visibleRect.size.height * cosf(rotationAngle);
CGFloat d = visibleRect.size.width * sinf(rotationAngle);

CGFloat zoomDiff = MAX(cropRect.size.width / (a + b), cropRect.size.height / (c + d));
CGFloat newZoomScale = (zoomDiff > 1) ? zoomDiff : 1.0 / zoomDiff;

[UIView animateWithDuration:0.2
                      delay:0.05
                    options:NO
                 animations:^{
                         [self centerToCropRect:[self convertRect:cropRect toView:self.zoomingView]];
                         _scrollView.zoomScale = _scrollView.zoomScale * newZoomScale;
                 } completion:^(BOOL finished) {
                     if (![self rotatedView:_imageView containsViewCompletely:_cropRectView]) 
                     {
                         // Damn, it's still broken - this happens a lot
                     }
                     else
                     {
                         // Woo! Fixed
                     }
                     _didDetectBadRotation = NO;
                 }];

请注意,我正在使用 AutoLayout,这会使框架和边界变得愚蠢。

【问题讨论】:

  • image view 是只绕中心旋转,还是可以平移?
  • @robmayoff 它也可以被翻译,但如果我能让它基于中心旋转工作,那将是一个巨大的开始。然后我可以使用相同的计算来比较裁​​剪视图的中心或其他东西。
  • 对于其他查看赏金的人来说,Oluseyi 下面的回答肯定是部分有效,但并非完全有效。随意添加其他答案。
  • 你找到方法了吗?
  • @brandonscript 你有想过这个吗?

标签: ios objective-c uiscrollview cgaffinetransform


【解决方案1】:

假设您的图像矩形(图中蓝色)和裁剪矩形(红色)具有相同的纵横比和中心。旋转时,图像矩形现在有一个边界矩形(绿色),这是您希望将裁剪缩放到的矩形(实际上,通过缩小图像)。

要有效缩放,您需要知道新边界矩形的尺寸并使用适合裁剪矩形的缩放因子。边界矩形的尺寸相当明显

(a + b) x (c + d)

请注意,每个线段 a、b、c、d 是由边界矩形和旋转图像矩形形成的直角三角形的相邻边或相对边。

a = image_rect_height * sin(rotation_angle)
b = image_rect_width * cos(rotation_angle)
c = image_rect_width * sin(rotation_angle)
d = image_rect_height * cos(rotation_angle)

你的比例因子很简单

MAX(crop_rect_width / (a + b), crop_rect_height / (c + d))

这是一个参考图:

【讨论】:

    【解决方案2】:

    覆盖矩形的填充框:

    对于方形裁剪,您需要知道将填充裁剪视图的旋转图像的新边界。

    我们看一下参考图: 您需要找到直角三角形的高度(图像编号 2)。两个高度相等。

    CGFloat sinAlpha = sin(alpha);
    CGFloat cosAlpha = cos(alpha);
    CGFloat hypotenuse = /* calculate */;
    CGFloat altitude = hypotenuse * sinAlpha * cosAlpha;
    

    然后您需要计算旋转图像的新宽度和所需的比例因子,如下所示:

     CGFloat newWidth = previousWidth + altitude * 2;
     CGFloat scale = newWidth / previousWidth;
    

    我已经实现了这个方法here

    【讨论】:

      【解决方案3】:

      我将使用示例代码来回答,但基本上这个问题变得非常容易,如果你在旋转视图坐标系中思考的话。

      UIView* container = [[UIView alloc] initWithFrame:CGRectMake(80, 200, 100, 100)];
      container.backgroundColor = [UIColor blueColor];
      
      UIView* content2 = [[UIView alloc] initWithFrame:CGRectMake(-50, -50, 150, 150)];
      content2.backgroundColor = [[UIColor greenColor] colorWithAlphaComponent:0.5];
      [container addSubview:content2];
      
      [self.view setBackgroundColor:[UIColor blackColor]];
      [self.view addSubview:container];
      
      [container.layer setSublayerTransform:CATransform3DMakeRotation(M_PI / 8.0, 0, 0, 1)];
      
      //And now the calculations
      CGRect containerFrameInContentCoordinates = [content2 convertRect:container.bounds fromView:container];
      CGRect unionBounds = CGRectUnion(content2.bounds, containerFrameInContentCoordinates);
      
      CGFloat midX = CGRectGetMidX(content2.bounds);
      CGFloat midY = CGRectGetMidY(content2.bounds);
      
      CGFloat scaleX1 = (-1 * CGRectGetMinX(unionBounds) + midX) / midX;
      CGFloat scaleX2 = (CGRectGetMaxX(unionBounds) - midX) / midX;
      CGFloat scaleY1 = (-1 * CGRectGetMinY(unionBounds) + midY) / midY;
      CGFloat scaleY2 = (CGRectGetMaxY(unionBounds) - midY) / midY;
      
      CGFloat scaleX = MAX(scaleX1, scaleX2);
      CGFloat scaleY = MAX(scaleY1, scaleY2);
      CGFloat scale = MAX(scaleX, scaleY);
      
      content2.transform = CGAffineTransformScale(content2.transform, scale, scale);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-02-09
        相关资源
        最近更新 更多