古いアプリをAdBannerView
で更新していますが、広告が表示されていない場合は画面からスライドします。広告があるとそれは画面上でスライドします。基本的なもの.
古いスタイルで、アニメーションブロックにフレームを設定しました。新しいスタイル、私はY位置を決定する自動レイアウト制約へのIBOutlet
を持っています、この場合それはスーパービューの底からの距離です、そして定数を修正します:
- (void)moveBannerOffScreen {
[UIView animateWithDuration:5 animations:^{
_addBannerDistanceFromBottomConstraint.constant = -32;
}];
bannerIsVisible = FALSE;
}
- (void)moveBannerOnScreen {
[UIView animateWithDuration:5 animations:^{
_addBannerDistanceFromBottomConstraint.constant = 0;
}];
bannerIsVisible = TRUE;
}
そして、バナーは予想通りに動きますが、no animationです。
更新: 私は再視聴しました WWDC 12トーク自動レイアウトをマスターするためのベストプラクティス アニメーションをカバーしています。 CoreAnimation を使用して制約を更新する方法について説明します。
私は以下のコードを試しましたが、まったく同じ結果が得られます。
- (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;
}
ちなみに、私は何度もチェックしてきましたが、これはmainスレッドで実行されています。
2つの重要な注意事項
アニメーションブロック内でlayoutIfNeeded
を呼び出す必要があります。実際には、保留中のすべてのレイアウト操作が完了していることを確認するために、アニメーションブロックの前に一度呼び出すことをお勧めします。
制約を付けた子ビューではなく、 親ビュー (self.view
など)で明示的に呼び出す必要があります。そうすることで、 all 制約ビューを更新します。これには、制約を変更したビューに制約されている可能性のある他のビューのアニメーション化も含まれます。オフセットして、ビュー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;
}
スイフト3
UIView.animate(withDuration: 5) {
self._addBannerDistanceFromBottomConstraint.constant = 0
self.view.layoutIfNeeded()
}
私は提供された答えに感謝します、しかし私はそれをもう少しそれを取ることはいいことだと思います。
[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
}];
しかし、これは非常に単純化したシナリオです。 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];
UpdateConstraintsメソッドはUIViewサブクラスでオーバーライドされ、メソッドの最後にsuperを呼び出す必要があります。
- (void)updateConstraints
{
// Update some constraints
[super updateConstraints];
}
AutoLayoutガイド は望ましいことをたくさん残しますが、読む価値があります。私自身、これをUISwitch
の一部として使用しています。これは、単純で微妙な折りたたみアニメーション(長さ0.2秒)を使って1組のUITextField
でサブビューを切り替えるものです。サブビューの制約は、上記のUIViewサブクラスのupdateConstraintsメソッドで処理されています。
通常、制約を更新し、アニメーションブロック内でlayoutIfNeeded
を呼び出すだけです。これは、NSLayoutConstraint
の.constant
プロパティの変更、削除制約の追加(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];
}];
制約を変更する必要があるかどうかbeforeアニメーションブロック、またはinsideit(前の回答を参照)。
以下は、iOSを教えるMartin PilkingtonとAuto Layoutを書いたKen Ferryとの間のTwitterの会話です。ケンは、アニメーションブロックの外で定数を変更すると現在動作するかもしれませんが、安全ではなく、実際に変更する必要があると説明していますinsideアニメーションブロック。 https://Twitter.com/kongtomorrow/status/440627401018466305
これは、ビューをアニメーション化する方法を示す簡単なプロジェクトです。 Objective Cを使用し、いくつかの制約の.active
プロパティを変更することでビューをアニメーション化します。 https://github.com/shepting/SampleAutoLayoutAnimation
// 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];
}];
Swift 4のソリューション
3つの簡単なステップ
制約を変更します。例:
heightAnchor.constant = 50
そのレイアウトが汚れていて、自動レイアウトがレイアウトを再計算する必要があることを含むview
に伝えます。
self.view.setNeedsLayout()
アニメーションブロックで、レイアウトを再計算するようにレイアウトに指示します。これは、フレームを直接設定するのと同じです(この場合、自動レイアウトはフレームを設定します)。
UIView.animate(withDuration: 0.5) {
self.view.layoutIfNeeded()
}
最も簡単な例:
heightAnchor.constant = 50
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.5) {
self.view.layoutIfNeeded()
}
サイドノート
オプションの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()
}
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()
animator
はアニメーションのカプセル化なので、それを参照し続けて後で呼び出すことができます。しかし、アニメーションブロックでは自動レイアウトにフレームを再計算するように指示しているだけなので、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()
制約を変更してアニメータを起動する順序は重要です。制約を変更してアニメータを後で使用する場合は、次の再描画サイクルで自動レイアウト再計算を呼び出すことができ、変更はアニメートされません。
また、1人のアニメーターは再利用できません - 一度実行すると「再実行」することはできません。それで、私たちがインタラクティブなアニメーションをコントロールするためにそれを使わない限り、私はアニメーターをあちこちに保つために本当に良い理由がないと思います。
他の答えは大丈夫ですが、これは最近の例を使用して制約をアニメートすることのいくつかのかなり重要な落とし穴を強調しています。次のことに気付く前に、さまざまなバリエーションを経験しました。
強力な参照を保持するために、ターゲットとする制約をクラス変数にします。 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!
}()
いくつかの実験の後、私はビューは(別名スーパービュー)から制約が定義されている2つのビューから制約を取得しなければなりません。私はその間に制約を作成しています、そしてそれらはself.view内のサブビューです。
フィルタチェーン
私はSwiftのフィルター法を利用して、変曲点として機能する目的の制約を分離します。もっと複雑になるかもしれませんが、filterはここではいい仕事をします。
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!
}()
…….
今度いつか...
@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
}
}
}
これらのメモは本当に私が自分自身のために書いたヒントのセットです。私は個人的にそして痛々しいことにすべての禁止事項をやりました。うまくいけば、このガイドは他の人を倹約することができます。
ZPositioningに気をつけてください。何も起きていないように見える場合は、他のビューを非表示にするか、ビューデバッガを使用してアニメーションビューを見つける必要があります。ストーリーボードのxmlでユーザー定義のランタイム属性が失われ、(動作中に)アニメーションビューが隠されることになったことさえあります。
ドキュメント(新旧)、クイックヘルプ、およびヘッダーを読むには、少々時間がかかります。 AppleはAutoLayout制約をよりよく管理するために多くの変更を続けています(スタックビューを参照)。または少なくとも AutoLayout Cookbook 。時には最善の解決策が古いドキュメント/ビデオにあることを覚えておいてください。
アニメーションの値をいろいろ試して、他のanimateWithDurationバリアントの使用を検討してください。
特定のレイアウト値を他の定数への変更を判断するための基準としてハードコードしないでください。代わりに、ビューの場所を判断できる値を使用してください。 CGRectContainsRect
はその一例です
let viewMargins = self.webview.layoutMarginsGuide
に参加しているビューに関連付けられているレイアウト余白を使用することを躊躇しないでください。ストーリーボード使用時のAVOIDに対する解決策のクイックサンプル
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
}()
これらのヒントの1つを忘れた場合、またはlayoutIfNeededを追加する場所などのより単純なヒントを忘れた場合は、ほとんど何も起こらないでしょう。この場合、次のようなハーフベイクの解決策があります。
注意 - 下のオートレイアウトセクションとオリジナルのガイドを読んでください。あなたのダイナミックアニメーターを補うためにこれらのテクニックを使う方法があります。
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
}
}
オートレイアウトガイドのスニペット(2番目のスニペットはOS Xを使用するためのものです)。 BTW - これは私が見ることができる限り現在のガイドにはもうありません。好ましいテクニックは進化し続けています。
自動レイアウトによる変更のアニメーション化
自動レイアウトによる変更のアニメーション化を完全に制御する必要がある場合は、プログラムで制約を変更する必要があります。基本的な概念は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
}];
OS Xでは、レイヤー付きアニメーションを使用するときは次のコードを使用します。
[containterView layoutSubtreeIfNeeded];
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[context setAllowsImplicitAnimation: YES];
// Make all constraint changes here
[containerView layoutSubtreeIfNeeded];
}];
レイヤバックアニメーションを使用していない場合は、コンストレインのアニメーターを使用して定数をアニメートする必要があります。
[[constraint animator] setConstant:42];
視覚的によく学ぶ人のために、この初期の{ Appleからのビデオ をチェックしてください。
ドキュメンテーションの中には、より大きなアイデアにつながる小さなメモやコードの断片があることがよくあります。たとえば、動的なアニメーターに自動レイアウト制約を付けることは大きなアイデアです。
幸運を祈り、力があなたと共にあるように。
迅速な解決策:
yourConstraint.constant = 50
UIView.animateWithDuration(1, animations: yourView.layoutIfNeeded)
ワーキングソリューション100% Swift 3.1
私はすべての答えを読み、それらを正しくアニメーション化するために私のすべてのアプリケーションで使用したコードと行の階層を共有したいと思います。ここでの解決策はうまくいかない。
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.
}
私は制約をアニメートしようとしていました、そして、良い説明を見つけることは本当に容易ではありませんでした。
他の答えが言っていることは全く本当です:あなたは[self.view layoutIfNeeded];
の中でanimateWithDuration: animations:
を呼び出す必要があります。ただし、もう1つの重要な点は、アニメーション化するすべてのNSLayoutConstraint
へのポインタを用意することです。
Xcode 8.3.3を使ったSwift 3のための実用的でテスト済みのソリューション:
self.view.layoutIfNeeded()
self.calendarViewHeight.constant = 56.0
UIView.animate(withDuration: 0.5, delay: 0.0, options: UIViewAnimationOptions.curveEaseIn, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
Self.calendarViewHeightはcustomView(CalendarView)と呼ばれる制約であることを忘れないでください。 self.calendarViewではなくself.viewで.layoutIfNeeded()を呼び出しました。
この助けを願っています。
これについて記事の話があります: http://weblog.invasivecode.com/post/42362079291/auto-layout-and-core-animation-auto-layout-was
それで、彼はこのようにコーディングしました:
- (void)handleTapFrom:(UIGestureRecognizer *)gesture {
if (_isVisible) {
_isVisible = NO;
self.topConstraint.constant = -44.; // 1
[self.navbar setNeedsUpdateConstraints]; // 2
[UIView animateWithDuration:.3 animations:^{
[self.navbar layoutIfNeeded]; // 3
}];
} else {
_isVisible = YES;
self.topConstraint.constant = 0.;
[self.navbar setNeedsUpdateConstraints];
[UIView animateWithDuration:.3 animations:^{
[self.navbar layoutIfNeeded];
}];
}
}
それが役に立てば幸い。
制約アニメーションのコンテキストでは、keyboard_opened通知内で制約をすぐにアニメーション化した特定の状況について説明します。
制約は、テキストフィールドからコンテナの上部までの上部スペースを定義しました。キーボードが開いたら、定数を2で割るだけです。
私はキーボード通知内で一貫した滑らかな拘束アニメーションを直接達成することができませんでした。アニメーション化せずに、ビューの約半分は新しい位置にジャンプします。
キーボードを開くと、レイアウトが追加される可能性があります。単純なdispatch_afterブロックを10ミリ秒の遅延で追加すると、アニメーションは毎回実行されます - ジャンプしません。