无法正确地为父视图中的子视图的框架设置动画
Unable to properly animate the frame of a subview within a parent view
我正在尝试为视图中子视图的帧更改设置动画。在动画块中,我将子视图的大小设置为其原始宽度和高度的一半。但是,当我 运行 代码时,子视图意外地以更大的尺寸开始,并 sh运行k 到其原始尺寸而不是成为其原始尺寸的一半。我正在使用下面的代码:
我的自定义视图:
import UIKit
class TestView: UIView {
var testSubview: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.yellow
testSubview = UIView(frame: .zero)
testSubview.backgroundColor = UIColor.red
addSubview(testSubview)
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
override func layoutSubviews() {
super.layoutSubviews()
testSubview.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
}
func testAnimate() {
UIView.animate(withDuration: 3, delay: 0, options: [], animations: { [unowned self] in
self.testSubview.bounds.size = CGSize(width: 50, height: 50)
}, completion: nil)
}
}
我的视图控制器:
import UIKit
class TestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let testView = TestView()
view.addSubview(testView)
testView.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
testView.center = view.center
testView.layoutIfNeeded()
testView.testAnimate()
}
}
我做错了什么吗?
问题是您的视图布局了两次,您可以通过在 layoutSubviews() 中打印调试消息来检查。
我建议这样,设置一个大小变量并在 layoutSubviews() 和 testAnimate() 中使用它:
class TestView: UIView {
var testSubview: UIView!
var mySize = CGSize(width: 100, height: 100)
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.yellow
testSubview = UIView(frame: .zero)
testSubview.backgroundColor = UIColor.red
addSubview(testSubview)
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
override func layoutSubviews() {
super.layoutSubviews()
testSubview.frame = CGRect(x: 0, y: 0, width: mySize.width, height: mySize.height)
}
func testAnimate() {
UIView.animate(withDuration: 3, delay: 0, options: [], animations: { [unowned self] in
self.mySize = CGSize(width: 50, height: 50)
self.testSubview.bounds.size = self.mySize
}, completion: nil)
}
}
编辑:重新制定我的整个答案,因为我觉得这里有很多地方应该改变。
首先是设置视图边界和它的框架之间的区别。
let view = UIView()
view.frame = CGRect(x: 0, y: 0, width: 200, height: 200) // This will set the position and size this view will take relative to it's future parents view
view.bounds = CGRect(x: 50, y: 50, width: 100, height: 100) // This will set position and size of it's own content (i.e. it's background) relative to it's own view
然后是您的 testView class 变量。除非您确定变量不会为零,否则不要使用 UIView!
作为类型,否则您最终会丢失一些初始化程序,因此您的 class 将无法始终如一地工作。
将 init 方法视为变量的默认值,之后您可以简单地更改它们。
import UIKit
class TestView: UIView {
var testSubview: UIView
override init(frame: CGRect) {
// Initialise your variables before calling super
testSubview = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
testSubview.backgroundColor = UIColor.red
super.init(frame: frame)
// Once super is called you can start using class methods or variable such as addSubview or backgroundColor
backgroundColor = UIColor.yellow
addSubview(testSubview)
}
required init?(coder aDecoder: NSCoder) {
testSubview = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
testSubview.backgroundColor = UIColor.blue
super.init(coder: aDecoder)
backgroundColor = UIColor.yellow
addSubview(testSubview)
}
}
现在终于到了动画部分
import UIKit
class TestView: UIView {
var testSubview: UIView
func testAnimation() {
UIView.animate(withDuration: 3, animations: {
self.testSubview.transform = CGAffineTransform(scaleX: 0.5, y: 0.5) // Simply make the view half its size over 3 seconds
}) { (_) in
UIView.animate(withDuration: 3, animations: {
self.testSubview.transform = CGAffineTransform(scaleX: 1, y: 1) // Upon completion, resize the view back to it's original size
})
}
}
}
或者如果你真的想使用 CGRect 在它的位置上做动画
import UIKit
class TestView: UIView {
var testSubview: UIView
func testAnimation() {
UIView.animate(withDuration: 3, animations: {
self.testSubview.frame = CGRect(x: 0, y: 0, width: 50, height: 50) // use frame not bounds to resize and reposition the frame
}) { (_) in
UIView.animate(withDuration: 3, animations: {
self.testSubview.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
})
}
}
}
你可以这样测试改变bounds属性的效果
import UIKit
class ViewController: UIViewController {
var testView: TestView!
override func viewDidLoad() {
testView = TestView(frame: CGRect(origin: .zero, size: CGSize(width: 200, height: 200)))
testView.bounds = CGRect(x: 50, y: 50, width: 100, height: 100)
testView.clipsToBounds = true
view.addSubview(testView)
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
testView.testAnimation()
}
}
只需玩转 clipsToBounds
、testView.bounds
、testView.subView.frame
和 testView = TestView(frame: CGRect(x:y:width:height)
即可查看所有这些
我正在尝试为视图中子视图的帧更改设置动画。在动画块中,我将子视图的大小设置为其原始宽度和高度的一半。但是,当我 运行 代码时,子视图意外地以更大的尺寸开始,并 sh运行k 到其原始尺寸而不是成为其原始尺寸的一半。我正在使用下面的代码:
我的自定义视图:
import UIKit
class TestView: UIView {
var testSubview: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.yellow
testSubview = UIView(frame: .zero)
testSubview.backgroundColor = UIColor.red
addSubview(testSubview)
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
override func layoutSubviews() {
super.layoutSubviews()
testSubview.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
}
func testAnimate() {
UIView.animate(withDuration: 3, delay: 0, options: [], animations: { [unowned self] in
self.testSubview.bounds.size = CGSize(width: 50, height: 50)
}, completion: nil)
}
}
我的视图控制器:
import UIKit
class TestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let testView = TestView()
view.addSubview(testView)
testView.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
testView.center = view.center
testView.layoutIfNeeded()
testView.testAnimate()
}
}
我做错了什么吗?
问题是您的视图布局了两次,您可以通过在 layoutSubviews() 中打印调试消息来检查。
我建议这样,设置一个大小变量并在 layoutSubviews() 和 testAnimate() 中使用它:
class TestView: UIView {
var testSubview: UIView!
var mySize = CGSize(width: 100, height: 100)
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.yellow
testSubview = UIView(frame: .zero)
testSubview.backgroundColor = UIColor.red
addSubview(testSubview)
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
override func layoutSubviews() {
super.layoutSubviews()
testSubview.frame = CGRect(x: 0, y: 0, width: mySize.width, height: mySize.height)
}
func testAnimate() {
UIView.animate(withDuration: 3, delay: 0, options: [], animations: { [unowned self] in
self.mySize = CGSize(width: 50, height: 50)
self.testSubview.bounds.size = self.mySize
}, completion: nil)
}
}
编辑:重新制定我的整个答案,因为我觉得这里有很多地方应该改变。
首先是设置视图边界和它的框架之间的区别。
let view = UIView()
view.frame = CGRect(x: 0, y: 0, width: 200, height: 200) // This will set the position and size this view will take relative to it's future parents view
view.bounds = CGRect(x: 50, y: 50, width: 100, height: 100) // This will set position and size of it's own content (i.e. it's background) relative to it's own view
然后是您的 testView class 变量。除非您确定变量不会为零,否则不要使用 UIView!
作为类型,否则您最终会丢失一些初始化程序,因此您的 class 将无法始终如一地工作。
将 init 方法视为变量的默认值,之后您可以简单地更改它们。
import UIKit
class TestView: UIView {
var testSubview: UIView
override init(frame: CGRect) {
// Initialise your variables before calling super
testSubview = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
testSubview.backgroundColor = UIColor.red
super.init(frame: frame)
// Once super is called you can start using class methods or variable such as addSubview or backgroundColor
backgroundColor = UIColor.yellow
addSubview(testSubview)
}
required init?(coder aDecoder: NSCoder) {
testSubview = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
testSubview.backgroundColor = UIColor.blue
super.init(coder: aDecoder)
backgroundColor = UIColor.yellow
addSubview(testSubview)
}
}
现在终于到了动画部分
import UIKit
class TestView: UIView {
var testSubview: UIView
func testAnimation() {
UIView.animate(withDuration: 3, animations: {
self.testSubview.transform = CGAffineTransform(scaleX: 0.5, y: 0.5) // Simply make the view half its size over 3 seconds
}) { (_) in
UIView.animate(withDuration: 3, animations: {
self.testSubview.transform = CGAffineTransform(scaleX: 1, y: 1) // Upon completion, resize the view back to it's original size
})
}
}
}
或者如果你真的想使用 CGRect 在它的位置上做动画
import UIKit
class TestView: UIView {
var testSubview: UIView
func testAnimation() {
UIView.animate(withDuration: 3, animations: {
self.testSubview.frame = CGRect(x: 0, y: 0, width: 50, height: 50) // use frame not bounds to resize and reposition the frame
}) { (_) in
UIView.animate(withDuration: 3, animations: {
self.testSubview.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
})
}
}
}
你可以这样测试改变bounds属性的效果
import UIKit
class ViewController: UIViewController {
var testView: TestView!
override func viewDidLoad() {
testView = TestView(frame: CGRect(origin: .zero, size: CGSize(width: 200, height: 200)))
testView.bounds = CGRect(x: 50, y: 50, width: 100, height: 100)
testView.clipsToBounds = true
view.addSubview(testView)
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
testView.testAnimation()
}
}
只需玩转 clipsToBounds
、testView.bounds
、testView.subView.frame
和 testView = TestView(frame: CGRect(x:y:width:height)
即可查看所有这些