有没有办法计算将视图从帧 A 转换到帧 B 所需的 CGAffineTransform?

Posted

技术标签:

【中文标题】有没有办法计算将视图从帧 A 转换到帧 B 所需的 CGAffineTransform?【英文标题】:Is there a way to calculate the CGAffineTransform needed to transform a view from frame A to frame B? 【发布时间】:2012-05-23 13:06:23 【问题描述】:

假设我有两个 CGRect,CGRect A 和 CGRect B。我的 UIView 的框架与 CGRect B 相同,但我想创建一个动画来显示 UIView 从框架 A 过渡到 B。

我正在尝试通过更改 UIView 的转换属性来做到这一点,因此我不必过多地弄乱它的框架。但是,我需要 CGAffineTransform 来实现这一点。计算这种变换的最佳方法是什么?

【问题讨论】:

是否有任何紧迫的理由不简单地将帧属性从 CGRect A 动画化到 CGRect B? 不按。但原因是保持视图的框架静态并使用变换来移动它可以显着简化在更复杂的状态更改(例如自动旋转)期间对其进行跟踪。 【参考方案1】:

以前的答案对我不起作用。这应该有效:

Swift 5(扩展)

extension CGAffineTransform 
    init(from source: CGRect, to destination: CGRect) 
        let t = CGAffineTransform.identity
            .translatedBy(x: destination.midX - source.midX, y: destination.midY - source.midY)
            .scaledBy(x: destination.width / source.width, y: destination.height / source.height)
        self.init(a: t.a, b: t.b, c: t.c, d: t.d, tx: t.tx, ty: t.ty)
    

斯威夫特 4

func transformFromRect(from source: CGRect, toRect destination: CGRect) -> CGAffineTransform 
    return CGAffineTransform.identity
        .translatedBy(x: destination.midX - source.midX, y: destination.midY - source.midY)
        .scaledBy(x: destination.width / source.width, y: destination.height / source.height)

斯威夫特

func transformFromRect(from: CGRect, toRect to: CGRect) -> CGAffineTransform 
    let transform = CGAffineTransformMakeTranslation(CGRectGetMidX(to)-CGRectGetMidX(from), CGRectGetMidY(to)-CGRectGetMidY(from))
    return CGAffineTransformScale(transform, to.width/from.width, to.height/from.height)

目标-C

+ (CGAffineTransform) transformFromRect:(CGRect)sourceRect toRect:(CGRect)finalRect 
    CGAffineTransform transform = CGAffineTransformIdentity;
    transform = CGAffineTransformTranslate(transform, -(CGRectGetMidX(sourceRect)-CGRectGetMidX(finalRect)), -(CGRectGetMidY(sourceRect)-CGRectGetMidY(finalRect)));
    transform = CGAffineTransformScale(transform, finalRect.size.width/sourceRect.size.width, finalRect.size.height/sourceRect.size.height);

    return transform;

【讨论】:

我遇到过的最好的 sn-ps 之一。干得好 我在您的评论中添加了 Swift 4 版本【参考方案2】:

我还没有找到任何一种方便的方法来解决这个问题,所以我求助于经过尝试和真正的矩阵计算来实现这一点。

给定一个 CGRect A 和 CGRect B,要计算从 A 到 B 所需的转换,请执行以下操作:

CGAffineTransform transform = CGAffineTransformTranslate(CGAffineTransformIdentity, -A.origin.x, -A.origin.y);
transform = CGAffineTransformScale(transform, 1/A.size.width, 1/A.size.height);
transform = CGAffineTransformScale(transform, B.size.width, B.size.height);
transform = CGAffineTransformTranslate(transform, B.origin.x, B.origin.y);

【讨论】:

【参考方案3】:

这是josema.vitaminew answer 的一个变体,它也考虑了纵横比(如果您正在处理视频或图像,这很有用):

