gpt4 book ai didi

ios - 如何在缩放的 UIView subview 上计算正确的 CGRect 原点?

转载 作者:行者123 更新时间:2023-11-28 20:55:43 26 4
gpt4 key购买 nike

我需要在原始 View 的坐标中计算 UIView subview 的可见 CGRect。如果比例为 1,我可以正常工作,但如果其中一个 super View 或 View 本身被缩放(收缩),可见的 CGRect 原点会稍微偏移。

这在 View 的比例为 1 或 View 是 Root View 的 subview 时起作用:

// 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
}

缩放 subview 时,结果 View 的大小是正确的,但原点有少量偏移。看来 convertRect 没有正确计算缩放 subview 的原点。

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

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

//
// ViewController.m
// VisibleViewDemo
//
// Copyright © 2018 ByteSlinger. All rights reserved.
//

#import "ViewController.h"

CG_INLINE void drawLine(UIView *view,CGPoint point1,CGPoint point2, UIColor *color, NSString *layerName) {
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:point1];
[path addLineToPoint:point2];

CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [path CGPath];
shapeLayer.strokeColor = color.CGColor;
shapeLayer.lineWidth = 2.0;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.name = layerName;

[view.layer addSublayer:shapeLayer];
}

CG_INLINE void removeShapeLayers(UIView *view,NSString *layerName) {
if (view.layer.sublayers.count > 0) {
for (CALayer *layer in [view.layer.sublayers copy]) {
if ([layer.name isEqualToString:layerName]) {
[layer removeFromSuperlayer];
}
}
}
}

CG_INLINE void drawXBox(UIView *view, CGRect rect,UIColor *color) {
NSString *layerName = @"xbox";

removeShapeLayers(view, layerName);

CGPoint topLeft = CGPointMake(rect.origin.x,rect.origin.y);
CGPoint topRight = CGPointMake(rect.origin.x + rect.size.width,rect.origin.y);
CGPoint bottomLeft = CGPointMake(rect.origin.x, rect.origin.y + rect.size.height);
CGPoint bottomRight = CGPointMake(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height);

drawLine(view,topLeft,topRight,color,layerName);
drawLine(view,topRight,bottomRight,color,layerName);
drawLine(view,topLeft,bottomLeft,color,layerName);
drawLine(view,bottomLeft,bottomRight,color,layerName);
drawLine(view,topLeft,bottomRight,color,layerName);
drawLine(view,topRight,bottomLeft,color,layerName);
}

@interface ViewController ()

@end

@implementation ViewController

UIView *view1;
UIView *view2;
UIView *view3;

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

CGFloat width = [UIScreen mainScreen].bounds.size.width / 2;
CGFloat height = [UIScreen mainScreen].bounds.size.height / 4;

view1 = [[UIView alloc] initWithFrame:CGRectMake(width / 2, height / 2, width, height)];
view1.backgroundColor = UIColor.yellowColor;
[self.view addSubview:view1];
[self addGestures:view1];

view2 = [[UIView alloc] initWithFrame:CGRectMake(width / 2, height / 2 + height + 16, width, height)];
view2.backgroundColor = UIColor.greenColor;
[self.view addSubview:view2];
[self addGestures:view2];

view3 = [[UIView alloc] initWithFrame:CGRectMake(10, 10, width / 2, height / 2)];
view3.backgroundColor = [UIColor.blueColor colorWithAlphaComponent:0.5];
[view1 addSubview:view3]; // this one will behave differently
[self addGestures:view3];
}
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];

[self checkOnScreen:view1];
[self checkOnScreen:view2];
[self checkOnScreen:view3];
}

- (IBAction)reset:(id)sender {
view1.transform = CGAffineTransformIdentity;
view2.transform = CGAffineTransformIdentity;
view3.transform = CGAffineTransformIdentity;

[self.view setNeedsLayout];
}

- (void)addGestures:(UIView *)view {
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc]
initWithTarget:self action:@selector(handlePan:)];
[view addGestureRecognizer:panGestureRecognizer];

UIPinchGestureRecognizer *pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc]
initWithTarget:self action:@selector(handlePinch:)];
[view addGestureRecognizer:pinchGestureRecognizer];
}

// 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 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
}

- (void)checkOnScreen:(UIView *)view {
CGRect visibleRect = [self getVisibleRect:view];

if (CGRectEqualToRect(visibleRect, CGRectNull)) {
visibleRect = CGRectZero;
}

drawXBox(view,visibleRect,UIColor.blackColor);
}

//
// Pinch (resize) an image on the ViewController View
//
- (IBAction)handlePinch:(UIPinchGestureRecognizer *)recognizer {
static CGAffineTransform initialTransform;

if (recognizer.state == UIGestureRecognizerStateBegan) {
[self.view bringSubviewToFront:recognizer.view];

initialTransform = recognizer.view.transform;
} else if (recognizer.state == UIGestureRecognizerStateEnded) {
} else {
recognizer.view.transform = CGAffineTransformScale(initialTransform,recognizer.scale,recognizer.scale);

[self checkOnScreen:recognizer.view];

[self.view setNeedsLayout]; // update subviews
}
}

- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
static CGAffineTransform initialTransform;

if (recognizer.state == UIGestureRecognizerStateBegan) {
[self.view bringSubviewToFront:recognizer.view];

initialTransform = recognizer.view.transform;
} else if (recognizer.state == UIGestureRecognizerStateEnded) {
} else {
//get the translation amount in x,y
CGPoint translation = [recognizer translationInView:recognizer.view];

recognizer.view.transform = CGAffineTransformTranslate(initialTransform,translation.x,translation.y);

[self checkOnScreen:recognizer.view];

[self.view setNeedsLayout]; // update subviews
}
}
@end

最佳答案

所以你需要知道真正可见的frame以某种方式从 bounds 派生的 View + center + transform并从中计算其他所有内容,而不是普通的 frame值(value)。这意味着您还必须重新创建 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) .因为 View 的大小没有改变,所以这只会导致 center 发生变化。 .新center现在是(60,70) .

怎么样transform

假设您现在通过将 View 缩放到 50% 来变换 View 。

=> View 现在的大小是以前的一半,同时仍保持相同的中心。看起来新框架是 (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 View ,一旦涉及转换,它们就会中断。

可以做什么?

假设您有三个 View :

  • view1(无转换)
    • view2(缩放变换 50%)
      • view3(无变换)

并且您想知道从view1 的角度来看view3 的坐标。

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

要做到这一点,您需要一些基本的线性代数(包括矩阵乘法)。 (不要忘记 anchorPoint ..)

希望对你有帮助

真正可以做什么?

在您的问题中,您说存在偏移量。也许你现在可以计算误差?错误应该类似于 0.5 * (1-scale) * (bounds.size) 。如果你能计算出误差,你可以减去它并收工:)

关于ios - 如何在缩放的 UIView subview 上计算正确的 CGRect 原点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52561171/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com