如何在缩放的UIView子视图上计算正确的CGRect原点?

时间:2018-09-28 18:52:41

标签: ios objective-c uiview scale

我需要在原始视图的坐标中计算UIView子视图的可见CGRect。如果缩放比例为1,则可以正常工作,但是如果对某个超级视图或视图本身进行缩放(捏),则可见CGRect原点会稍微偏移。

当视图的比例为1或该视图是根视图的子视图时,此方法有效:

// return the part of the passed view that is visible
// TODO:  figure out why result origin is wrong for scaled subviews
//
- (CGRect)getVisibleRect:(UIView *)view {
    // get the root view controller (and it's view is vc.view)
    UIViewController *vc = UIApplication.sharedApplication.keyWindow.rootViewController;

    // get the view's frame in the root view's coordinate system
    CGRect frame = [vc.view convertRect:view.frame fromView:view.superview];

    // get the intersection of the root view bounds and the passed view frame
    CGRect intersection = CGRectIntersection(vc.view.bounds, frame);

    // adjust the intersection coordinates thru any nested views
    UIView *loopView = view;
    do {
        intersection = [loopView convertRect:intersection fromView:loopView.superview];

        loopView = loopView.superview;
    } while (loopView != vc.view);

    return intersection; // may be same as the original view frame
}

缩放子视图时,结果视图的大小是正确的,但原点会偏移少量。看来convertRect不能正确计算缩放后的子视图的原点。

我尝试相对于X / Y变换比例调整原点,但无法正确计算。也许有人可以帮忙?

为节省时间,这是一个完整的ViewController.m测试,其中在视图的可见部分上绘制了一个带有X的框-只需在Main.storyboard中创建一个reset按钮并将其连接到{{1 }}方法:

reset

2 个答案:

答案 0 :(得分:2)

因此,您需要知道以某种方式从frame + bounds + center派生而来的视图的真实可见transform,并从中计算其他所有内容,而不是普通的frame值。这意味着您还必须重新创建convertRect:fromView:以基于此。我总是通过仅对不需要这种计算的短动画使用transform来避免这个问题。考虑编码这样的-getVisibleRect:方法使我想逃避尖叫;)

什么是frame

frame属性是从centerbounds派生的。

示例:

  • center(60,50)
  • bounds(0,0,100,100)
  • => frame(10,0,100,100)

现在,您将frame更改为(10,20,100,100)。因为视图的大小没有改变,所以只会导致center的改变。新的center现在为(60,70)

transform怎么样?

假设您现在将视图缩放到50%来对其进行变换。

=>现在,视图的大小是以前的一半,而中心仍然保持不变。看起来新框架是(35,45,50,50)。但是实际结果是:

  • center仍然是(60,50):这是预期的
  • bounds仍然是(0,0,100,100):这应该也是预期的
  • frame仍然是(10,20,100,100)这有点违反直觉

frame是一个计算所得的属性,它与当前的transform完全无关。这意味着只要frame不是身份变换,transform的值就没有意义。这甚至是documented behaviour。 Apple在这种情况下将frame的值称为“未定义”。

后果

这会带来额外的后果,当涉及到非标准转换时,诸如convertRect:fromView:之类的方法将无法正常工作。这是因为所有这些方法都依赖于视图的framebounds,并且一旦涉及到转换,它们就会立即中断。

该怎么办?

假设您有三种视图:

  • view1(无变换)
    • view2(比例转换50%)
      • view3(无变换)

您想从view1的角度知道view3的坐标。

从view2的角度来看,view3具有框架view3.frame。简单。 从view1的角度来看,view2没有框架view2.frame,但是可见的框架是矩形,大小为view2.bounds/2,中心为view2.center

要正确处理此问题,您需要一些基本的线性代数(带有矩阵乘法)。 (并且不要忘记anchorPoint ..)

希望对您有帮助。

真正可以做什么?

在您的问题中,您说有一个偏移量。也许您现在可以计算误差?该错误应类似于0.5 *(1-scale)*(bounds.size)。如果可以计算出误差,则可以将其减去并称之为一天:)

答案 1 :(得分:0)

感谢@Michael付出了很多努力。它没有解决问题,但使我思考了更多,然后尝试了其他一些事情。

瞧,我尝试了以前可以做的事情,但是这次我以最新的代码开始。事实证明,有一个简单的解决方案可以解决问题。内置UIView convertRect:fromViewconvertRect:toView一起使用时可以按预期工作。

我向在此问题上花费时间的任何人表示歉意。我对自己的愚蠢以及在其中花费了多少时间感到谦卑。我以前尝试过此操作时一定在某个地方犯了一个错误,因为它不起作用。但这现在效果很好:

// return the part of the passed view that is visible
- (CGRect)getVisibleRect:(UIView *)view {
    // get the root view controller (and it's view is vc.view)
    UIViewController *vc = UIApplication.sharedApplication.keyWindow.rootViewController;

    // get the view's frame in the root view's coordinate system
    CGRect rootRect = [vc.view convertRect:view.frame fromView:view.superview];

    // get the intersection of the root view bounds and the passed view frame
    CGRect rootVisible = CGRectIntersection(vc.view.bounds, rootRect);

    // convert the rect back to the initial view's coordinate system
    CGRect visible = [view convertRect:rootVisible fromView:vc.view];

    return visible; // may be same as the original view frame
}

如果有人使用我问题中的Viewcontroller.m,只需用此方法替换getVisibleRect方法,它将很好地工作。

注意:我尝试旋转视图,并且可见矩形也被旋转,因为我将其显示在视图本身上。我想我可以反转形状图层上的任何视图旋转,但这是另一天!