转换矩阵,以CGAffineTransform

问题描述 投票:1回答:2

我已经开发了Android上,应用程序,你可以在屏幕和泛上添加图片/旋转/缩放左右他们。从技术上说,我有一个固定方面父布局,其中包含这些图像(ImageViews)。他们设置宽度和高度match_parent。然后,我可以节省他们的服务器,该服务器然后我可以稍后加载上相同或其他设备上的位置。基本上,我只需保存矩阵值。然而,翻译的x / y具有绝对值(以像素为单位),但屏幕尺寸不同,所以代替我将它们设置为百分比值(因此在翻译x由父宽度和翻译y由父高度除以分频)。当加载图片我只是父布局宽度/高度乘以平移的x / y,一切看起来是一样的,甚至不同的设备上。

下面是我如何做到这一点:

float[] values = new float[9];
parentLayout.matrix.getValues(values);

values[Matrix.MTRANS_X] /= parentLayout.getWidth();
values[Matrix.MTRANS_Y] /= parentLayout.getHeight();

然后,在装载功能,我只是这个值乘。

图像操作是通过使用的postTranslate /后缩放/ postRotate与锚点进行(2指手势它的这些指状物之间的中点)。

现在,我想这个端口应用iOS和使用类似的技术来实现在iOS设备上正确的图像中的位置为好。 CGAffineTransform似乎非常相似,Matrix类。字段的顺序是不同的,但他们似乎相同的方式工作。

比方说,这个数组是从Android应用程序的样本值阵列:

let a: [CGFloat] = [0.35355338, -0.35355338, 0.44107446, 0.35355338, 0.35355338, 0.058058262]

我不保存最后3场,因为他们总是0,0和1,在iOS上,他们也不能将设置(文档还指出这是[0,0,1])。因此,这是“军转民”的样子:

let tx = a[2] * width //multiply percentage translation x with parent width
let ty = a[5] * height //multiply percentage translation y with parent height
let transform = CGAffineTransform(a: a[0], b: a[3], c: a[1], d: a[4], tx: tx, ty: ty)

然而,这只是非常简单的情况下工作。对于其他的图像通常是放错了地方,甚至完全搞砸了。

我注意到,在默认情况下,在Android上我在[0,0]锚点,但在iOS上它的图像的中心。

例如:

float midX = parentLayout.getWidth() / 2.0f;
float midY = parentLayout.getHeight() / 2.0f;

parentLayout.matrix.postScale(0.5f, 0.5f, midX, midY);

提供了以下矩阵:

0.5, 0.0, 360.0, 
0.0, 0.5, 240.0, 
0.0, 0.0, 1.0

[360.0,240.0]是简单地从左上角的载体。

然而,在iOS上我没有提供中点,因为它已经围绕中心转化:

let transform = CGAffineTransform(scaleX: 0.5, y: 0.5)

它给我:

CGAffineTransform(a: 0.5, b: 0.0, c: 0.0, d: 0.5, tx: 0.0, ty: 0.0)

我试过设置在iOS不同的定位点:

parentView.layer.anchorPoint = CGPoint(x: 0, y: 0)

但是,我不能得到正确的结果。我已经-width * 0.5试着翻译,-height * 0.5,规模和翻译回来,但我没有得到相同的结果在Android上。

总之:我怎样才能从iOS的修改矩阵从安卓到CGAffineTransform达到相同的外观?

我还附上作为-简单的,可能的示范项目。我们的目标是从Android输出数组中的方式复制到iOS中的项目“一个”阵列和修改CGAffineTransform计算(和/或parentView设置)的形象看起来这两个平台上相同的,无论是什么从Android的输出日志。

安卓:https://github.com/piotrros/MatrixAndroid

iOS版:https://github.com/piotrros/MatrixIOS

编辑:

@Sulthan解决方案是伟大的工作!虽然,我已经拿出了倒车的过程中,这也是我需要的子问题。使用他的回答,我试图融入我的应用程序:

let savedTransform = CGAffineTransform.identity
        .concatenating(CGAffineTransform(translationX: -width / 2, y: -height / 2))
        .concatenating(
            self.transform
        )
        .concatenating(CGAffineTransform(translationX: width / 2, y: height / 2))

