UIViewサイズ変更イベントはありますか?


102

画像ビューの行と列が含まれているビューがあります。

このビューのサイズが変更された場合、imageviewsの位置を再配置する必要があります。

このビューは、サイズ変更される別のビューのサブビューです。

このビューのサイズが変更されていることを検出する方法はありますか?


ビューのサイズはいつ変更されますか?デバイスが回転したとき、またはユーザーがマルチタッチを使用して回転したとき
Evan Mulawski、2010

ユーザーが別のビュー(マスタービュー)のボタンをタップすると、このビューのサイズが変更されます。
live-love

回答:


98

Uliが以下にコメントしたように、それを行う適切な方法はlayoutSubviews、そこにimageViewsをオーバーライドしてレイアウトすることです。

何らかの理由で、をサブクラス化してオーバーライドできない場合は、たとえダーティであってもlayoutSubviews、監視boundsは機能するはずです。さらに悪いことに、監視にはリスクがあります-Appleは、KVOがUIKitクラスで動作することを保証していません。ここでAppleエンジニアとの議論を読んでください:関連するオブジェクトはいつリリースされますか?

元の答え:

キーと値の監視を使用できます。

[yourView addObserver:self forKeyPath:@"bounds" options:0 context:nil];

そして実装:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (object == yourView && [keyPath isEqualToString:@"bounds"]) {
        // do your stuff, or better schedule to run later using performSelector:withObject:afterDuration:
    }
}

2
実際、サブビューのレイアウトは、layoutSubviewsの目的とまったく同じです。そのため、サイズの変化を観察することは、機能的であるにもかかわらず、IMOの設計としては良くありません。
公証人

@uliwitnessよく彼らはこのものを変え続けます。とにかく、今viewWillTransitionなどを使用する必要があります
Dan Rosenstark

UIViewではなく、UIViewControllersのviewWillTransition。
アルマンド

layoutSubviewsビューの現在のサイズに応じて、異なるサブビューを追加/削除する必要があり、異なる制約を追加/削除する必要がある場合でも、推奨される方法を使用していますか?
Chris Prince、

1
さて、私は私の場合はこれに答えたと思います。境界の上書きに必要な設定(サイズに依存)を実行すると、機能します。私がlayoutSubviewsでそれを行うときはしません。
Chris Prince、

93

ではUIView、サブクラス、プロパティのオブザーバーは使用することができます。

override var bounds: CGRect {
    didSet {
        // ...
    }
}

サブクラス化を行わない場合スマートキーパスを使用したキーと値の監視は次のようになります。

var boundsObservation: NSKeyValueObservation?

func beginObservingBounds() {
    boundsObservation = observe(\.bounds) { capturedSelf, _ in
        // ...
    }
}

2
@アリなぜあなたはそう思いますか?
ルドルフアダムコビッチ2015年

ロードフレームの変更では、境界がそうではないように見えます(私はそれが奇妙で、多分私は何か間違っていることを知っています)
Ali

3
@Ali:ここで数回言及されているように、これはframe派生して実行時に計算されるプロパティです。あなたがそうする非常に賢明で知っている理由がない限り、これを上書きしないでください。それ以外の場合:boundsまたは(さらに良い)を使用しますlayoutSubviews
auco

3
サークルを作成するこのような素晴らしい方法。ありがとう!override var bounds: CGRect { didSet { layer.cornerRadius = bounds.size.width / 2 }}
SimplGy

4
ビュー階層のどこにビューを配置するかによって、検出boundsまたはframe変更が機能するとは限りません。layoutSubviews代わりにオーバーライドします。これこの答えを見てください。
HuaTham

31

UIViewのサブクラスを作成し、layoutSubviewsをオーバーライドする


11
この問題は、サブビューがサイズを変更できるだけでなく、サイズの変更をアニメーション化できることです。UIViewがアニメーションを実行するとき、毎回layoutSubviewsを呼び出しません。
David Jeske、2013

1
@DavidJeske UIViewAnimationOptionsには、UIViewAnimationOptionLayoutSubviewsというオプションがあります。これはおそらく役立つと思います。:)
Neal.Marlin

8

Swift 4キーパスKVO-これが自動回転とiPadサイドパネルへの移動を検出する方法です。どのようなビューでも機能するはずです。UIViewのレイヤーを監視する必要がありました。

private var observer: NSKeyValueObservation?

