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を使用してそれらを除外することはできません。それらを効果的にテストする別の方法をまだ見つけていません。これは面倒です。
あなたが説明する最も一般的な問題は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
}
_
同様の問題に対してレーダー(28342777(28221021の複製としてマークされているがオープン))を作成し、受け取った応答は以下のとおりです。
「問題を報告していただきありがとうございます。プロファイル画像ビューに関する詳細情報を入手できますか?Xcode 8では、完全に制約のある見当違いのビューでは、差分を最小限に抑えるためにフレームが保存されなくなり、IBでのフレームの自動更新がサポートされます。 、これらのビューは1000x1000のプレースホルダーサイズでデコードされますが、最初のレイアウト後に解決されます。最初のレイアウトの前に画像を割り当てることができ、最初のレイアウト後に画像を画像ビューに割り当てることができますか?さらに分析します。ありがとう!」
現在、サンプルプロジェクトを提供しています。私の観察:
Appleは、Xcode 8からはビューが1000x1000に設定されることが標準になると主張していますが、動作は一貫していないようです。
お役に立てれば。
これについてはどうですか:
- (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;
}
私はまったく同じ問題を抱えていました。カスタム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;
}
この後、画像は適切にロードされ、レイヤーコードも機能するはずです。
ビューがレイアウトされるタイミングに依存しないでください。それが以前あなたのために働いたなら、それから純粋な運から。 UIKitにはこれに関する保証はほとんどありません。ビューのサイズに適合するものに依存している場合、正しいことは、そのビューでlayoutSubviews
をオーバーライドし、そこにあるものを調整することです。
ビューが画面に完全にレンダリングされた後でも、ビューのサイズを変更する可能性のある非常に多くの条件があります。たとえば、いくつかの例を挙げると、ダブルハイトステータスバー、iPadでのマルチタスク、デバイスの回転などです。したがって、特定の時点でフレーム関連のレイアウト変更を行うことは決して良い考えではありません。