web-dev-qa-db-fra.com

UIWindows personnalisé ne tourne pas correctement dans iOS 8

Mettez votre application en mode paysage et exécutez le code suivant:

UIWindow *toastWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
toastWindow.hidden = NO;
toastWindow.backgroundColor = [[UIColor cyanColor] colorWithAlphaComponent:0.5f];

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [toastWindow removeFromSuperview];
});

Sous iOS 7, vous obtiendrez une superposition bleue transparente en haut de l'écran qui disparaîtra au bout de 5 secondes. Sous iOS 8, vous obtiendrez une superposition bleue transparente couvrant un peu plus de la moitié de l'écran.

device screenshot

Cela a évidemment quelque chose à voir avec le changement opéré par Apple dans iOS 8, où les coordonnées de l'écran sont désormais orientées interface et non pas orientées vers les appareils, mais selon la véritable méthode Apple, elles semblent avoir laissé une multitude de bugs en mode paysage et en rotation.

Je peux "réparer" cela en vérifiant si l'orientation de l'appareil est en mode paysage et inverser la largeur et la hauteur des limites de l'écran principal, mais cela ressemble à un bidouillage horrible qui va se rompre lorsque Apple modifiera à nouveau tout dans iOS 9.

CGRect frame = [[UIScreen mainScreen] bounds];

if (UIInterfaceOrientationIsLandscape([UIDevice currentDevice].orientation))
{
    frame.size.width = frame.size.height;
    frame.size.height = [[UIScreen mainScreen] bounds].size.width;
}

UIWindow *toastWindow = [[UIWindow alloc] initWithFrame:frame];
toastWindow.hidden = NO;
toastWindow.backgroundColor = [[UIColor cyanColor] colorWithAlphaComponent:0.5f];

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [toastWindow removeFromSuperview];
});

Quelqu'un a-t-il rencontré ce problème et trouvé une solution meilleure et moins fragile?

EDIT: Je sais que je pourrais simplement utiliser un UIView et l'ajouter à la fenêtre de clé mais je voudrais mettre quelque chose au-dessus de la barre d'état.

15
Reid Main

Votre "solution" n'est pas excellente pour une autre raison, en ce sens qu'elle ne fait pas pivoter la fenêtre afin que le texte et les autres sous-vues apparaissent avec l'orientation appropriée. En d'autres termes, si vous souhaitiez améliorer la fenêtre avec d'autres sous-vues, celles-ci seraient mal orientées.

...

Dans iOS8, vous devez définir le rootViewController de votre fenêtre. Ce dernier doit également renvoyer les valeurs appropriées de 'shouldAutoRotate' et 'supportedInterfaceOrientations'. Pour en savoir plus à ce sujet, rendez-vous sur: https://devforums.Apple.com/message/1050398#1050398

Si vous n'avez pas de rootViewController pour votre fenêtre, vous indiquez effectivement au framework que la fenêtre ne doit jamais autoRotate. Dans iOS7, cela ne faisait aucune différence, car le framework ne fonctionnait pas pour vous de toute façon. Dans iOS8, le framework gère les rotations et pense faire ce que vous avez demandé (avec un nil rootViewController) lorsqu'il restreint les limites de votre fenêtre.

Essaye ça:

@interface MyRootViewController : UIViewController
@end

@implementation MyRootViewController

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskAll;
}

- (BOOL)shouldAutorotate
{
    return YES;
}

@end

Maintenant, ajoutez ce rootViewController à votre fenêtre après l’instanciation:

UIWindow *toastWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
toastWindow.rootViewController = [[MyRootViewController alloc]init];
26
Rich Waters

Je crois que vous devez convertir le UICoordinateSpace.

Sur iPhone 6 Plus, les applications peuvent désormais se lancer déjà en orientation paysage, ce qui a gâché une application sur laquelle je travaille, car elle ne prend en charge l'orientation portrait que dans la plupart des applications, à l'exception d'un écran (ce qui signifie qu'il doit prendre en charge les orientations de paysage dans le clavier).

Le code qui a résolu ce problème était le suivant:

self.window = [[UIWindow alloc] initWithFrame:[self screenBounds]];

et en calculant les bornes avec le code suivant:

- (CGRect)screenBounds
{
    CGRect bounds = [UIScreen mainScreen].bounds;
    if ([[UIScreen mainScreen] respondsToSelector:@selector(fixedCoordinateSpace)]) {
        id<UICoordinateSpace> currentCoordSpace = [[UIScreen mainScreen] coordinateSpace];
        id<UICoordinateSpace> portraitCoordSpace = [[UIScreen mainScreen] fixedCoordinateSpace];
        bounds = [portraitCoordSpace convertRect:[[UIScreen mainScreen] bounds] fromCoordinateSpace:currentCoordSpace];
    }
    return bounds;
}

J'espère que cela vous mènera dans la bonne direction.

3
runmad

Dans iOS 7 et les versions antérieures, le système de coordonnées UIWindow ne tournait pas. Dans iOS 8, c'est le cas. Je suppose que l'image fournie à partir de [[UIScreen mainScreen] bounds] ne tient pas compte de la rotation, ce qui pourrait entraîner des problèmes tels que ce que vous voyez.

Au lieu d'obtenir le cadre de UIScreen, vous pouvez le récupérer dans la fenêtre de clé actuelle de appDelegate.

Cependant, il ne semble pas que vous ayez réellement besoin des fonctionnalités fournies par UIWindow. J'aimerais faire écho à la recommandation des autres que vous utilisiez une UIView à cette fin. UIView est plus générique que UIWindow et devrait être préféré.

1
Nick

La meilleure chose à faire est d'utiliser des vues à la place de Windows:

La plupart des applications iOS créent et utilisent une seule fenêtre au cours de leur vie. Cette fenêtre couvre la totalité de l'écran principal [...]. Cependant, si une application prend en charge l'utilisation d'un écran externe pour la sortie vidéo, elle peut créer une fenêtre supplémentaire pour afficher le contenu sur cet écran externe. Toutes les autres fenêtres sont généralement créées par le système

Mais si vous pensez que vous avez une raison valable pour créer plusieurs fenêtres, je vous suggère de créer une sous-classe de NSWindow qui gère les tailles automatiquement.

Notez également que les fenêtres utilisent beaucoup de RAM, en particulier sur les écrans de rétine 3x. Avoir plusieurs d'entre eux va réduire la quantité de mémoire que le reste de votre application peut utiliser avant de recevoir des avertissements de faible mémoire et d'être éventuellement tué.

0
Abhi Beckert