override func viewDidLoad() {
    super.viewDidLoad()
    observer = view.layer.observe(\.bounds) { object, _ in
        print(object.bounds)
    }
    // ...
}
override func viewWillDisappear(_ animated: Bool) {
    observer?.invalidate()
    //...
}

この解決策は私にとってうまくいきました。私はSwiftを初めて使用するので、ここで使用した\ .bounds構文がわかりません。それはどういう意味ですか?
WBuck


.layerトリックをした!を使用view.observeしても機能しない理由を知っていますか?
d4Rk

@ d4Rk-わかりません。私の推測では、UIViewフレームよりもレイヤーの境界があいまいではありません。たとえば、UITableViewのcontentOffsetは、そのサブビューの最終的な座標に影響します。わからない。
ウォーレンストリンガー2018年

これは私にとって素晴らしい答えです。私の場合は、ビューのレイアウトにAutoLayoutを使用しています。ただし、UICollectionViewFlowLayoutは、明示的なitemSizeを指定するように強制します。しかし、collectionViewのサイズが変更されたとき(私の場合、AutoLayoutでツールバーを表示しているときのように)、itemSizeは同じままでスローされます= [オブザーバーは、私がこれまでに得た最もクリーンなアプローチです。そして最高のもの!
Yitzchak

7

UIViewのサブクラスを作成して、

setFrame:(CGRect)frame

方法。これは、ビューのフレーム(サイズ)が変更されたときに呼び出されるメソッドです。このようなことをしてください:

- (void) setFrame:(CGRect)frame
{
  // Call the parent class to move the view
  [super setFrame:frame];

  // Do your custom code here.
}

賢明で機能的です。よかった。
El Zorko

1
FYIこれはUIImageViewサブクラスでは機能しません。詳細:stackoverflow.com/questions/19216684/...
ウィリアム・エントリケン

また、自動回転によるサイズ変更中setFrame:UITextViewサブクラスで呼び出されませんlayoutSubviews:でした。注:自動レイアウトとiOS 7.0を使用しています。
ma11hew28 2013年

3
オーバーライドしないでくださいsetFrame:frame派生プロパティです。私の答えをご覧ください
Adlai Holler 14

7

かなり古いですが、それでも良い質問です。Appleのサンプルコード、および一部のプライベートUIViewサブクラスでは、次のようにsetBoundsをオーバーライドします。

-(void)setBounds:(CGRect)newBounds {
    BOOL const isResize = !CGSizeEqualToSize(newBounds.size, self.bounds.size);
    if (isResize) [self prepareToResizeTo:newBounds.size]; // probably saves 
    [super setBounds:newBounds];
    if (isResize) [self recoverFromResizing];
}

オーバーライドsetFrame:はお勧めできません。frame由来しcenterboundstransform、そうiOSのは、必ずしもコールすることはありませんsetFrame:


2
これは(理論的には)真実です。ただし、setBounds:フレームプロパティを設定するときも呼び出されません(少なくともiOS 7.1では)。これは、Appleが追加メッセージを回避するために追加した最適化である可能性があります。
nschum 2014

1
…そして、Apple側の悪いデザインは、自分のアクセサを呼び出さないことです。
osxdirk 2014年

1
実際には、両方 frameとはbounds、ビューの基礎となるから派生していますCALayer。レイヤーのゲッターを呼び出すだけです。そしてsetFrame:一方で、レイヤーのフレームを設定し、setBounds:セット層の境界。したがって、どちらか一方をオーバーライドすることはできません。また、layoutSubviews(ジオメトリの変更だけでなく)過度に呼び出されるため、必ずしも適切なオプションであるとは限りません。まだ探しています...
big_m

-3

UIViewControllerインスタンスを使用viewDidLayoutSubviewsしている場合は、オーバーライドすることでうまくいきます。

override func viewDidLayoutSubviews() {
    // update subviews
}

UIViewControllerではなく、UIViewサイズの変更が必要です。
ガストンアントニオモンテス2017年

@GastónAntonioMontesありがとうございます!UIViewインスタンスとUIViewControllerインスタンスの関係に気づいたかもしれません。したがって、UIViewVCがアタッチされていないインスタンスがある場合、他の回答は素晴らしいですが、たまたまVCにアタッチされている場合、これはあなたの男です。申し訳ありませんが、お客様のケースには適用されません。
Dan Rosenstark 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.