+ (CGAffineTransform)transformFromRect:(CGRect)sourceRect toRect:(CGRect)finalRect keepingAspectRatio:(BOOL)keepingAspectRatio 
    CGAffineTransform transform = CGAffineTransformIdentity;

    transform = CGAffineTransformTranslate(transform, -(CGRectGetMidX(sourceRect)-CGRectGetMidX(finalRect)), -(CGRectGetMidY(sourceRect)-CGRectGetMidY(finalRect)));

    if (keepingAspectRatio) 
        CGFloat sourceAspectRatio = sourceRect.size.width/sourceRect.size.height;
        CGFloat finalAspectRatio = finalRect.size.width/finalRect.size.height;

        if (sourceAspectRatio > finalAspectRatio) 
            transform = CGAffineTransformScale(transform, finalRect.size.height/sourceRect.size.height, finalRect.size.height/sourceRect.size.height);
         else 
            transform = CGAffineTransformScale(transform, finalRect.size.width/sourceRect.size.width, finalRect.size.width/sourceRect.size.width);
        
     else 
        transform = CGAffineTransformScale(transform, finalRect.size.width/sourceRect.size.width, finalRect.size.height/sourceRect.size.height);
    

    return transform;

【讨论】:

【参考方案4】:

一个不错的 Swift 3 解决方案:

extension CGAffineTransform 
    init(from: CGRect, toRect to: CGRect) 
        self.init(translationX: to.midX-from.midX, y: to.midY-from.midY)
        self = self.scaledBy(x: to.width/from.width, y: to.height/from.height)
    

【讨论】:

【参考方案5】:

上面代码sn -p的最后一行需要改成如下:

transform = CGAffineTransformTranslate (transform, B.origin.x*A.size.width/B.size.width, B.origin.y*A.size.height/B.size.height);

【讨论】:

【参考方案6】:

我开始使用 Kanobius 答案,但要么我误解了他对“纵横比”一词的使用,要么他是,因为它没有按我预期的那样执行。这是我的版本,它保持原始视图的纵横比(通过对 H 和 W 应用缩放)以实现“纵横比”行为

+ (CGAffineTransform)transformFromRect:(CGRect)sourceRect toRect:(CGRect)finalRect keepingAspectRatio:(BOOL)keepingAspectRatio

    CGAffineTransform transform = CGAffineTransformIdentity;

    // since the scale transform works around the midpoints, align them before doing the transform.
    transform = CGAffineTransformTranslate(transform, -(CGRectGetMidX(sourceRect)-CGRectGetMidX(finalRect)), -(CGRectGetMidY(sourceRect)-CGRectGetMidY(finalRect)));

    CGFloat originalHeight = sourceRect.size.height;
    CGFloat scaledHeight = finalRect.size.height;
    CGFloat hScalePercentage = scaledHeight / originalHeight;

    CGFloat originalWidth = sourceRect.size.width;
    CGFloat scaledWidth = finalRect.size.width;
    CGFloat wScalePercentage = scaledWidth / originalWidth;

    if (keepingAspectRatio)  
    
        CGFloat scalePercentage = MIN(hScalePercentage, wScalePercentage);   // the most amount of scaling == smallest float number
        transform =  CGAffineTransformScale(transform, scalePercentage, scalePercentage);
    
    else
    
        transform = CGAffineTransformScale(transform, wScalePercentage, hScalePercentage);
    

    return transform;

【讨论】:

以上是关于有没有办法计算将视图从帧 A 转换到帧 B 所需的 CGAffineTransform?的主要内容,如果未能解决你的问题,请参考以下文章

将我的场景渲染到帧缓冲区对象(FBO)然后将该 FBO 渲染到屏幕所需的步骤是啥?

将数据帧转换为所需的字典格式

如何计算将字符串转换为回文所需的字符数?

有没有办法以所需的格式加入这两个表?

🔥界面卡顿的原因?

有没有办法询问 AutoLayout 约束视图的高度?