let tx = savedTransform.tx
let ty = savedTransform.ty

let t = CGAffineTransform(a: savedTransform.a, b: savedTransform.b, c: savedTransform.c, d: savedTransform.d, tx: tx / cWidth, ty: ty / cHeight)

placement.transform = [Float(t.a), Float(t.c), Float(t.tx), Float(t.b), Float(t.d), Float(t.ty), 0, 0, 1]

放置只是一个举办矩阵的“Android”的版本类。宽度/高度是父的宽度/高度。我把TX / TY通过他们得到的百分比宽度/高度,就像我在Android上做。

但是,这是行不通的。 TX / TY是错误的。

对于输入:

[0.2743883, -0.16761175, 0.43753636, 0.16761175, 0.2743883, 0.40431038, 0.0, 0.0, 1.0]

它给回:

[0.2743883, -0.16761175, 0.18834576, 0.16761175, 0.2743883, 0.2182884, 0.0, 0.0, 1.0]

所以一切其他然后TX / TY是罚款。哪里的错误?

ios swift core-graphics
2个回答
1
投票

第一个问题是,在你的Android设置的转换父布局,进而影响孩子。但是在iOS上,你必须设置的转换上imageView本身。这是下面两种解决方案常见。

解决方法1:更新锚点

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    imageView.translatesAutoresizingMaskIntoConstraints = true
    imageView.bounds = parentView.bounds
    imageView.layer.anchorPoint = .zero
    imageView.center = .zero

    let width = parentView.frame.width
    let height = parentView.frame.height

    let a: [CGFloat] = [0.35355338, -0.35355338, 0.44107446, 0.35355338, 0.35355338, 0.058058262]

    let tx = a[2] * width
    let ty = a[5] * height
    let transform = CGAffineTransform(a: a[0], b: a[3], c: a[1], d: a[4], tx: tx, ty: ty)

    imageView.transform = transform
}

重要提示:我已删除了所有imageView从约束故事板

我们要查看anchorPoint移动到图像相匹配的Android的左上角。然而,非显而易见的问题是,这也将影响视图的位置,因为视图位置相对于定位点来计算。带约束它开始被混淆,因此我除去所有约束并切换至手动定位:

imageView.bounds = parentView.bounds // set correct size
imageView.layer.anchorPoint = .zero // anchor point to top-left corner
imageView.center = .zero // set position, "center" is now the top-left corner

解决方案2:更新变换矩阵

如果我们不想碰锚点,我们可以保持约束像他们,我们可以只更新改造:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    let width = parentView.frame.width
    let height = parentView.frame.height

    let a: [CGFloat] = [0.35355338, -0.35355338, 0.44107446, 0.35355338, 0.35355338, 0.058058262]

    let tx = a[2] * width
    let ty = a[5] * height
    let savedTransform = CGAffineTransform(a: a[0], b: a[3], c: a[1], d: a[4], tx: tx, ty: ty)

    let transform = CGAffineTransform.identity
        .translatedBy(x: width / 2, y: height / 2)
        .concatenating(savedTransform)
        .concatenating(CGAffineTransform(translationX: -width / 2, y: -height / 2))

    imageView.transform = transform
}

CGAffineTransform非明显的问题是,

.concatenating(CGAffineTransform(translationX: -width / 2, y: -height / 2))

.translatedBy(x: -width / 2, y: -height / 2)

是不一样的。乘法顺序是不同的。

iOS

在倒车过程中:

为了产生用于Android的同一矩阵中,我们只需要移动原点:

let savedTransform = CGAffineTransform.identity
    .translatedBy(x: width / 2, y: height / 2)
    .scaledBy(x: 0.5, y: 0.5)
    .rotated(by: .pi / 4)
    .translatedBy(x: -width / 2, y: -height / 2)

或使用串联:

let savedTransform = CGAffineTransform.identity
    .concatenating(CGAffineTransform(translationX: -width / 2, y: -height / 2))
    .concatenating(
        CGAffineTransform.identity
            .scaledBy(x: 0.5, y: 0.5)
            .rotated(by: .pi / 4)
    )
    .concatenating(CGAffineTransform(translationX: width / 2, y: height / 2))

随着级联现在是明显的起源转变如何取消出。

