ios - 为什么 CGAffineTransform(scaleX:y:) 会导致我的视图像调用 CGAffineTransform(rotationAngle:) 一样旋转?
问题描述
我学习了 Paul Hudson 的(Hacking with Swift)动画教程(他的项目 15)并尝试扩展它以查看多个动画相互叠加的效果。
有四种不同的动画:缩放、平移、旋转和颜色。在他的教程中,我使用了四个按钮来允许单独选择每个动画,而不是像他的教程中那样循环遍历所有动画。我还通过反转原始动画而不是使用 CGAffineTransform.identity 来修改他的“撤消”动画,因为身份转换会撤消所有动画到该点。
我的问题是,当我单击我的 c 按钮时,它会在第一次单击时进行适当的缩放,但是,它不会在第二次单击时将企鹅缩放回其原始大小,而是旋转视图。随后单击缩放按钮继续旋转视图,就像我单击旋转按钮一样。
还有其他异常情况,例如第一次单击移动按钮会适当地移动企鹅,但随后单击旋转按钮会旋转企鹅并将其移回原始位置。我不确定它为什么会向后移动,但我接受这可能是我自己对动画系统的无知。
我添加了打印语句并放入断点来调试比例问题。代码中的所有内容似乎都完全按照编码工作,但动画不符合逻辑!任何帮助,将不胜感激。
完整的程序比较简单:
import UIKit
class ViewController: UIViewController {
var imageView: UIImageView!
var scaled = false
var moved = false
var rotated = false
var colored = false
@IBOutlet var scaleButton: UIButton!
@IBOutlet var moveButton: UIButton!
@IBOutlet var rotateButton: UIButton!
@IBOutlet var colorButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
imageView = UIImageView(image: UIImage(named: "penguin"))
imageView.center = CGPoint(x: 200, y: 332)
view.addSubview(imageView)
}
@IBAction func tapped(_ sender: UIButton) {
sender.isHidden = true
var theAnimation: ()->Void
switch sender {
case scaleButton:
theAnimation = scaleIt
case moveButton:
theAnimation = moveIt
case rotateButton:
theAnimation = rotateIt
case colorButton:
theAnimation = colorIt
default:
theAnimation = { self.imageView.transform = .identity }
}
print("scaled = \(scaled), moved = \(moved), rotated = \(rotated), colored = \(colored)")
UIView.animate(withDuration: 1, delay: 0, options: [], animations: theAnimation) { finished in
sender.isHidden = false
}
}
func scaleIt() {
print("scaleIt()")
let newScale: CGFloat = self.scaled ? -1.5 : 1.5
self.imageView.transform = CGAffineTransform(scaleX: newScale, y: newScale)
self.scaled.toggle()
}
func moveIt() {
print("moveIt()")
let newX: CGFloat = self.moved ? 0 : -50
let newY: CGFloat = self.moved ? 0 : -150
self.imageView.transform = CGAffineTransform(translationX: newX, y: newY)
self.moved.toggle()
}
func rotateIt() {
print("rotateIt()")
let newAngle = self.rotated ? 0.0 : CGFloat.pi
self.imageView.transform = CGAffineTransform(rotationAngle: newAngle)
self.rotated.toggle()
}
func colorIt() {
print("colorIt()")
let newAlpha: CGFloat = self.colored ? 1 : 0.1
let newColor = self.colored ? UIColor.clear : UIColor.green
self.imageView.alpha = newAlpha
self.imageView.backgroundColor = newColor
self.colored.toggle()
}
}
通过单击按钮的任意组合,我只能得到 Penguin 的 10 种配置(其中 5 种带有 CGAffineTransforms 和相同的 5 种由颜色按钮修改)。
我将使用这些图像来回答您的一些问题。我尝试将这些图像标记为“Configuration1A,Configuration2A,... Configuration1B”,其中 B 配置与 A 配置相同,但具有颜色按钮的效果。也许标签会出现在我的帖子中(我对在 StackOverflow 上发帖很陌生)。
以下是使用颜色按钮重复第一个配置的五种配置:
我首先尝试重复按下按钮。对于每一系列按下给定按钮,我首先将企鹅恢复到其原始位置、大小、角度和颜色。对于这些重复的按钮按下,我观察到的行为如下:
按钮观察到的行为
- scale Penguin 从 Configuration1A 缩放到 Configuration2A(如预期的那样)。
- scale Penguin 从 Configuration2A 旋转到 Configuration4A(什么???)。
- scale Penguin 以 pi 从 Configuration4A 旋转到 Configuration2A(什么???)。
规模......上面的第 2 步和第 3 步,无限重复(什么???)。
move Penguin 移动 (-50, -150),Configuration1A 到 Configuration3A(如预期的那样)。
move Penguin 将 (50, 150) Configuration3A 移动到 Configuration1A(如预期的那样)。
move ... 上述行为无限重复(如预期的那样)。
rotate Penguin 旋转 pi,Configuration1A 到 Configuration5A(如预期的那样)。
rotate Penguin 旋转 pi,Configuration5A 到 Configuration1A(如预期的那样)。
rotate ... 上述行为无限重复(如预期的那样)。
color Penguin 的颜色变化,Configuration1A 到 Configuration1B(如预期的那样)。
color Penguin 的颜色变化,Configuration1B 到 Configuration1A(如预期的那样)。
color ... 上述行为无限重复(如预期的那样)。
解决方案
比例 1.5(大 50%)的反面不是 -1.5,而是 0.5。(原尺寸的 50%)
实际上,您不希望它在 1.5(大 50%)和 1.0(正常大小)之间交替吗?
假设您确实想要在大 50% 和小 50% 之间交替,请将 scaleIt 函数更改为:
func scaleIt() {
print("scaleIt()")
let newScale: CGFloat = self.scaled ? -1.5 : 0.5
self.imageView.transform = CGAffineTransform(scaleX: newScale, y: newScale)
self.scaled.toggle()
}
当您将 X 或 Y 比例设置为负数时,它会反转该维度中的坐标。两个维度的反转看起来就像一个旋转。
正如其他人所提到的,您编写函数的方式将无法组合不同的转换。您可能需要重写代码以将更改应用于视图的现有转换:
func scaleIt() {
print("scaleIt()")
// Edited to correct the math if you are applying a scale to an existing transform
let scaleAdjustment: CGFloat = self.scaled ? -1.5 : 0.66666
self.imageView.transform = self.imageView.transform.scaledBy(x: scaleAdjustment, y: scaleAdjustment)
self.scaled.toggle()
}
编辑:
请注意,对变换的更改不是“可交换的”,这是一种奇特的说法,即应用它们的顺序很重要。例如,先应用移位,然后再旋转,将产生与应用旋转再旋转不同的结果。
编辑#2:
另一件事:
您编写函数的方式,它们会将视图设置为其当前状态,然后为下一次反转该当前状态(您scaled
在决定要做什么后切换。)
这意味着对于 moveIt() 之类的函数,在您第一次点击按钮时不会发生任何事情。像这样重写该函数:
func moveIt() {
print("I like to moveIt moveIt!")
self.moved.toggle() //Toggle the Bool first
let newX: CGFloat = self.moved ? 0 : -50
let newY: CGFloat = self.moved ? 0 : -150
self.imageView.transform = self.imageView.transform.translatedBy(x: newX, y: newY)
}
推荐阅读
- javascript - 如何将值从 HTML 传递到 JavaScript?
- python - 如何在系统日志消息中设置主机头字段?
- react-native - 如何更改键盘上的“返回”按钮?
- android - 尝试在空对象引用上调用虚拟方法“void com.google.android.gms.ads.AdView.loadAd(com.google.android.gms.ads.AdRequest)”
- node.js - mongoDB nodejs驱动程序中的唯一键
- delphi - {$SetPEFlags IMAGE_FILE_LARGE_ADDRESS_AWARE} 无法在我的应用程序中工作
- javascript - CSS 定位 2 个 div - 滚动到较短的一个的末尾
- ssl - grpc中不安全的连接是什么意思?
- ios - 为什么设置 backgroundColor 对我的自定义 UIView 没有明显影响?
- python - Tkinter 不更新游标