私はどうにかして新しいセーフエリアのレイアウトガイドを扱い、iOS 9とiOS 10との後方互換性を維持しています。(編集:@NickEntinによるコメントで指摘されているように、この実装はステータスバーが表示されますが、これはiPhone Xの横長の風景には当てはまりませんが、上部のスペースを最大にすると(20ポイント)、問題なく実行できます。
例えば。ステータスバーの10ポイント下(およびiPhone Xのセンサーハウジングの10ポイント下)に表示する場合は、次のようにします。
File Inspector
に移動し、Use Safe Area Layout Guides
をチェックして金庫を有効にします。>=
(以上)制約、定数30
(高さ20ポイントのステータスバーに対して10ポイントの間隔が必要であるため)、および優先順位High
(750)を使用して、ビューの上からメインビューの上への制約を作成します。=
(equal)制約、定数10
およびpriority Low
(250)を使用して、ビューの上からセーフエリアの上までの制約を作成します。下部のビュー(およびセーフエリアの先頭/末尾または左/右)でも同じことができます。
File Inspector
に移動し、Use Safe Area Layout Guides
をチェックして金庫を有効にします。>=
(以上)制約、定数10
および優先度High
(750)を使用して、ビューの下からメインビューの下までの制約を作成します。=
(equal)制約、定数10
およびpriority Low
(250)を使用して、ビューの下からセーフエリアの下までの制約を作成します。IOS 9およびiOS 10用のSafe Areasの下位互換性は、ストーリーボードを使用している場合にのみ機能します。 xibを使用している場合は、使用するレイアウトガイドはありません。 https://forums.developer.Apple.com/thread/87329
回避策はどちらかと思われます
(a)xibをストーリーボードに移行する。
(b)プログラム的にいくつかの制約を追加する。
(a)が本当に選択肢ではない場合、手動のアプローチは次のようになります。
あなたの安全な領域内(つまりステータスバーやナビゲーションバーの下)に表示したいビューがxibにあるとします。
ビューとiOS 11の安全な領域の間にxibで制約を追加します。一番上の制約を優先度750に割り当てます。
View Controllerで、プロパティを追加します。
@property (nonatomic, strong) NSLayoutConstraint *topLayoutConstraint;
そしてviewDidLayoutSubviewsで:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
if (@available(iOS 11, *)) {
// safe area constraints already set
}
else {
if (!self.topLayoutConstraint) {
self.topLayoutConstraint = [self.<yourview>.topAnchor constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor];
[self.topLayoutConstraint setActive:YES];
}
}
}
新しい制約はiOS 9とiOS 10に対してのみ作成され、デフォルトの優先順位は1000で、xibのものより優先されます。
ホーム指標を回避する必要がある場合は、下側の拘束に対して繰り返します。
Swift 4バージョン:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if #available(iOS 11, *) {
// safe area constraints already set
} else {
if topLayoutConstraint == nil {
topLayoutConstraint = <yourview>.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor)
topLayoutConstraint?.isActive = true
}
}
}
私がXcode 9 GMで観察したiOS 11の安全領域制限との少なくとも1つの後方互換性の問題があります - 安全な領域制限のあるView Controller。
ナビゲーションバーが非表示になっているときにセーフエリアの上部が制限されたビューをプッシュすると、プッシュされたビューはiOS 9および10のステータスバーと重なります。
ナビゲーションバーが表示されていて、「トップバーの下」が無効になっている場合でも、プッシュされたビューはナビゲーションバーの下にスライドして画面の一番上に移動します。ナビゲーションバーは正しく配置されています。
IOS 11では、どちらの場合もレイアウトは正しくなります。
これは簡単な例です: http://www.filedropper.com/foobar
そして、これはnavバーを隠した状態のビデオです(左側のiOS 10.3、右側のiOS 11): https://vimeo.com/234174841/1e27a96a87
ナビゲーションバーが表示されるバージョンは次のとおりです(ペン先で有効)。 https://vimeo.com/234316256/f022132d57
私はこれをレーダーとして登録しました#34477706。
ナビゲーションバーの表示例を指摘してくれた @ Sander に感謝します。
ストーリーボードなしでxibを使用する場合、それらはios 10に関するレイアウトガイドを持っていません。後方互換性を持たせるためにxibをストーリーボードに移動してください。
はい、あなたのプロジェクト/アプリは問題なくiOS 11より前のiOSバージョンで動作します。 11より前のiOSバージョンでは、Safe Area Layoutを通常のAutoLayoutに置き換えたり考慮したりして、Rules of TopとBottomのレイアウトガイドに従っています。
私は既存のプロジェクトを 'SafeAreaLayout'の有無にかかわらず両方のプラットフォーム(iOS 11と後方iOS 10)でテストしました。それはうまくいっています。
確認してください。
プロジェクト/ユーザーインターフェースをオートレイアウトで設計した場合あなたのUIElementの制約は、(スーパービューではなく)トップとボトムのレイアウトガイドに従います。だからSafeAreaLayoutオプションをシングルクリック(有効にする)することで、あなたのストーリーボードのすべてのInterface BuildersファイルにSafeAreaレイアウトを自動的に正しく実装するでしょう。
SafeAreaLayoutでプロジェクト/ユーザーインターフェースを設計した場合それからそれは自動的に後方iOSの上と下のレイアウトガイドに従います。
これが結果のサンプルスナップショットです。セーフエリアレイアウトを有効または無効にしても、既存のデザインには影響しません。
自動レイアウト
簡単に言うと、あなたの質問に対する答えは、「11より前のiOSと互換性のあるセーフエリアレイアウトガイドの有効化」です。
プロジェクト/アプリにセーフエリアレイアウトを実装できます。セーフエリアレイアウトを上部レイアウトおよび下部レイアウトに変換することで、以前のiOSバージョンで正常に機能します。
Objective-Cでこれを使用していましたが、iOS 10で良い結果が得られました。xibでSafeAreaを使用する場合は、viewDidLoad
を追加します。
if (@available(iOS 11.0, *)) {}
else {
self.edgesForExtendedLayout = UIRectEdgeNone;
}
私はあなたがあなたのNSLayoutConstraint
に固定されているsafeArea
をサブクラス化するだけでよいもっと便利な方法を見つけました。
あなたはUIViewからViewControllerを入手しなければならないのでちょっと厄介ですが、私の意見では、それはAppleがXibsのsafeAreaのための下位互換性を最終的に修正するまでは簡単で良い方法です。
サブクラス:
class SafeAreaBackwardsCompatabilityConstraint: NSLayoutConstraint {
private weak var newConstraint: NSLayoutConstraint?
override var secondItem: AnyObject? {
get {
if #available(iOS 11.0, *) {}
else {
if let vc = (super.secondItem as? UIView)?.parentViewController, newConstraint == nil {
newConstraint = (self.firstItem as? UIView)?.topAnchor.constraint(equalTo: vc.topLayoutGuide.bottomAnchor)
newConstraint?.isActive = true
newConstraint?.constant = self.constant
}
}
return super.secondItem
}
}
override var priority: UILayoutPriority {
get {
if #available(iOS 11.0, *) { return super.priority }
else { return 750 }
}
set { super.priority = newValue }
}
}
private extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.next
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
Xib:
「セーフエリアレイアウトガイド」は後方互換性があります。まあ、あなたがxibでそれを使わない限り。ストーリーボードでは大丈夫そうです。
私は自分のビューの一番上にある最初のオブジェクトから "Top layout constraint"にアクセスすることで問題を解決しました。
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *topLayoutConstraint;
それから、Constant値をその制約に変更してビューを更新します。たとえば、ナビゲーションバー(高さ44)とステータスバー(高さ20)を使用すると、次のようになります。
if (SYSTEM_VERSION_LESS_THAN(@"11.0")) {
_topLayoutConstraint.constant = 64;
[self.view layoutIfNeeded];
}
そのように定義されているSYSTEM_VERSION_LESS_THANの場合:
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
IOS 9上のWKWebView&Safe Areaとの下位互換性の問題があります。何らかの理由で、WKWebViewは単にセーフエリアレイアウト設定を無視します。
Objective-Cでは、iPhone-Xの場合、上下の余白が表示されます。
if (@available(iOS 11, *)) {
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:self.childView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.parentView.safeAreaLayoutGuide
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0];
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:self.childView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.parentView.safeAreaLayoutGuide
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
} else {
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:self.childView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.parentView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0];
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:self.childView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.parentView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
}
すべてのViewControllerが拡張する汎用ViewControllerがある場合、別の解決策は調整する項目をIBOutletCollectionに配置し、そのGenericViewControllerでプログラム的に調整することです。これが私のコードです:
@IBOutlet var adjustTopSpaceViews: [UIView]?
override func viewDidLoad() {
super.viewDidLoad()
adjustViews()
....
}
func adjustViews() {
guard let views = adjustTopSpaceViews,
ProcessInfo.processInfo.operatingSystemVersion.majorVersion < 11 else {
return
}
let statusBarHeight = UIApplication.shared.statusBarFrame.height
for subview in views {
subview.superview?.constraints.filter({ (constraint) -> Bool in
return constraint.firstAttribute == .top
&& constraint.secondAttribute == .top
&& (constraint.firstItem as? UIView == subview || constraint.secondItem as? UIView == subview)
}).forEach({ (constraint) in
constraint.constant += (constraint.firstItem as? UIView == subview) ? statusBarHeight : -statusBarHeight
})
}
}
これが私のプロジェクトでやったことです
私の場合、私のtopConstraint
とbottomConstraint
sは両方とも@IBOutlet
sです。これはiOS 8
とも互換性があります。
上と下の制約に対する私の初期設定は通常のiPhone用で、これが私がiPhone Xのための制約を編集するだけの理由です
// iOS 11 Layout Fix. (For iPhone X)
if #available(iOS 11, *) {
self.topConstraint.constant = self.topConstraint.constant + self.view.safeAreaInsets.top
self.bottomConstraint.constant = self.bottomConstraint.constant + self.view.safeAreaInsets.bottom
}
。
注:self.view
があなたのsuperViewなので、私はsafeAreaInsets
に使っています。
これが私のiOS 9からiOS 11+へのSwift 4 +でのソリューションラッパーです
let safeAreaTopAnchor:NSLayoutYAxisAnchor?
if #available(iOS 11.0, *) {
safeAreaTopAnchor = contentView.safeAreaLayoutGuide.topAnchor
} else {
// Fallback on earlier versions
var parentViewController: UIViewController? {
var parentVCResponder: UIResponder? = self
while parentVCResponder != nil {
parentVCResponder = parentVCResponder!.next
if let viewController = parentVCResponder as? UIViewController {
return viewController
}
}
return nil
}
safeAreaTopAnchor = parentViewController?.topLayoutGuide.bottomAnchor
}