IPhone X Autolayoutの癖に関する質問。
私は2つのボタンを持っています。以前は、これらは画面の下部に触れないように、20のオフセットでスーパービューの下部に配置されていました(以来、リンクをスーパービューではなくセーフエリアに変更しました)。
これで、下部の制約に関する20の定数により、ボタンがファンキーになり、iPhone Xのホームバーから離れすぎているように見えます。
論理的には、iPhone Xの制約から20定数を削除し、ボタンを安全領域の下部に直接配置する必要があります。
しかし今では、ホームバー以外の電話の画面の下部にボタンが近すぎます。
Apple docs?で見逃したこの問題の直接的な解決策はありますか?この場合、iPhone Xサイズクラスは他のiPhoneと重複するため、サイズクラスを使用できません。
IPhone Xを検出するコードを簡単にコーディングして、制約の定数を0に設定できましたが、よりエレガントなソリューションを望んでいました。
おかげで、
Appleドキュメントでは、この問題の解決策になる可能性のあるiOS 11の新しい宣言があると述べています。現在、iPhone XとiPhone 8は同じサイズのクラスを共有しているため、別の解決策を考え出す必要があります。
var additionalSafeAreaInsets:UIEdgeInsets {get set}
以下のコードをAppDelegateに追加すると、rootViewControllerのすべての子が追加の安全領域を継承します。以下のスクリーンショットの例は、この動作を説明しています。
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
if !self.isIphoneX() {
self.window?.rootViewController?.additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: 20, right: 0)
}
return true
}
func isIphoneX() -> Bool {
if #available(iOS 11.0, *) {
if ((self.window?.safeAreaInsets.top)! > CGFloat(0.0)) {
return true;
}
}
return false
}
セーフエリアに配置されたiPhone X Interface Builder
安全領域に配置されたiPhone 8 Interface Builder
iPhone Xシミュレータ-マスター画面
iPhone Xシミュレータ-詳細画面
iPhone 8シミュレータ-マスター画面
iPhone 8シミュレータ-詳細画面
最初に Marcosのソリューション を使用して、この種の問題を修正しようとかなりの時間を費やした後、それが解決しない場合、特に「安全な領域」の場合に遭遇しました高さがゼロではないが画面のセーフエリアではないということは、オフセットが最小20ではなく0であることを意味します。例は、additionalSafeAreaInsets
を介して設定されたボトムセーフエリアを持つ任意のビューコントローラーです。
解決策は、セーフエリアインセットが変更されたときに、ビューがゼロ以外のセーフエリアを持つウィンドウと整列しているかどうかを確認し、それに基づいて制約の下部オフセットをセーフエリアに調整することでした。以下は、長方形スタイルの画面で下から20ptのオフセットにつながり、セーフエリアスタイルの画面(iPhone X、最新のiPad Pro、iPadのスライドオーバーなど)を備えたフルスクリーンでは0になります。
// In UIView subclass, or UIViewController using viewSafeAreaInsetsDidChange instead of safeAreaInsetsDidChange
@available(iOS 11.0, *)
override func safeAreaInsetsDidChange() {
super.safeAreaInsetsDidChange()
isTakingCareOfWindowSafeArea = self.isWithinNonZeroWindowBottomSafeArea
}
private var isTakingCareOfWindowSafeArea = false {
didSet {
guard isTakingCareOfWindowSafeArea != oldValue else { return }
// Different offset based on whether we care about the safe area or not
let offset: CGFloat = isTakingCareOfWindowSafeArea ? 0 : 20
// bottomConstraint is a required bottom constraint to the safe area of the view.
bottomConstraint.constant = -offset
}
}
extension UIView {
/// Allows you to check whether the view is dealing directly with the window's safe area. The reason it's the window rather than
/// the screen is that on iPad, slide over apps and such also have this nonzero safe area. Basically anything that doesn't have a square area (such as the original iPhones with rectangular screens).
@available(iOS 11.0, *)
var isWithinNonZeroWindowBottomSafeArea: Bool {
let view = self
// Bail if we're not in a window
guard let window = view.window else { return false }
let windowBottomSafeAreaInset = window.safeAreaInsets.bottom
// Bail if our window doesn't have bottom safe area insets
guard windowBottomSafeAreaInset > 0 else { return false }
// Bail if our bottom area doesn't match the window's bottom - something else is taking care of that
guard windowBottomSafeAreaInset == view.safeAreaInsets.bottom else { return false }
// Convert our bounds to the window to get our frame within the window
let viewFrameInWindow = view.convert(view.bounds, to: window)
// true if our bottom is aligned with the window
// Note: Could add extra logic here, such as a leeway or something
let isMatchingBottomFrame = viewFrameInWindow.maxY == window.frame.maxY
return isMatchingBottomFrame
}
}