web-dev-qa-db-fra.com

iPhone X Alignement du bas de l'objet sur les ruines de la zone de sécurité sur d'autres appareils

Question sur les bizarreries de l'iPhone X Autolayout.

J'ai deux boutons, auparavant, ils seraient alignés en bas de la vue d'ensemble avec un décalage de 20 pour qu'ils ne touchent pas le bas de l'écran (j'ai depuis changé le lien en zone sûre et non en vue d'ensemble).

Voici la configuration d'origine: iPhone 8 - Setup

A l'air bien comme prévu sur les iPhones plus anciens. iPhone 8 20 Offset

Maintenant, la constante de 20 sur la contrainte du bas rend maintenant les boutons géniaux et trop éloignés de la barre d'accueil de l'iPhone X. iPhone X 20 Offset

Logiquement, je dois supprimer la constante 20 de la contrainte sur l'iPhone X et aligner les boutons directement avec le bas de la zone de sécurité. iPhone X - Setup

Ça a l'air bien sur iPhone X maintenant. iPhone X 0 Offset

Mais maintenant, il place les boutons trop près du bas de l'écran sur les téléphones non résidentiels. iPhone 8 0 Offset

Toutes les solutions immédiates à ce problème que j'ai manqué dans les documents Apple? Vous ne pouvez pas utiliser les classes de taille car la classe de taille iPhone X chevauche les autres iPhones dans ce cas.

Je pouvais facilement coder pour détecter pour iPhone X et définir la constante de la contrainte à 0, mais j'espérais une solution plus élégante.

Merci,

16
0000101010

Le Apple Docs indique qu'il existe une nouvelle déclaration dans iOS 11 qui peut être une solution à ce problème. Actuellement, l'iPhone X et l'iPhone 8 partagent la même classe de taille, nous devons donc trouver une autre solution.

var additionalSafeAreaInsets: UIEdgeInsets {get set}

Ajoutez le code suivant ci-dessous dans votre AppDelegate et tous les enfants de rootViewController hériteront de la zone de sécurité supplémentaire. Des exemples de captures d'écran ci-dessous décrivent ce comportement.

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 aligné sur la zone de sécurité

enter image description here

le générateur d'interface de l'iPhone 8 est aligné sur la zone de sécurité

enter image description here

iPhone X Simulator - Écran principal

enter image description here

iPhone X Simulator - Écran Détails

enter image description here

iPhone 8 Simulator - Écran principal

enter image description here

iPhone 8 Simulator - Écran Détails

enter image description here

8
Justin Fischer

Une autre façon d'y parvenir directement à partir du storyboard est de créer deux contraintes:
1. Entre votre élément et la zone de sécurité avec 250 priorité avec 0 constante
2. Entre votre élément et le fond superview avec 750 priorité et 20 constante et greater Than or Equal relation.

enter image description here

6
Marcos Griselli

Après avoir passé pas mal de temps à essayer de résoudre ce type de problème, en utilisant à l'origine la solution de Marcos , je suis tombé sur un cas où il ne l'a pas résolu - en particulier dans le cas où la "zone de sécurité" étant une hauteur non nulle mais n'étant pas la zone de sécurité de l'écran, le décalage était de 0 au lieu du minimum de 20. Exemple étant un contrôleur de vue arbitraire avec une zone de sécurité inférieure définie via additionalSafeAreaInsets.

La solution était de vérifier si notre vue est alignée avec la fenêtre avec une zone de sécurité non nulle lorsque les encarts de zone de sécurité changent, et d'ajuster le décalage inférieur de la contrainte à la zone de sécurité en fonction de cela. Ce qui suit conduit à un décalage de 20 points par rapport au bas de l'écran de style rectangulaire et de 0 en plein écran avec un écran de style zone sûre (iPhone X, dernier iPad Pro, diapositives iPad, etc.).

// 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