web-dev-qa-db-ja.com

ios10:viewDidLoadフレームの幅/高さが正しく初期化されていません

XCode8 GM and ios10にアップグレードするので、Interface Builderで作成されたすべてのビューは、予想よりずっと遅くまで正しく初期化されません。これは、viewDidLoad、cellForRowAtIndexPath、viewWillAppearなどで、サイズはすべてのビューで{1000,1000}に設定されています。ある時点で修正されているように見えますが、手遅れです。

最初に遭遇する問題は、一般的な角の丸みがボード全体で失敗することです。

view.layer.cornerRadius = view.frame.size.width/2

コードで計算を行うためにフレームサイズに依存するものについては、さらなる問題が現れています。

cellForRowAtIndexPath 

CellForRowAtIndexPathの場合、フレームサイズは最初のテーブル表示で失敗しますが、スクロールすると正常に機能します。 willDisplayCell:forRowAtIndexPathにも正しいフレームサイズがありません。

私はいくつかの値をハードコーディングしましたが、明らかにこれは非常に悪いコードの実践であり、私のプロジェクトでは非常に多数です。

正しいフレームサイズを取得する方法や場所はありますか?

[〜#〜] edit [〜#〜]

フレーム幅の高さの代わりに高さ/幅の制約を使用する方が信頼性が高いことを発見しました。ただし、これにより、アイテムの高さ/幅の制約をリンクするために多くの新しいIBOutletsが必要になるオーバーヘッドが追加される場合があります。

現時点では、IBOutletsなしでビューの高さ/幅の制約に直接アクセスできるUIViewカテゴリを作成しました。最小限の使用では、小さなループは大した問題ではありません。明らかに幅/高さの制約が作成されていないIBアイテムの結果は保証されていません。定数に対してはせいぜい0を返します。また、高さ/幅の制約がなく、先頭/末尾の制約に基づいてビューのサイズが動的に変更される場合、これは機能しません。

-viewDidLoadは正しいフレームサイズを持っているように見えますが、ここで変更を行うと、UIが視覚的に変化することがよくあります。

UIView + WidthHeightConstraints.h

@interface UIView (WidthHeightConstraints)

-(NSLayoutConstraint*)widthConstraint;
-(NSLayoutConstraint*)heightConstraint;
-(NSLayoutConstraint*)constraintForAttribute:(NSLayoutAttribute)attribute;

@end

UIView + WidthHeightConstraints.m

#import "UIView+WidthHeightConstraints.h"

@implementation UIView (WidthHeightConstraints)

-(NSLayoutConstraint*)widthConstraint{
    return [self constraintForAttribute:NSLayoutAttributeWidth];
}
-(NSLayoutConstraint*)heightConstraint {
    return [self constraintForAttribute:NSLayoutAttributeHeight];
}
-(NSLayoutConstraint*)constraintForAttribute:(NSLayoutAttribute)attribute {
    NSLayoutConstraint *targetConstraint = nil;
    for (NSLayoutConstraint *constraint in self.constraints) {
        if (constraint.firstAttribute == attribute) {
            targetConstraint = constraint;
            break;
        }
    }
    return targetConstraint;
}

@end

編集2

上記のカテゴリは、部分的にのみ有効であることが証明されています。主に、iosがNSContentSizeLayoutConstraintタイプのいくつかの余分な高さ/幅の制約の複製を自動的に追加するように見えるため、実際には通常の制約と同じサイズではありません。 NSContentSizeLayoutConstraintはプライベートクラスでもあるため、isKindOfClassを使用してそれらを除外することはできません。それらを効果的にテストする別の方法をまだ見つけていません。これは面倒です。

40
Miro

あなたが説明する最も一般的な問題はiOS 10にのみ現れており、この行を追加することで解決できます(必要な場合):

_self.view.layoutIfNeeded()
_

コードのすぐ上で、制約、layer.cornerRadiusなどを変更する責任があります。

[〜#〜] or [〜#〜]

フレーム/レイヤーに関連するコードをviewDidLayoutSubviews()メソッドに配置します。

_override func viewDidLayoutSubviews() {

    super.viewDidLayoutSubviews()
    view.layer.cornerRadius = self.myView.frame.size.width/2
    view.clipsToBounds = true

    ... etc
}
_
29
pedrouan

同様の問題に対してレーダー(28342777(28221021の複製としてマークされているがオープン))を作成し、受け取った応答は以下のとおりです。

「問題を報告していただきありがとうございます。プロファイル画像ビューに関する詳細情報を入手できますか?Xcode 8では、完全に制約のある見当違いのビューでは、差分を最小限に抑えるためにフレームが保存されなくなり、IBでのフレームの自動更新がサポートされます。 、これらのビューは1000x1000のプレースホルダーサイズでデコードされますが、最初のレイアウト後に解決されます。最初のレイアウトの前に画像を割り当てることができ、最初のレイアウト後に画像を画像ビューに割り当てることができますか?さらに分析します。ありがとう!」

現在、サンプルプロジェクトを提供しています。私の観察:

  • Xcode 7.xからXcode 8.xに変換されたXIBで発生していた問題
  • XIBで意図的に制約を破ると、viewDidLoadは1000x1000ではなく、予想される高さと幅を取得します。
  • 私たちにとっては、UIImageViewで、レイヤーを適用して円形にし、masksToBoundsを使用していました。 masksToBounds = NOに設定すると、すべて正常に機能しました。

Appleは、Xcode 8からはビューが1000x1000に設定されることが標準になると主張していますが、動作は一貫していないようです。

お役に立てれば。

16
Bhavik Bhagat

私は同じ問題に遭遇し、上記の提案を参照することで運のない解決を試みました。

Appleを解決するためのバグ。

Apple修正がリリースされるまで、ハッキングに時間を費やしたくありません。

enter image description hereenter image description here

6
allen

これについてはどうですか:

- (NSLayoutConstraint*)widthConstraint{
    return [self constraintForAttribute:NSLayoutAttributeWidth];
}

- (NSLayoutConstraint*)heightConstraint {
    return [self constraintForAttribute:NSLayoutAttributeHeight];
}

- (NSLayoutConstraint*)constraintForAttribute:(NSLayoutAttribute)attribute {
    NSLayoutConstraint *targetConstraint = nil;
    for (NSLayoutConstraint *constraint in self.constraints) {
        //NSLog(@"constraint: %@", constraint);
        if (![constraint isKindOfClass:NSClassFromString(@"NSContentSizeLayoutConstraint")]) {
            if (constraint.firstAttribute == attribute) {
                targetConstraint = constraint;
                break;
            }
        }
    }
    return targetConstraint;
}
2
942v

私はまったく同じ問題を抱えていました。カスタムUITableViewCellサブクラスがあり、clipsToBounds = YESおよびself.iconView.layer.cornerRadius = self.iconView.frame.size.width/2を使用して、円形の画像を作成していました。 cellForRowAtIndexPathおよびwillDisplayCellからセル構成メソッドを呼び出してみましたが、どちらも機能しませんでした。

ここに機能するものがあります:
レイヤーコードをセルの-layoutSubviewsメソッドに次のように移動します。

-(void)layoutSubviews {
    [super layoutSubviews];
    self.iconView.clipsToBounds = YES;
    self.iconView.layer.cornerRadius = self.iconView.frame.size.width/2;
} 

この後、画像は適切にロードされ、レイヤーコードも機能するはずです。

2
TylerJames

ビューがレイアウトされるタイミングに依存しないでください。それが以前あなたのために働いたなら、それから純粋な運から。 UIKitにはこれに関する保証はほとんどありません。ビューのサイズに適合するものに依存している場合、正しいことは、そのビューでlayoutSubviewsをオーバーライドし、そこにあるものを調整することです。

ビューが画面に完全にレンダリングされた後でも、ビューのサイズを変更する可能性のある非常に多くの条件があります。たとえば、いくつかの例を挙げると、ダブルハイトステータスバー、iPadでのマルチタスク、デバイスの回転などです。したがって、特定の時点でフレーム関連のレイアウト変更を行うことは決して良い考えではありません。

2
Michael Ochs

のみ自動レイアウトボックスのフレームを更新します. enter image description here

0
neha mishra