web-dev-qa-db-ja.com

nativeContentSizeの変更をアニメーション化する

半径が変化する円を描画するUIViewサブクラスがあります(ニースの弾力性のあるアニメーションを使用)。ビューは円のサイズを決定しています。

このUIViewサブクラスのフレームサイズを変更して、円の半径のアニメーションによる変更に一致させ、ビューに接続されているNSLayoutConstraintsを変更します(円のエッジに制約されているビューがサークルのサイズが変更されます)。

半径が変更されたときに-(CGSize)intrinsicContentSizeを実装してinvalidateIntrinsicContentSizeを呼び出すと、制約に更新が指示されることを理解していますが、intrinsicContentSizeへの変更をアニメーション化する方法がわかりません。

[UIView animateWith ...ブロック内からinvalidateIntrinsicContentSizeを呼び出すと、レイアウトが即座に更新されます。

これは可能ですか、回避策/より良いアプローチはありますか?

50
Loz

invalidateIntrinsicContentSizeは、アニメーションやlayoutIfNeededでうまく機能します。考慮すべき唯一のことは、固有のコンテンツサイズを変更すると、スーパービューのレイアウトが無効になることです。だからこれはうまくいくはずです:

[UIView animateWithDuration:0.2 animations:^{
    [self invalidateIntrinsicContentSize];
    [self.superview setNeedsLayout];
    [self.superview layoutIfNeeded];
}];
57
stigi

幅/高さの制約は役に立ちませんか?この制約の参照を維持し、...

_[NSLayoutConstraint constraintWithItem:view
                             attribute:NSLayoutAttributeWidth
                             relatedBy:NSLayoutRelationEqual
                                toItem:nil
                             attribute:NSLayoutAttributeNotAnAttribute
                            multiplier:1
                              constant:myViewInitialWidth];
_

... myViewのサイズ変更をアニメーション化する場合は、次のようにします...

_self.viewWidthConstraint.constant = 100; // new width
[UIView animateWithDuration:0.3 animations:^{ [view layoutIfNeeded]; }];
_

高さについても同じことを行います。

他の制約によって異なりますが、これらの2つの制約の優先順位を上げる必要があるかもしれません。

または、UIViewをサブクラス化し、- (void)invalidateIntrinsicContentSize:(BOOL)animatedを追加して、自分で偽造することもできます。 - (CGSize)intrinsicContentSizeから新しいサイズを取得し、幅/高さの制約をアニメーション化してアニメーション化します。または、プロパティを追加してアニメーションを有効/無効にし、invalidateIntrinsicContentSizeをオーバーライドして、このメソッド内で実行します。たくさんの方法 ...

3
zrzka

Swift私のために働いた@stigiの回答のバージョン:

UIView.animate(withDuration: 0.2, animations: {
    self.invalidateIntrinsicContentSize()
    self.superview?.setNeedsLayout()
    self.superview?.layoutIfNeeded()
})
2
nathangitter

以下のコードでは、組み込みクラスは、変数を変更するとサイズが変更されたクラスです。組み込みクラスをアニメーション化するには、以下のコードのみを使用してください。ビュー階層の上位にある他のオブジェクトに影響を与える場合は、self.intrinsicクラスをsetNeedsLayoutおよびlayoutIfNeededの最上位のビューに置き換えます。

[UIView animateWithDuration:.2 animations:^{
        self.intrinsicClass.numberOfWeeks=8;
        [self.intrinsicClass setNeedsLayout];
        [self.intrinsicClass layoutIfNeeded];
    }];
2
Ajaxharg

これのどれも私のために働いていません。 NSAttributedStringで設定しているUILabelがあります。テキストは複数行で、Wordの境界で折り返されます。したがって、高さは可変です。私はこれを試しました:

[UIView animateWithDuration:1.0f animations:^{
    self.label = newLabelText;
    [self.label invalidateIntrinsicContentSize];
    [self.view setNeedsLayout];
    [self.view layoutIfNeeded];
}];

そして、いくつかのバリエーション。何も動作しません。ラベルはすぐにサイズを変更し、新しい位置にスライドします。したがって、画面上のラベル位置のアニメーションが機能しています。しかし、ラベルのサイズ変更のアニメーションはそうではありません。

1
drekka

Swift 4/3の場合はこれでうまくいきます。これがベストプラクティスだと思います。UILabelが含まれているUIViewがあり、UIViewがUILabelのフレームを適応させる場合は、次を使用します。

self.theUILabel.text = "text update"
UIView.animate(withDuration: 5.0, animations: {
   self.theUIView.layoutIfNeeded()
})

通常のself.view.layoutIfNeeded()もほとんどの場合機能します。

0
J. Doe