ストーリーボード、コード、ヒント、およびいくつかの注意点
他の答えは問題ありませんが、これは最近の例を使用して制約をアニメーション化することのいくつかのかなり重要な問題を強調しています。次のことに気付く前に、さまざまなバリエーションを試しました。
強力な参照を保持するために、対象とする制約をクラス変数に作成します。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!
}()
いくつかの実験の後、私は、1つのビューからの制約を取得しなければならないことに留意ABOVE(スーパー別名)制約が定義されている二つのビュー。以下の例では(MNGStarRatingとUIWebViewの両方が、私が間に制約を作成する2つのタイプのアイテムであり、それらは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!
}()
....
今度いつか...
@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でユーザー定義のランタイム属性が失われ、アニメーションビューが(作業中に)カバーされる場合さえありました。
ドキュメント(新旧)、クイックヘルプ、およびヘッダーを読むのに常に1分かかります。AppleはAutoLayout制約をより適切に管理するために多くの変更を続けています(スタックビューを参照)。または、少なくともAutoLayoutクックブック。時々、最良の解決策は古いドキュメント/ビデオにあることを覚えておいてください。
アニメーションの値をいじって、他のanimateWithDurationバリアントの使用を検討してください。
特定のレイアウト値を他の定数への変更を決定する基準としてハードコーディングしないでください。代わりに、ビューの場所を決定できる値を使用してください。CGRectContainsRect
一例です
- 必要に応じて、制約定義に参加しているビューに関連付けられているレイアウトマージンを使用することをためらわないでください
let viewMargins = self.webview.layoutMarginsGuide
:例にあります
- 必要のない作業は行わないでください。ストーリーボードに制約があるすべてのビューには、プロパティself.viewName.constraintsに制約がアタッチされています。
- 制約の優先度を1000未満に変更します。ストーリーボードで、鉱山を250(低)または750(高)に設定しました。(1000の優先度をコード内の何かに変更しようとすると、1000が必要なため、アプリがクラッシュします)
- activateConstraintsとdeactivateConstraintsをすぐに使用しようとしないことを検討してください(それらには場所がありますが、学習している場合、またはこれらを使用してストーリーボードを使用している場合は、多すぎることを意味します〜以下に示すように場所があります)
- コードに新しい制約を実際に追加するのでない限り、addConstraints / removeConstraintsを使用しないことを検討してください。ほとんどの場合、ストーリーボードのビューを目的の制約でレイアウトし(ビューを画面外に配置)、コードで、以前にストーリーボードで作成した制約をアニメーション化してビューを移動します。
- 新しいNSAnchorLayoutクラスとサブクラスを使用して制約を構築するのに多くの時間を費やしました。これらは問題なく機能しますが、必要なすべての制約がストーリーボードにすでに存在していることを理解するのにしばらく時間がかかりました。コードで制約を作成する場合、このメソッドを使用して制約を集計するのが最も確実です。
ストーリーボード使用時の回避策の簡単なサンプル
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を追加する場所などのより単純なヒントの1つを忘れた場合、ほとんど何も起こりません。その場合、次のような中途半端な解決策が考えられます。
注意-以下の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
}
}
AutoLayoutガイドのスニペット(2番目のスニペットは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
}];
OS Xでは、レイヤーベースのアニメーションを使用するときに次のコードを使用します。
[containterView layoutSubtreeIfNeeded];
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[context setAllowsImplicitAnimation: YES];
// Make all constraint changes here
[containerView layoutSubtreeIfNeeded];
}];
レイヤーベースのアニメーションを使用していない場合は、コンストレイントのアニメーターを使用して定数をアニメーション化する必要があります。
[[constraint animator] setConstant:42];
よりよく視覚的に学ぶ人のために、アップルからこの初期のビデオをチェックしてください。
注意を払う
ドキュメントには、大きなアイデアにつながる小さなメモやコードが含まれていることがよくあります。たとえば、自動レイアウト制約をダイナミックアニメーターにアタッチすることは、素晴らしいアイデアです。
幸運とフォースがあなたと共におられますように。