私は自分のアプリをiPhone Xに適応させ始め、Interface Builderで問題を見つけました。公式Appleビデオによると、セーフエリアレイアウトガイドは後方互換性があると想定されています。ストーリーボードではうまく機能することがわかりました。
しかし、私のXIBファイルでは、iOS 10でセーフエリアレイアウトガイドが尊重されていません。
新しいOSバージョンでは正常に機能しますが、iOS 10デバイスは、セーフエリアの距離をゼロと見なすようです(ステータスバーのサイズは無視します)。
必要な構成がありませんか?それはXcodeのバグですか?もしそうなら、既知の回避策はありますか?
テストプロジェクトの問題のスクリーンショットを次に示します(左iOS 10、右iOS 11):
セーフエリアレイアウトと下位互換性にはいくつかの問題があります。 here のコメントを参照してください。
Superview.topの優先度が1000以上= 20.0、safearea.topの優先度が750などの追加の制約を使用して、問題を回避できる場合があります。常にステータスバーを表示する場合は、問題を修正する必要があります。
これよりも多くの問題が発生した場合は特に、iOS 11以前とiOS-11以上で別々のストーリーボード/ xibsを使用することをお勧めします。これが望ましい理由は、iOS 11より前のリリースでは制約を上部/下部のレイアウトガイドにレイアウトする必要があるが、iOS 11では制約を安全な領域にレイアウトする必要があるためです。レイアウトガイドはなくなりました。結果が常にステータスバーに表示されるのと同じIFFになりますが、iOS 11より前のレイアウトガイドをレイアウトすることは、20ピクセルの最小値でオフセットするよりもスタイル的に優れています。
このアプローチをとる場合、Xcodeが警告を表示せず、レイアウトガイドを使用できるように、各ファイルを使用する正しい展開ターゲット(iOS 11またはそれ以前のもの)に設定する必要があります。安全なエリア、依存。コードで、実行時にiOS 11を確認し、適切なストーリーボード/ xibsをロードします。
このアプローチの欠点はメンテナンスです(2つのセットのView Controllerを保持して同期を維持します)が、アプリがiOS 11以降のみをサポートするか、Appleが下位互換性レイアウトガイドを修正します制約の生成では、iOS 11より前のバージョンを取り除くことができます。
ところで、これを見ているコントローラーをどのように表示していますか?それは単なるルートビューコントローラーですか、それともあなたはそれを提示しましたか、それとも..?私が気づいた問題は、View Controllerのプッシュに関係しているため、別のケースに遭遇する可能性があります。
現在、下位互換性はうまく機能していません。
私の解決策は、インターフェイスビルダーで2つの制約を作成し、使用しているiOSバージョンに応じて1つを削除することです。
view.top == safe area.top
view.top == superview.top + 20
それらをそれぞれmyConstraintSAFEAREA
およびmyConstraintSUPERVIEW
としてアウトレットとして追加します。次に:
override func viewDidLoad() {
if #available(iOS 11.0, *) {
view.removeConstraint(myConstraintSUPERVIEW)
} else {
view.removeConstraint(myConstraintSAFEAREA)
}
}
私にとって、両方のバージョンで動作させるための簡単な修正は
if #available(iOS 11, *) {}
else {
self.edgesForExtendedLayout = []
}
ドキュメントから:「iOS 10以前では、このプロパティを使用して、View ControllerのどのエッジがNavigation Barまたは他のシステム提供のビューの下に伸びているかを報告します。」したがって、それらを空の配列に設定すると、View Controllerがナビゲーションバーの下にnot拡張することが確実になります。
Docuが利用可能です こちら
このページからの回答のいくつかをこれに組み合わせました。これは魅力のように機能します(質問で要求されたトップレイアウトガイドのみ)。
view
を持つ各constraint
に対してview
のIBOutletを作成しますconstraint
のIBOutlerを作成しますviewDidLoad:
のViewController内
if (@available(iOS 11.0, *)) {}
else {
// For each view and constraint do:
[self.view.topAnchor constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor].active = YES;
self.constraint.active = NO;
}
編集:
コードベースで最終的に使用した改良版を以下に示します。以下のコードをコピーして貼り付け、各ビューと制約をそれぞれのIBOutletCollectionに接続します。
@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *constraintsAttachedToSafeAreaTop;
@property (strong, nonatomic) IBOutletCollection(UIView) NSArray *viewsAttachedToSafeAreaTop;
if (@available(iOS 11.0, *)) {}
else {
for (UIView *viewAttachedToSafeAreaTop in self.viewsAttachedToSafeAreaTop) {
[viewAttachedToSafeAreaTop.topAnchor constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor].active = YES;
}
for (NSLayoutConstraint *constraintAttachedToSafeAreaTop in self.constraintsAttachedToSafeAreaTop) {
constraintAttachedToSafeAreaTop.active = NO;
}
}
各IBOutletCollectionの数は等しくなければなりません。例えば各ビューには、関連する制約が必要です
私はこれを使用し、一番上のセーフエリアレイアウトを追加し、コンセントに接続します
@IBOutlet weak var topConstraint : NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
if !DeviceType.IS_IPHONE_X {
if #available(iOS 11, *) {
}
else{
topConstraint.constant = 20
}
}
}
NSLayoutConstraintサブクラスを追加して、この問題(IBAdjustableConstraint
)を修正し、@IBInspectable
変数を使用して、次のようにします。
class IBAdjustableConstraint: NSLayoutConstraint {
@IBInspectable var safeAreaAdjustedConstant: CGFloat = 0 {
didSet {
if OS.TenOrBelow {
constant += safeAreaAdjustedConstantLegacy
}
}
}
}
そしてOS.TenOrBelow
struct OS {
static let TenOrBelow = UIDevice.current.systemVersion.compare("10.9", options: NSString.CompareOptions.numeric) == ComparisonResult.orderedAscending
}
それをIBの制約のサブクラスとして設定するだけで、<iOS11固有の変更を行うことができます。これが誰かを助けることを願っています。
これも機能します:
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 11.0, *) {}
else {
view.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height - 80).isActive = true
view.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width - 20).isActive = true
}
}
最終的に、xibファイルにある安全な領域への制約を削除しました。代わりに、問題のUIViewへのアウトレットを作成し、コードからこのように、viewDidLayoutSubviewsに接続しました。
let constraint = alert.viewContents.topAnchor.constraint(equalTo: self.topLayoutGuide.bottomAnchor, constant: 0)
constraint.priority = 998
constraint.isActive = true
これにより、小さな「アラート」が画面の上部に結び付けられますが、アラート内のコンテンツビューは常に上部の安全領域(iOS11ish)/ topLayoutGuide(iOS10ish)の下になります。
シンプルで一度限りのソリューション。何かが壊れた場合、私は戻ってきます????。