web-dev-qa-db-fra.com

Mises en page iOS alternatives pour le portrait et le paysage en utilisant un seul fichier .xib

En utilisant le générateur d'interface dans xcode et un seul fichier .xib, comment puis-je créer des dispositions alternatives lors de la rotation entre les orientations paysage et portrait?

Voir schéma des différentes dispositions enter image description here

N.b. la vue/zone verte contiendrait 3 éléments circulant horizontalement dans le paysage et en portrait ces 3 éléments couleraient verticalement dans la vue/zone verte.

40
Dave Haigh

Une façon de procéder consiste à avoir trois vues dans votre fichier .xib. Le premier est la vue normale de votre Viewcontroller sans sous-vues.

Ensuite, vous créez les vues pour le portrait et le paysage selon vos besoins. Les trois doivent être des vues de niveau racine (regardez la capture d'écran)

enter image description here

Dans votre Viewcontroller, créez 2 IBOutlets, une pour le portrait et une pour la vue paysage et connectez-les aux vues correspondantes dans le générateur d'interface:

IBOutlet UIView *_portraitView;
IBOutlet UIView *_landscapeView;
UIView *_currentView;

La troisième vue, _currentView est nécessaire pour garder une trace de laquelle de ces vues est actuellement affichée. Créez ensuite une nouvelle fonction comme celle-ci:

-(void)setUpViewForOrientation:(UIInterfaceOrientation)orientation
{
    [_currentView removeFromSuperview];
    if(UIInterfaceOrientationIsLandscape(orientation))
    {
        [self.view addSubview:_landscapeView];
        _currentView = _landscapeView;
    }
    else
    {
        [self.view addSubview:_portraitView];
        _currentView = _portraitView;
    }
}

Vous devrez appeler cette fonction à partir de deux endroits différents, d'abord pour l'initialisation:

-(void)viewDidLoad
{
    [super viewDidLoad];
    UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
    [self setUpViewForOrientation:interfaceOrientation];
}

Et deuxième pour les changements d'orientation:

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [self setUpViewForOrientation:toInterfaceOrientation];
}

J'espère que cela vous aide!

44
MeXx

Il n'existe aucun moyen automatique de prendre en charge cela, car il est contraire à la conception Apple. Vous devez avoir un ViewController prenant en charge les deux orientations.

Mais si vous voulez vraiment le faire, vous devez recharger les xib sur les événements de rotation et charger différents fichiers nib.

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [[NSBundle mainBundle] loadNibNamed:[self nibNameForInterfaceOrientation:toInterfaceOrientation] owner:self options:nil];
    [self viewDidLoad];
}

- (NSString*) nibNameForInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    NSString *postfix = (UIInterfaceOrientationIsLandscape(interfaceOrientation)) ? @"portrait" : @"landscape";
    return [NSString stringWithFormat:@"%@-%@", NSStringFromClass([self class]), postfix];
}

Et vous créez deux fichiers nib post-fixes avec "paysage" et "portrait".

14
Grzegorz Krukowski

J'aime la solution de @ MeXx, mais elle a la surcharge de garder en mémoire deux hiérarchies de vues différentes. De plus, si une sous-vue a un état (par exemple, la couleur) qui change, vous devrez le mapper sur lorsque vous changez de hiérarchie.

Une autre solution pourrait être d'utiliser la mise en page automatique et de permuter les contraintes de chaque sous-vue pour chaque orientation. Cela fonctionnerait mieux si vous avez un mappage 1: 1 entre les sous-vues dans les deux orientations.

Naturellement, vous souhaitez utiliser IB pour définir visuellement la disposition de chaque orientation. Selon mon plan, vous devrez faire ce que @MeXx prescrit, mais ensuite créer un mécanisme pour stocker les deux ensembles de contraintes une fois la nib chargée (awakeFromNib) et réappliquer le jeu correct sur la mise en page (viewWillLayoutSubviews). Vous pouvez supprimer la hiérarchie des vues secondaires une fois que vous avez supprimé et stocké ses contraintes. (Étant donné que les contraintes sont spécifiques à la vue, vous créerez probablement de nouvelles contraintes à appliquer aux sous-vues réelles).

Désolé je n'ai pas de code. Ce n'est qu'un plan à ce stade.

