DBD*_*DBD 934 animation objective-c ios autolayout ios6
我正在更新旧的应用程序,AdBannerView
当没有广告时,它会从屏幕上滑落.当有广告时,它会在屏幕上滑动.基本的东西.
旧样式,我在动画块中设置框架.新风格,我有一个IBOutlet
确定Y位置的约束,在这种情况下,它与superview底部的距离,并修改常量.
- (void)moveBannerOffScreen {
[UIView animateWithDuration:5 animations:^{
_addBannerDistanceFromBottomConstraint.constant = -32;
}];
bannerIsVisible = FALSE;
}
- (void)moveBannerOnScreen {
[UIView animateWithDuration:5 animations:^{
_addBannerDistanceFromBottomConstraint.constant = 0;
}];
bannerIsVisible = TRUE;
}
Run Code Online (Sandbox Code Playgroud)
横幅移动,完全符合预期,但没有动画.
更新:我重新观看了WWDC12视频" 掌握自动布局的最佳实践 ",其中包括动画.它讨论了如何使用更新约束AdBannerView
.
我尝试使用以下代码,但得到完全相同的结果.
- (void)moveBannerOffScreen {
_addBannerDistanceFromBottomConstraint.constant = -32;
[UIView animateWithDuration:2 animations:^{
[self.view setNeedsLayout];
}];
bannerIsVisible = FALSE;
}
- (void)moveBannerOnScreen {
_addBannerDistanceFromBottomConstraint.constant = 0;
[UIView animateWithDuration:2 animations:^{
[self.view setNeedsLayout];
}];
bannerIsVisible = TRUE;
}
Run Code Online (Sandbox Code Playgroud)
在旁注中,我已经多次检查过,这是在主线程上执行的.
g3r*_*rv4 1652
两个重要说明:
你需要layoutIfNeeded
在动画块中调用.Apple实际上建议您在动画块之前调用一次,以确保所有挂起的布局操作都已完成
您需要在父视图(例如self.view
)上专门调用它,而不是具有附加约束的子视图.这样做会更新所有受约束的视图,包括动画可能被约束到您更改约束的视图的其他视图(例如,视图B附加到视图A的底部,您刚刚更改了视图A的顶部偏移,您希望视图B用它来制作动画)
试试这个:
Objective-C的
- (void)moveBannerOffScreen {
[self.view layoutIfNeeded];
[UIView animateWithDuration:5
animations:^{
self._addBannerDistanceFromBottomConstraint.constant = -32;
[self.view layoutIfNeeded]; // Called on parent view
}];
bannerIsVisible = FALSE;
}
- (void)moveBannerOnScreen {
[self.view layoutIfNeeded];
[UIView animateWithDuration:5
animations:^{
self._addBannerDistanceFromBottomConstraint.constant = 0;
[self.view layoutIfNeeded]; // Called on parent view
}];
bannerIsVisible = TRUE;
}
Run Code Online (Sandbox Code Playgroud)
斯威夫特3
UIView.animate(withDuration: 5) {
self._addBannerDistanceFromBottomConstraint.constant = 0
self.view.layoutIfNeeded()
}
Run Code Online (Sandbox Code Playgroud)
Cam*_*mer 107
我很欣赏所提供的答案,但我认为进一步采取它会很好.
[containerView layoutIfNeeded]; // Ensures that all pending layout operations have been completed
[UIView animateWithDuration:1.0 animations:^{
// Make all constraint changes here
[containerView layoutIfNeeded]; // Forces the layout of the subtree animation block and then captures all of the frame changes
}];
Run Code Online (Sandbox Code Playgroud)
但实际上这是一个非常简单的场景.如果我想通过该updateConstraints
方法为子视图约束设置动画 怎么办?
[self.view layoutIfNeeded];
[self.subView setNeedsUpdateConstraints];
[self.subView updateConstraintsIfNeeded];
[UIView animateWithDuration:1.0f delay:0.0f options:UIViewAnimationOptionLayoutSubviews animations:^{
[self.view layoutIfNeeded];
} completion:nil];
Run Code Online (Sandbox Code Playgroud)
updateConstraints方法在UIView子类中被重写,并且必须在方法结束时调用super.
- (void)updateConstraints
{
// Update some constraints
[super updateConstraints];
}
Run Code Online (Sandbox Code Playgroud)
AutoLayout指南还有很多不足之处,但值得一读.我自己正在使用它作为UISwitch
一个用一对UITextField
简单而微妙的崩溃动画(0.2秒长)切换子视图的一部分.如上所述,在UIView子类updateConstraints方法中处理子视图的约束.
Ste*_*ing 71
通常,您只需更新约束并layoutIfNeeded
在动画块内调用.这可以是更改a的.constant
属性NSLayoutConstraint
,添加删除约束(iOS 7),或更改.active
约束的属性(iOS 8和9).
[UIView animateWithDuration:0.3 animations:^{
// Move to right
self.leadingConstraint.active = false;
self.trailingConstraint.active = true;
// Move to bottom
self.topConstraint.active = false;
self.bottomConstraint.active = true;
// Make the animation happen
[self.view setNeedsLayout];
[self.view layoutIfNeeded];
}];
Run Code Online (Sandbox Code Playgroud)
关于是否应该在动画块之前或其内部更改约束存在一些问题(请参阅前面的答案).
以下是教授iOS的Martin Pilkington和编写Auto Layout的Ken Ferry之间的Twitter对话.Ken解释说,尽管在动画块之外更改常量当前可能有效,但它并不安全,它们应该在动画块内部进行更改. https://twitter.com/kongtomorrow/status/440627401018466305
这是一个简单的项目,展示了如何动画视图.它使用Objective C并通过更改.active
几个约束的属性来动画化视图.
https://github.com/shepting/SampleAutoLayoutAnimation
Joh*_*rck 35
// Step 1, update your constraint
self.myOutletToConstraint.constant = 50; // New height (for example)
// Step 2, trigger animation
[UIView animateWithDuration:2.0 animations:^{
// Step 3, call layoutIfNeeded on your animated view's parent
[self.view layoutIfNeeded];
}];
Run Code Online (Sandbox Code Playgroud)
Mil*_*sáľ 24
Swift 4解决方案
三个简单的步骤:
更改约束,例如:
heightAnchor.constant = 50
Run Code Online (Sandbox Code Playgroud)告诉包含view
它的布局是脏的,并且autolayout应该重新计算布局:
self.view.setNeedsLayout()
Run Code Online (Sandbox Code Playgroud)在动画块中告诉布局重新计算布局,这相当于直接设置帧(在这种情况下,autolayout将设置帧):
UIView.animate(withDuration: 0.5) {
self.view.layoutIfNeeded()
}
Run Code Online (Sandbox Code Playgroud)完整最简单的例子:
heightAnchor.constant = 50
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.5) {
self.view.layoutIfNeeded()
}
Run Code Online (Sandbox Code Playgroud)
边注
有一个可选的第0步 - 在更改您可能想要调用的约束之前self.view.layoutIfNeeded()
,确保动画的起点来自应用了旧约束的状态(如果有一些其他约束更改,则不应包含在动画中):
otherConstraint.constant = 30
// this will make sure that otherConstraint won't be animated but will take effect immediately
self.view.layoutIfNeeded()
heightAnchor.constant = 50
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.5) {
self.view.layoutIfNeeded()
}
Run Code Online (Sandbox Code Playgroud)
从iOS 10开始,我们得到了一种新的动画机制 - UIViewPropertyAnimator
我们应该知道基本相同的机制适用于它.步骤基本相同:
heightAnchor.constant = 50
self.view.setNeedsLayout()
let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: UICubicTimingParameters(animationCurve: .linear))
animator.addAnimations {
self.view.layoutIfNeeded()
}
animator.startAnimation()
Run Code Online (Sandbox Code Playgroud)
由于animator
是动画的封装,我们可以继续引用它并稍后调用它.但是,因为在动画块中我们只是告诉autolayout重新计算帧,我们必须在调用之前更改约束startAnimation
.因此这样的事情是可能的:
// prepare the animator first and keep a reference to it
let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: UICubicTimingParameters(animationCurve: .linear))
animator.addAnimations {
self.view.layoutIfNeeded()
}
// at some other point in time we change the constraints and call the animator
heightAnchor.constant = 50
self.view.setNeedsLayout()
animator.startAnimation()
Run Code Online (Sandbox Code Playgroud)
更改约束和启动动画师的顺序非常重要 - 如果我们只是更改约束并让我们的动画师保留稍后的点,则下一个重绘周期可以调用自动布局重新计算,并且不会对动画进行动画处理.
此外,请记住,单个动画师是不可重复使用的 - 一旦你运行它,你就不能"重新运行"它.所以我想除非我们用它来控制交互动画,否则没有什么理由让动画师保持不变.
Tom*_* C. 14
其他答案都很好,但是这个答案突出了一些使用最近的例子制作动画约束的相当重要的问题.在我意识到以下情况之前,我经历了很多变化:
将要定位的约束设置为类变量以保存强引用.在Swift中我使用了懒惰变量:
lazy var centerYInflection:NSLayoutConstraint = {
let temp = self.view.constraints.filter({ $0.firstItem is MNGStarRating }).filter ( { $0.secondItem is UIWebView }).filter({ $0.firstAttribute == .CenterY }).first
return temp!
}()
Run Code Online (Sandbox Code Playgroud)
经过一些实验,我注意到必须从视图ABOVE(也就是超视图)获得约束定义的两个视图的约束.在下面的示例中(MNGStarRating和UIWebView都是我在其中创建约束的两种类型的项目,它们是self.view中的子视图).
过滤链接
我利用Swift的滤波器方法来分离将作为拐点的所需约束.人们也可能会变得更加复杂,但过滤器在这里做得很好.
使用Swift动画约束
Nota Bene - 这个例子是故事板/代码解决方案,并假设一个人在故事板中制定了默认约束.然后,可以使用代码为更改设置动画.
假设您创建一个属性以使用准确的条件进行过滤并获取动画的特定拐点(当然,如果需要多个约束,您还可以过滤数组并循环):
lazy var centerYInflection:NSLayoutConstraint = {
let temp = self.view.constraints.filter({ $0.firstItem is MNGStarRating }).filter ( { $0.secondItem is UIWebView }).filter({ $0.firstAttribute == .CenterY }).first
return temp!
}()
Run Code Online (Sandbox Code Playgroud)
....
一段时间以后...
@IBAction func toggleRatingView (sender:AnyObject){
let aPointAboveScene = -(max(UIScreen.mainScreen().bounds.width,UIScreen.mainScreen().bounds.height) * 2.0)
self.view.layoutIfNeeded()
//Use any animation you want, I like the bounce in springVelocity...
UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.75, options: [.CurveEaseOut], animations: { () -> Void in
//I use the frames to determine if the view is on-screen
if CGRectContainsRect(self.view.frame, self.ratingView.frame) {
//in frame ~ animate away
//I play a sound to give the animation some life
self.centerYInflection.constant = aPointAboveScene
self.centerYInflection.priority = UILayoutPriority(950)
} else {
//I play a different sound just to keep the user engaged
//out of frame ~ animate into scene
self.centerYInflection.constant = 0
self.centerYInflection.priority = UILayoutPriority(950)
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
}) { (success) -> Void in
//do something else
}
}
}
Run Code Online (Sandbox Code Playgroud)
这些笔记实际上是我为自己写的一组提示.我亲自和痛苦地做了所有的不该做的事情.希望本指南可以让其他人放心.
注意zPositioning.有时,当没有任何事情发生时,您应该隐藏其他一些视图或使用视图调试器来定位您的动画视图.我甚至发现了在故事板的xml中丢失了用户定义的运行时属性并导致动画视图被覆盖(工作时)的情况.
总是花点时间阅读文档(新旧),快速帮助和标题.Apple不断进行大量更改以更好地管理AutoLayout约束(请参阅堆栈视图).或者至少是AutoLayout Cookbook.请记住,有时最好的解决方案是在较旧的文档/视频中.
使用动画中的值并考虑使用其他animateWithDuration变体.
不要将特定布局值硬编码为确定其他常量更改的条件,而是使用允许您确定视图位置的值.CGRectContainsRect
就是一个例子
let viewMargins = self.webview.layoutMarginsGuide
:例如使用Storyboard时避免快速解决方案样本
private var _nc:[NSLayoutConstraint] = []
lazy var newConstraints:[NSLayoutConstraint] = {
if !(self._nc.isEmpty) {
return self._nc
}
let viewMargins = self.webview.layoutMarginsGuide
let minimumScreenWidth = min(UIScreen.mainScreen().bounds.width,UIScreen.mainScreen().bounds.height)
let centerY = self.ratingView.centerYAnchor.constraintEqualToAnchor(self.webview.centerYAnchor)
centerY.constant = -1000.0
centerY.priority = (950)
let centerX = self.ratingView.centerXAnchor.constraintEqualToAnchor(self.webview.centerXAnchor)
centerX.priority = (950)
if let buttonConstraints = self.originalRatingViewConstraints?.filter({
($0.firstItem is UIButton || $0.secondItem is UIButton )
}) {
self._nc.appendContentsOf(buttonConstraints)
}
self._nc.append( centerY)
self._nc.append( centerX)
self._nc.append (self.ratingView.leadingAnchor.constraintEqualToAnchor(viewMargins.leadingAnchor, constant: 10.0))
self._nc.append (self.ratingView.trailingAnchor.constraintEqualToAnchor(viewMargins.trailingAnchor, constant: 10.0))
self._nc.append (self.ratingView.widthAnchor.constraintEqualToConstant((minimumScreenWidth - 20.0)))
self._nc.append (self.ratingView.heightAnchor.constraintEqualToConstant(200.0))
return self._nc
}()
Run Code Online (Sandbox Code Playgroud)
如果您忘记了其中一个提示或更简单的提示,例如添加layoutIfNeeded的位置,则很可能不会发生任何事情:在这种情况下,您可能会有一个半假的解决方案,如下所示:
注意 - 请花点时间阅读下面的AutoLayout部分和原始指南.有一种方法可以使用这些技术来补充动态动画师.
UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 1.0, options: [.CurveEaseOut], animations: { () -> Void in
//
if self.starTopInflectionPoint.constant < 0 {
//-3000
//offscreen
self.starTopInflectionPoint.constant = self.navigationController?.navigationBar.bounds.height ?? 0
self.changeConstraintPriority([self.starTopInflectionPoint], value: UILayoutPriority(950), forView: self.ratingView)
} else {
self.starTopInflectionPoint.constant = -3000
self.changeConstraintPriority([self.starTopInflectionPoint], value: UILayoutPriority(950), forView: self.ratingView)
}
}) { (success) -> Void in
//do something else
}
}
Run Code Online (Sandbox Code Playgroud)
AutoLayout指南中的代码段(请注意第二个代码段用于使用OS X).顺便说一句 - 就我所见,这已不再是现行指南. 优选技术继续发展.
动画自动布局所做的更改
如果您需要完全控制自动布局所做的动画更改,则必须以编程方式更改约束.iOS和OS X的基本概念相同,但存在一些细微差别.
在iOS应用中,您的代码看起来如下所示:
[containerView layoutIfNeeded]; // Ensures that all pending layout operations have been completed
[UIView animateWithDuration:1.0 animations:^{
// Make all constraint changes here
[containerView layoutIfNeeded]; // Forces the layout of the subtree animation block and then captures all of the frame changes
}];
Run Code Online (Sandbox Code Playgroud)
在OS X中,使用支持图层的动画时使用以下代码:
[containterView layoutSubtreeIfNeeded];
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[context setAllowsImplicitAnimation: YES];
// Make all constraint changes here
[containerView layoutSubtreeIfNeeded];
}];
Run Code Online (Sandbox Code Playgroud)
如果不使用图层支持的动画,则必须使用约束的动画制作器为常量设置动画:
[[constraint animator] setConstant:42];
Run Code Online (Sandbox Code Playgroud)
对于那些在视觉上学得更好的人来说,请查看Apple的早期视频.
通常在文档中有小笔记或代码片段可以带来更大的想法.例如,将自动布局约束附加到动态动画师是一个很大的想法.
祝你好运,愿力量与你同在.
Swift解决方案:
yourConstraint.constant = 50
UIView.animate(withDuration: 1.0, animations: {
yourView.layoutIfNeeded
})
Run Code Online (Sandbox Code Playgroud)
工作解决方案100% Swift 3.1
我已经阅读了所有答案,并希望共享我在所有应用程序中使用过的代码和行的层次结构以使它们正确地进行动画处理。此处的某些解决方案不起作用,您现在应该在速度较慢的设备(例如iPhone 5)上检查它们。
self.view.layoutIfNeeded() // Force lays of all subviews on root view
UIView.animate(withDuration: 0.5) { [weak self] in // allowing to ARC to deallocate it properly
self?.tbConstraint.constant = 158 // my constraint constant change
self?.view.layoutIfNeeded() // Force lays of all subviews on root view again.
}
Run Code Online (Sandbox Code Playgroud)
我试图对约束进行动画处理,但找到一个好的解释并不容易。
其他答案所说的完全正确:您需要致电[self.view layoutIfNeeded];
inside animateWithDuration: animations:
。然而,另一个重要的一点是为每个NSLayoutConstraint
想要设置动画的对象提供指针。
归档时间: |
|
查看次数: |
274823 次 |
最近记录: |