Swift scale-transform 动画不小心平移了 UIView
Swift scale-transform animation accidentally translates UIView
真的很简单的问题。我正在使用 UIView 动画让我的按钮根据用户输入缩小和增大:
private func animateSelect() {
guard !isAnimating else {
return
}
alpha = 0.8
UIView.animate(withDuration: 0.1) {
self.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
}
isAnimating = true
}
private func animateDeselect() {
guard isAnimating else {
return
}
alpha = 1.0
UIView.animate(withDuration: 0.2) {
self.transform = .identity
}
isAnimating = false
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
animateSelect()
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
animateDeselect()
}
而且我认为我使用 isAnimating
变量安排了一切以避免布局或动画冲突 - 守卫 layoutSubviews
:
override func layoutSubviews() {
guard !isAnimating else {
return
}
...
但是按钮会随动画一起移动。这真的很烦人,破坏了平滑度,我有点困惑,因为从动画内部的变换中减去平移并没有解决它。有人有解决此问题的提示吗?
编辑:
仔细一看,好像根本不需要isAnimating
。像这样应该没问题:
class MyView: UIView {
private func animateSelect() {
alpha = 0.8
UIView.animate(withDuration: 0.1) {
self.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
}
}
private func animateDeselect() {
alpha = 1.0
UIView.animate(withDuration: 0.2) {
self.transform = .identity
}
}
}
除了,问题最有可能是self.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
。当转换不是 .identity
时,frame
会变得混乱。因此,例如,当您初始化 MyView
:
let myView = MyView()
myView.frame = CGRect(x: 50, y: 60, width: 100, height: 200)
...这里,myView.frame
可能不准确。
在 MyView
中,您应该创建一个新的子视图 contentView
。这将包含您要缩放的内容。像这样:
/// usage
let myView = MyView()
myView.frame = CGRect(x: 50, y: 200, width: 50, height: 100)
myView.contentView.backgroundColor = .red
view.addSubview(myView)
/// code
class MyView: UIView {
var contentView: UIView!
private func animateSelect() {
alpha = 0.8
UIView.animate(withDuration: 0.1) {
self.contentView?.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
}
}
private func animateDeselect() {
alpha = 1.0
UIView.animate(withDuration: 0.2) {
self.contentView?.transform = .identity
}
}
// MARK: Init methods
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
// MARK: Set up the contentView
private func commonInit() {
let contentView = UIView()
addSubview(contentView)
contentView.frame = self.bounds
contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.contentView = contentView
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
animateSelect()
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
animateDeselect()
}
}
结果:
旧答案:
动画完成后,使用完成处理程序重置 isAnimating
。此外,无需检查 guard isAnimating else { return }
...UIView 动画通常会自动为您解决。
private func animateSelect() {
alpha = 0.8
isAnimating = true /// put it in front so that it's easier to read
UIView.animate(withDuration: 0.1) {
self.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
} completion: { _ in /// set the completion handler
self.isAnimating = false
}
}
private func animateDeselect() {
alpha = 1.0
isAnimating = true
UIView.animate(withDuration: 0.2) {
self.transform = .identity
} completion: { _ in
self.isAnimating = false
}
}
真的很简单的问题。我正在使用 UIView 动画让我的按钮根据用户输入缩小和增大:
private func animateSelect() {
guard !isAnimating else {
return
}
alpha = 0.8
UIView.animate(withDuration: 0.1) {
self.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
}
isAnimating = true
}
private func animateDeselect() {
guard isAnimating else {
return
}
alpha = 1.0
UIView.animate(withDuration: 0.2) {
self.transform = .identity
}
isAnimating = false
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
animateSelect()
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
animateDeselect()
}
而且我认为我使用 isAnimating
变量安排了一切以避免布局或动画冲突 - 守卫 layoutSubviews
:
override func layoutSubviews() {
guard !isAnimating else {
return
}
...
但是按钮会随动画一起移动。这真的很烦人,破坏了平滑度,我有点困惑,因为从动画内部的变换中减去平移并没有解决它。有人有解决此问题的提示吗?
编辑:
仔细一看,好像根本不需要isAnimating
。像这样应该没问题:
class MyView: UIView {
private func animateSelect() {
alpha = 0.8
UIView.animate(withDuration: 0.1) {
self.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
}
}
private func animateDeselect() {
alpha = 1.0
UIView.animate(withDuration: 0.2) {
self.transform = .identity
}
}
}
除了,问题最有可能是self.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
。当转换不是 .identity
时,frame
会变得混乱。因此,例如,当您初始化 MyView
:
let myView = MyView()
myView.frame = CGRect(x: 50, y: 60, width: 100, height: 200)
...这里,myView.frame
可能不准确。
在 MyView
中,您应该创建一个新的子视图 contentView
。这将包含您要缩放的内容。像这样:
/// usage
let myView = MyView()
myView.frame = CGRect(x: 50, y: 200, width: 50, height: 100)
myView.contentView.backgroundColor = .red
view.addSubview(myView)
/// code
class MyView: UIView {
var contentView: UIView!
private func animateSelect() {
alpha = 0.8
UIView.animate(withDuration: 0.1) {
self.contentView?.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
}
}
private func animateDeselect() {
alpha = 1.0
UIView.animate(withDuration: 0.2) {
self.contentView?.transform = .identity
}
}
// MARK: Init methods
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
// MARK: Set up the contentView
private func commonInit() {
let contentView = UIView()
addSubview(contentView)
contentView.frame = self.bounds
contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.contentView = contentView
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
animateSelect()
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
animateDeselect()
}
}
结果:
旧答案:
动画完成后,使用完成处理程序重置 isAnimating
。此外,无需检查 guard isAnimating else { return }
...UIView 动画通常会自动为您解决。
private func animateSelect() {
alpha = 0.8
isAnimating = true /// put it in front so that it's easier to read
UIView.animate(withDuration: 0.1) {
self.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
} completion: { _ in /// set the completion handler
self.isAnimating = false
}
}
private func animateDeselect() {
alpha = 1.0
isAnimating = true
UIView.animate(withDuration: 0.2) {
self.transform = .identity
} completion: { _ in
self.isAnimating = false
}
}