Remarque finale - cela serait plus facile avec un xib qu'avec un storyboard car dans un storyboard, il est difficile de décrire des vues qui vivent en dehors de la vue principale d'un contrôleur de vue (ce qui est souhaitable car sinon c'est un PITA à modifier). Blach!

8
TomSwift

Vous pouvez sûrement monsieur ....

J'avais l'habitude de faire en donnant le cadre manuellement lorsque l'appareil tourne

Chaque fois que le périphérique tourne, - (void)viewWillLayoutSubviews est appelé

par exemple - prenez un UIButton *button dans votre UIView,

- (void)viewWillLayoutSubviews
   {
       if (UIDeviceOrientationIsLandscape([self.view interfaceOrientation]))
       {
         //x,y as you want
         [ button setFrame:CGRectMake:(x,y,button.width,button.height)];

       }
       else
       {
         //In potrait
          //x,y as you want
         [ button setFrame:CGRectMake:(x,y,button.width,button.height)];


       }

   }

De cette façon, vous pouvez le placer comme vous le souhaitez. Merci

Jetez un œil à ces images

Première image de mon xCode XIB UIView que je veux dans les deux écrans

enter image description here

Maintenant, comme je veux aussi regarder cette vue en paysage, je suis allé modifier ma -(void)viewWillLayoutSubviews

enter image description here

Maintenant, le résultat est comme cette première image de potrait Screen dans Simulator

enter image description here

et c'est en paysage

enter image description here

3
kshitij godara

Je voudrais juste ajouter une nouvelle réponse pour refléter les nouvelles fonctionnalités à venir dans ios8 et XCode 6.

Dans le dernier nouveau logiciel, Apple introduit classes de taille , qui permettent au storyboard de s'adapter intelligemment en fonction de la taille d'écran et de l'orientation que vous utilisez. Bien que je vous suggère de regarder dans les documents ci-dessus ou lors de la session WWDC 2014 création d'applications adaptatives avec UIKit , je vais essayer de paraphraser.

Assurez-vous que les classes de taille sont activé, .

Vous remarquerez que vos fichiers de storyboard sont désormais carrés. Vous pouvez configurer l'interface de base ici. L'interface sera redimensionnée intelligemment pour tous les appareils activés et l'orientation.

Au bas de l'écran, vous verrez un bouton disant yAny xAny. En cliquant dessus, vous pouvez modifier une seule classe de taille, par exemple, paysage et portrait.

Je vous suggère de lire les documents ci-dessus, mais j'espère que cela vous aidera, car il n'utilise qu'un seul storyboard.

2
rocket101

Solution avec storyboard [ios7]. À l'intérieur du contrôleur de vue principal, il y a la vue de conteneur qui peut inclure le contrôleur de barre d'onglets. À l'intérieur du contrôleur de la barre d'onglets, il y a 2 onglets. Un onglet est destiné au contrôleur avec portrait et un autre au contrôleur en mode paysage.

Le contrôleur de vue principal implémente ces fonctions:

#define INTERFACE_ORIENTATION() ([[UIApplication sharedApplication]statusBarOrientation])

@interface MainViewController ()
@property(nonatomic,weak) UITabBarController *embeddedTabBarController;
@end

@implementation MainViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"embedContainer"])
    {
        self.embeddedTabBarController = segue.destinationViewController;
        [self.embeddedTabBarController.tabBar setHidden:YES];
    }
}

- (void) adaptToOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    if (UIInterfaceOrientationIsLandscape(interfaceOrientation))
    {
        [self.embeddedTabBarController setSelectedIndex:1];
    }
    else
    {
        [self.embeddedTabBarController setSelectedIndex:0];
    }
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
{
    [self adaptToOrientation:interfaceOrientation];    
}

- (void) viewWillAppear:(BOOL)animated
{
    [self adaptToOrientation:INTERFACE_ORIENTATION()];
}

Le lien de séquence dans le storyboard doit être nommé "embedContainer".

Assurez-vous que la vue du conteneur est redimensionnable pour correspondre à la vue parent, dans les deux cas, paysage et portrait.

Faites attention au fait que dans le runtime 2 instances du même contrôleur vivent en même temps.

Storyboard


Mise à jour: Apple docs pour un autre contrôleur de paysage

2
Prcela

Vous pouvez utiliser la bibliothèque GPOrientation. Voici lien

1
Savitha

Redimensionner par programme

J'ai une application où j'ai exactement la même situation. Dans ma NIB, je n'ai que la mise en page du portrait. L'autre mise en page est effectuée par programme. Il n'est pas si difficile de spécifier quelques tailles par programme, donc pas besoin de maintenir deux NIB.

Notez que l'exécution du changement de vue par programmation en définissant des images se traduira par une animation (ou peut facilement être réalisée à l'aide d'une animation). Cela donnera à l'utilisateur une rétroaction précieuse sur le composant déplacé vers quelle position. Si vous chargez simplement une deuxième vue, vous perdrez cet avantage et du point de vue de l'interface utilisateur, je pense que le chargement d'une autre vue n'est pas une bonne solution.

Utilisez deux ViewControllers

Si vous aimez utiliser deux ViewControllers, alors Apple décrit comment le faire dans sa documentation développeur, c'est-à-dire que vous trouverez la réponse à votre question ici: RespondingtoDeviceOrientationChanges .

Si vous souhaitez le faire par programme, vous pouvez ajouter du code de repositionnement dans la méthode didRotateFromInterfaceOrientation.

Exemple

J'ai ajouté la méthode à mon ViewController:

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    [self adjustLayoutToOrientation];
}

Puis mis en œuvre l'ajustement de la mise en page. Voici un exemple (le code est spécifique à mon application, mais vous pouvez lire les tailles des super-aperçus et en calculer des cadres de sous-vues.

- (void)adjustLayoutToOrientation
{
    UIInterfaceOrientation toInterfaceOrientation = self.interfaceOrientation;
    bool isIPhone = !([[UIDevice currentDevice] respondsToSelector:@selector(userInterfaceIdiom)] && [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad);

    [self adjustViewHeight];
    [self adjustSubviewsToFrame: [mailTemplateBody.superview.superview frame]];

    CGRect pickerViewWrapperFrame = pickerViewWrapper.frame;
    if(isIPhone) {
        pickerViewWrapperFrame.Origin.x = 0;
        pickerViewWrapperFrame.size.height = keyboardHeight;
        pickerViewWrapperFrame.Origin.y = [self view].frame.size.height-pickerViewWrapperFrame.size.height;
        pickerViewWrapperFrame.size.width = [self view].frame.size.width;
    }
    else {
        pickerViewWrapperFrame = pickerView.frame;
    }

// MORE ADJUSTMENTS HERE

}
1
Christian Fries