web-dev-qa-db-ja.com

iPhone Xオブジェクトの下部をセーフエリアの遺跡に合わせると他のデバイスで見える

IPhone X Autolayoutの癖に関する質問。

私は2つのボタンを持っています。以前は、これらは画面の下部に触れないように、20のオフセットでスーパービューの下部に配置されていました(以来、リンクをスーパービューではなくセーフエリアに変更しました)。

これが元の設定です: iPhone 8 - Setup

古いiPhoneで期待どおりに見えます。 iPhone 8 20 Offset

これで、下部の制約に関する20の定数により、ボタンがファンキーになり、iPhone Xのホームバーから離れすぎているように見えます。 iPhone X 20 Offset

論理的には、iPhone Xの制約から20定数を削除し、ボタンを安全領域の下部に直接配置する必要があります。 iPhone X - Setup

IPhone Xで問題ありません。 iPhone X 0 Offset

しかし今では、ホームバー以外の電話の画面の下部にボタンが近すぎます。 iPhone 8 0 Offset

Apple docs?で見逃したこの問題の直接的な解決策はありますか?この場合、iPhone Xサイズクラスは他のiPhoneと重複するため、サイズクラスを使用できません。

IPhone Xを検出するコードを簡単にコーディングして、制約の定数を0に設定できましたが、よりエレガントなソリューションを望んでいました。

おかげで、

16
0000101010

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

enter image description here

安全領域に配置されたiPhone 8 Interface Builder

enter image description here

iPhone Xシミュレータ-マスター画面

enter image description here

iPhone Xシミュレータ-詳細画面

enter image description here

iPhone 8シミュレータ-マスター画面

enter image description here

iPhone 8シミュレータ-詳細画面

enter image description here

8
Justin Fischer

ストーリーボードから直接これを実現する別の方法は、2つの制約を作成することです。
1。要素と安全領域の間、優先度250、定数0
2。あなたの要素と750の優先度と20の定数とgreater Than or Equal関係を持つスーパービューの下部の間。

enter image description here

6
Marcos Griselli

最初に 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

    }

}
1
Ohifriend