摘要:

func androidValuesToIOSTransform() -> CGAffineTransform{
    let width = parentView.frame.width
    let height = parentView.frame.height

    let androidValues: [CGFloat] = [0.35355338, -0.35355338, 0.44107446, 0.35355338, 0.35355338, 0.058058262]
    let androidTransform = CGAffineTransform(
        a: androidValues[0], b: androidValues[3],
        c: androidValues[1], d: androidValues[4],
        tx: androidValues[2] * width, ty: androidValues[5] * height
    )

    let iOSTransform = CGAffineTransform.identity
        .concatenating(CGAffineTransform(translationX: width / 2, y: height / 2))
        .concatenating(androidTransform)
        .concatenating(CGAffineTransform(translationX: -width / 2, y: -height / 2))

    return iOSTransform
}

func iOSTransformToAndroidValues() -> [CGFloat] {
    let width = parentView.frame.width
    let height = parentView.frame.height

    let iOSTransform = CGAffineTransform.identity
        .scaledBy(x: 0.5, y: 0.5)
        .rotated(by: .pi / 4)
    let androidTransform = CGAffineTransform.identity
        .concatenating(CGAffineTransform(translationX: -width / 2, y: -height / 2))
        .concatenating(iOSTransform)
        .concatenating(CGAffineTransform(translationX: width / 2, y: height / 2))

    let androidValues = [
        androidTransform.a, androidTransform.c, androidTransform.tx / width,
        androidTransform.b, androidTransform.d, androidTransform.ty / height
    ]
    return androidValues
}

0
投票

这个怎么样回答:我添加四个滑块来让事情变得容易看到。

class ViewController: UIViewController {

@IBOutlet weak var parentView: UIView!
@IBOutlet weak var imageView: UIImageView!

override func viewDidLoad() {
    super.viewDidLoad()


}




let width =  UIScreen.main.bounds.width
let height = UIScreen.main.bounds.width * 2.0 / 3.0


var zoom: CGFloat = 1.0
var rotate: CGFloat = 0.0

 var locX: CGFloat = 0.0
 var locY: CGFloat = 0.0

@IBAction func rotate(_ sender: UISlider){  //0-6.28

    rotate = CGFloat(sender.value)
    parentView.transform = CGAffineTransform(rotationAngle: rotate).concatenating(CGAffineTransform(scaleX: zoom, y: zoom))
    parentView.transform.tx = (locX-0.5) * width
    parentView.transform.ty = (locY-0.5) * height
}

@IBAction func zoom(_ sender: UISlider){ //0.1 - 10

    zoom = CGFloat(sender.value)
    parentView.transform = CGAffineTransform(rotationAngle: rotate).concatenating(CGAffineTransform(scaleX: zoom, y: zoom))
    parentView.transform.tx = (locX-0.5) * width
    parentView.transform.ty = (locY-0.5) * height
}

@IBAction func locationX(_ sender: UISlider){  //0 - 1

    locX = CGFloat(sender.value)
    parentView.transform = CGAffineTransform(rotationAngle: rotate).concatenating(CGAffineTransform(scaleX: zoom, y: zoom))
    parentView.transform.tx = (locX-0.5) * width
    parentView.transform.ty = (locY-0.5) * height
}

@IBAction func locationY(_ sender: UISlider){ //0 - 1
    locY = CGFloat(sender.value)
    parentView.transform = CGAffineTransform(rotationAngle: rotate).concatenating(CGAffineTransform(scaleX: zoom, y: zoom))
    parentView.transform.tx = (locX-0.5) * width
    parentView.transform.ty = (locY-0.5) * height
}



override func viewWillAppear(_ animated: Bool) {
  let a: [CGFloat] = [0.35355338, -0.35355338, 0.44107446, 0.35355338, 0.35355338, 0.058058262]
    let tx = (a[2] - 0.5) * width
   let ty = (a[5] - 0.5) * height
let transform =  CGAffineTransform(a: a[0], b: a[3], c: a[1], d: a[4], tx:tx, ty: ty)
parentView.layer.anchorPoint   = CGPoint.zero
parentView.layer.position = CGPoint.zero
parentView.transform = transform

}

}

© www.soinside.com 2019 - 2024. All rights reserved.