web-dev-qa-db-fra.com

iOS: ViewController modal avec fond transparent

J'essaie de présenter un contrôleur de vue de manière modale, avec un arrière-plan transparent. Mon objectif est de permettre l'affichage simultané de la vue des contrôleurs de présentation présentés et présentés. Le problème est que, lorsque l'animation de présentation est terminée, la vue du contrôleur de vue de présentation disparaît.

- (IBAction)pushModalViewControllerButtonPressed:(id)sender
{
    ModalViewController *modalVC = [[ModalViewController alloc] init];
    [self presentViewController:modalVC animated:YES completion:nil];
}

Je sais que je pourrais simplement ajouter la vue en tant que sous-vue, mais j'aimerais éviter cette solution pour une raison quelconque. Comment pourrais-je le réparer?

157
Michael

Ce code suivant ne fonctionne que sur l'iPad.

self.view.backgroundColor = [UIColor clearColor];
self.modalPresentationStyle = UIModalPresentationCurrentContext;
[self presentModalViewController:modalVC animated:YES];

J'irais avec ajouter une vue secondaire. 

Voici une très bonne discussion. Regardez les commentaires en particulier. Pas seulement la réponse.

Vue modale

Si j'étais vous, je ne le ferais pas. Je voudrais ajouter une vue secondaire et le faire. Cela semble me permettre de mieux contrôler les choses.

MODIFIER: 

Comme mentionné par Paul Linsay, depuis iOS 8, tout ce qui est nécessaire est UIModalPresentationOverFullScreen pour le modalPresentationStyle du ViewController présenté. Cela couvrirait également les boutons navigationBar et tabBar.

82
S.P.

Pour ceux qui essaient de faire fonctionner cela dans iOS 8, la méthode "approuvée par Apple" pour afficher un contrôleur de vue modale transparent consiste à définir modalPresentationStylesur le contrôleur presented sur UIModalPresentationOverCurrentContext.

Cela peut être fait en code ou en définissant les propriétés de la séquence dans le storyboard.

A partir de la documentation UIViewController:

UIModalPresentationOverCurrentContext

Un style de présentation où le contenu est affiché uniquement sur le contenu du contrôleur de vue parent. Les vues sous le présenté les contenus ne sont pas supprimés de la hiérarchie des vues lors de la présentation se termine. Donc, si le contrôleur de vue présenté ne remplit pas l'écran avec un contenu opaque, le contenu sous-jacent est visible.

Lors de la présentation d’un contrôleur de vue dans un popover, cette présentation Le style n'est pris en charge que si le style de transition est UIModalTransitionStyleCoverVertical. Tenter d'utiliser un autre Le style de transition déclenche une exception. Cependant, vous pouvez utiliser d'autres styles de transition (sauf la transition curl partielle) si le parent contrôleur de vue n'est pas dans un popover.

Disponible dans iOS 8.0 et versions ultérieures.

https://developer.Apple.com/documentation/uikit/uiviewcontroller

La vidéo «View Controller Advancements in iOS 8» de WWDC 2014 va plus en détail.

Remarque:

  • Assurez-vous de donner à votre contrôleur de vue présenté une couleur d'arrière-plan claire, afin qu'il ne soit pas réellement transparent!
  • Vous devez définir ceci avant la présentation de, c'est-à-dire que définir ce paramètre dans la viewDidLoad du PresentViewController n'aura aucun effet.
179
Jeff C.

Dans iOS 8.0 et versions ultérieures, vous pouvez définir la propriété modalPresentationStyle to UIModalPresentationOverCurrentContext

//Set property **definesPresentationContext** YES to avoid presenting over presenting-viewController's navigation bar

self.definesPresentationContext = YES; //self is presenting view controller
presentedController.view.backgroundColor = [YOUR_COLOR with alpha OR clearColor]
presentedController.modalPresentationStyle = UIModalPresentationOverCurrentContext;

[self presentViewController:presentedController animated:YES completion:nil];

See Image Attached

89
sat20786

Ce code fonctionne bien sur iPhone sous iOS6 et iOS7:

presentedVC.view.backgroundColor = YOUR_COLOR; // can be with 'alpha'
presentingVC.modalPresentationStyle = UIModalPresentationCurrentContext;
[presentingVC presentViewController:presentedVC animated:YES completion:NULL];

Dans ce cas, vous manquez l'animation par diapositives. Pour conserver l'animation, vous pouvez toujours utiliser l'extension "non élégante" suivante:

[presentingVC presentViewController:presentedVC animated:YES completion:^{
    [presentedVC dismissViewControllerAnimated:NO completion:^{
        presentingVC.modalPresentationStyle = UIModalPresentationCurrentContext;
        [presentingVC presentViewController:presentedVC animated:NO completion:NULL];
    }];
}];

Si notre presentV est situé dans UINavigationController ou UITabbarController, vous devez utiliser ces contrôleurs en tant que presentationVC.

De plus, dans iOS7, vous pouvez implémenter une animation de transition personnalisée en appliquant le protocole UIViewControllerTransitioningDelegate. Bien sûr, dans ce cas, vous pouvez obtenir un fond transparent

@interface ModalViewController : UIViewController <UIViewControllerTransitioningDelegate>

Tout d'abord, avant de présenter, vous devez définir modalPresentationStyle

modalViewController.modalPresentationStyle = UIModalPresentationCustom;

Ensuite, vous devez implémenter deux méthodes de protocole

- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
    CustomAnimatedTransitioning *transitioning = [CustomAnimatedTransitioning new];
    transitioning.presenting = YES;
    return transitioning;
}

- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
    CustomAnimatedTransitioning * transitioning = [CustomAnimatedTransitioning new];
    transitioning.presenting = NO;
    return transitioning;
}

La dernière chose à faire est de définir votre transition personnalisée dans la classe CustomAnimatedTransitioning

@interface CustomAnimatedTransitioning : NSObject <UIViewControllerAnimatedTransitioning>
@property (nonatomic) BOOL presenting;
@end

@implementation CurrentContextTransitionAnimator

- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext 
{
    return 0.25;
}

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext 
{
    UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

    if (self.presenting) {
        // custom presenting animation
    }
    else {
        // custom dismissing animation
    }
}
42
malex

J'ai eu un peu de mal avec Interface Builder de XCode 7 pour définir le style de présentation comme suggéré par @VenuGopalTewari. Dans cette version, il semble n'y avoir aucun mode de présentation Over Current Context ou Over Full Screen pour le segue. Ainsi, pour que cela fonctionne, je règle le mode sur Default:

 enter image description here avec  enter image description here

De plus, je règle le mode de présentation du contrôleur de vue à présentation modale sur Over Full Screen:

 enter image description here

17
Bastian

Créez une séquence pour une présentation modale et définissez la propriété Presentation de cette séquence sur le contexte actuelit fonctionnera à 100%

enter image description here

16
Venu Gopal Tewari

PresentViewController avec fond transparent - sous iOS 8 et iOS 9

MYViewController *myVC = [self.storyboard   instantiateViewControllerWithIdentifier:@"MYViewController"];
    myVC.providesPresentationContextTransitionStyle = YES;
    myVC.definesPresentationContext = YES;
    [myVC setModalPresentationStyle:UIModalPresentationOverCurrentContext];
    [self.navigationController presentViewController:myVC animated:YES completion:nil];

Et dans MYViewController, définissez la couleur de fond noire et réduisez l'opacité 

14
Ashwini Chougale

C'est un peu hacky, mais pour moi ce code fonctionne (iOS 6): 

AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];

[self presentViewController:self.signInViewController animated:YES completion:^{
    [self.signInViewController dismissViewControllerAnimated:NO completion:^{
        appDelegate.window.rootViewController.modalPresentationStyle = UIModalPresentationCurrentContext;
        [self presentViewController:self.signInViewController animated:NO completion:nil];
        appDelegate.window.rootViewController.modalPresentationStyle = UIModalPresentationFullScreen;

    }];
}];

Ce code fonctionne aussi sur iPhone

12
Mak

Cette catégorie a fonctionné pour moi (iOS 7, 8 et 9)

Fichier H

@interface UIViewController (navigation)
- (void) presentTransparentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion;
@end

Fichier M

@implementation UIViewController (navigation)
- (void)presentTransparentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion
{
    if(SYSTEM_VERSION_LESS_THAN(@"8.0")) {
        [self presentIOS7TransparentController:viewControllerToPresent withCompletion:completion];

    }else{
        viewControllerToPresent.modalPresentationStyle = UIModalPresentationOverCurrentContext;
         [self presentViewController:viewControllerToPresent animated:YES completion:completion];
    }
}
-(void)presentIOS7TransparentController:(UIViewController *)viewControllerToPresent withCompletion:(void(^)(void))completion
{
    UIViewController *presentingVC = self;
    UIViewController *root = self;
    while (root.parentViewController) {
        root = root.parentViewController;
    }
    UIModalPresentationStyle orginalStyle = root.modalPresentationStyle;
    root.modalPresentationStyle = UIModalPresentationCurrentContext;
    [presentingVC presentViewController:viewControllerToPresent animated:YES completion:^{
        root.modalPresentationStyle = orginalStyle;
    }];
}
@end
11
Ted

Si vous utilisez Storyboard, vous pouvez suivre cette étape:

  1. Ajoutez un contrôleur de vue (V2), configurez l'interface utilisateur comme vous le souhaitez
  • ajouter un UIView - définir l'arrière-plan sur noir et l'opacité sur 0,5
  • ajoutez un autre UIView (2) - qui servira de fenêtre contextuelle (notez que UIView et UIView (2) doivent avoir le même niveau/hiérarchie. Ne faites pas de la vue image l'enfant de la vue sinon l'opacité de l'uiview affecter le UIView (2))
  1. Present V2 Modally

  2. Cliquez sur la segue. Dans l'inspecteur Attributs, définissez la présentation comme en plein écran . Supprimer l'animation si vous aimez

Storyboard

  1. Sélectionnez V2. Dans l'inspecteur Attributs, définissez la présentation comme en plein écran . Vérifier Définit le contexte et fournit le contexte

Storyboard

  1. Sélectionnez le MainView de votre V2 (image de contrôle Pls.). Définissez backgroundColor sur Clear Color

Storyboard

6
dhin

J'ai ajouté ces trois lignes dans la méthode init dans le contrôleur de vue présenté, et fonctionne comme un charme:

self.providesPresentationContextTransitionStyle = YES;
self.definesPresentationContext = YES;
[self setModalPresentationStyle:UIModalPresentationOverCurrentContext];

EDIT (fonctionne sur iOS 9.3):

self.modalPresentationStyle = UIModalPresentationOverFullScreen;

Selon la documentation:

UIModalPresentationOverFullScreen Style de présentation de vue dans lequel la vue présentée couvre l'écran. Les vues sous le contenu présenté ne sont pas supprimées de la hiérarchie des vues lorsque la présentation est terminée. Ainsi, si le contrôleur de vue présenté ne remplit pas l'écran de contenu opaque, le contenu sous-jacent est visible.

Disponible dans iOS 8.0 et versions ultérieures.

6
inigo333

Une autre méthode consiste à utiliser une "vue conteneur". Il suffit de faire alpha en dessous de 1 et intégrer avec seque. XCode 5, cible iOS7. Testé sur iPhone.

enter image description here

Vue du conteneur disponible sur iOS6 . Lien pour publier un article à ce sujet.

4
Mike Glukhov

La solution à cette réponse en utilisant Swift serait la suivante.

let vc = MyViewController()
vc.view.backgroundColor = UIColor.clear // or whatever color.
vc.modalPresentationStyle = .overCurrentContent
present(vc, animated: true, completion: nil)
3
Anthony Dito

J'ai créé un objet pour gérer la présentation de ce que j'appelle un "modal superposé", ce qui signifie qu'il conserve la vue de l'arrière-plan et vous permet d'avoir un modal avec un arrière-plan transparent.

Il a une seule méthode simple qui fait ceci:

- (void)presentViewController:(UIViewController *)presentedViewController
       fromViewController:(UIViewController *)presentingViewController
{
    presentedViewController.modalPresentationStyle = UIModalPresentationCustom;
    presentedViewController.transitioningDelegate = self;
    presentedViewController.modalPresentationCapturesStatusBarAppearance = YES;

    [presentedViewController setNeedsStatusBarAppearanceUpdate];

    [presentingViewController presentViewController:presentedViewController
                                       animated:YES
                                     completion:nil];
}

Il est important de définir la propriété modalPresentationCapturesStatusBarAppearance sur YES et de forcer la mise à jour de l'apparence de la barre d'état, si le contrôleur de vue que vous présentez possède une variable preferredStatusBarStyle différente.

Cet objet devrait avoir un @property (assign, nonatommic) isPresenting

Vous souhaitez que cet objet respecte les protocoles UIViewControllerAnimatedTransitioning et UIViewControllerTransitioningDelegate et implémente les méthodes suivantes:

- (id)animationControllerForPresentedController:(UIViewController *)presented
                           presentingController:(UIViewController *)presenting
                               sourceController:(UIViewController *)source
{
    self.isPresenting = YES;

    return self;
}

- (id)animationControllerForDismissedController:(UIViewController *)dismissed
{
    self.isPresenting = NO;

    return self;
}

et:

- (NSTimeInterval)transitionDuration:(id)transitionContext
{
    return 0.25;
}

- (void)animateTransition:(id)transitionContext
{
    UIViewController* firstVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController* secondVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIView* containerView = [transitionContext containerView];
    UIView* firstView = firstVC.view;
    UIView* secondView = secondVC.view;

    if (self.isPresenting) {
        [containerView addSubview:secondView];
        secondView.frame = (CGRect){
            containerView.frame.Origin.x,
            containerView.frame.Origin.y + containerView.frame.size.height,
            containerView.frame.size
        };

        firstView.tintAdjustmentMode = UIViewTintAdjustmentModeDimmed;
        [UIView animateWithDuration:0.25 animations:^{
            secondView.frame = containerView.frame;
        } completion:^(BOOL finished) {
            [transitionContext completeTransition:YES];
        }];
        } else {
        [UIView animateWithDuration:0.25 animations:^{
            firstView.frame = (CGRect){
                containerView.frame.Origin.x,
                containerView.frame.Origin.y + containerView.frame.size.height,
                containerView.frame.size
        };

        } completion:^(BOOL finished) {
            [transitionContext completeTransition:YES];
        }];
    }
}

Cela fait une animation glissée à partir du bas imitant l'animation modale par défaut, mais vous pouvez en faire ce que vous voulez.

L'important est que la vue du contrôleur de la vue de présentation reste à l'arrière, ce qui vous permet de créer un effet transparent.

Cette solution fonctionne pour iOS 7+

3
Pedro Mancheno

Un moyen très simple de faire cela (en utilisant Storyboards, par exemple) est:

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"SomeStoryboard" bundle:nil];
UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:@"SomeStoryboardViewController"];
// the key for what you're looking to do:
vc.modalPresentationStyle = UIModalPresentationOverCurrentContext;
vc.view.alpha = 0.50f;

[self presentViewController:vc animated:YES completion:^{
    // great success
}];

Ceci présentera une UIViewController dans une Storyboard modale, mais avec un arrière-plan translucide.

3
JaredH

Travailler pour iOS 7-10

if #available(iOS 8.0, *) {
    nextVC.modalPresentationStyle = .OverCurrentContext
    self.presentViewController(nextVC, animated: true, completion: nil)
} else {
    // Fallback on earlier version
    self.modalPresentationStyle = .Custom          
    nextVC.modalTransitionStyle = .CrossDissolve            
    self.presentViewController(nextVC, animated: false, completion: nil)
    }
}
2
iluvatar_GR

Pour récapituler toutes les bonnes réponses et commentaires ici et pour avoir une animation tout en déplaçant votre nouvelle ViewController c'est ce que j'ai fait: (Prise en charge de iOS 6 et plus)

Si vous utilisez une UINavigationController\UITabBarController c'est la voie à suivre:

    SomeViewController *vcThatWillBeDisplayed = [self.storyboard instantiateViewControllerWithIdentifier:@"SomeVC"];

    vcThatWillBeDisplayed.view.backgroundColor = [UIColor colorWithRed: 255/255.0 green:255/255.0 blue:255/255.0 alpha:0.50];    

    self.navigationController.modalPresentationStyle = UIModalPresentationCurrentContext;
    [self presentViewController:presentedVC animated:YES completion:NULL];

Si vous le faites, vous perdrez votre animation modalTransitionStyle. Pour le résoudre, vous pouvez facilement ajouter à votre classe SomeViewController ceci:

-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [UIView animateWithDuration:0.4 animations:^() {self.view.alpha = 1;}
       completion:^(BOOL finished){}];
}
- (void)viewDidLoad
{
    [super viewDidLoad];
    self.view.alpha = 0;
}
2
Segev

Swift 4.2

guard let someVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "someVC") as? someVC else {
    return
}
someVC.modalPresentationStyle = .overCurrentContext

present(someVC, animated: true, completion: nil)
1
Aleksey Shevchenko

Si vous utilisez le mode modal, assurez-vous de le définir comme cette image (vous pouvez désactiver l'animation si vous le souhaitez).enter image description here

1
Ahmed

Bien sûr, vous devez définir UIModalPresentationCurrentContext, mais l'emplacement pour définir clearColor est également très important! Vous ne pouvez pas définir d'arrière-plan dans la fonction viewDidLoad, définissez-le avant le chargement de la vue, comme dans le contrôleur de vue root ou dans la fonction init du contrôleur à afficher!

actionController.view.backgroundColor = [UIColor clearColor];
[self presentViewController:actionController animated:YES completion:nil];

ou

- (instancetype)init {

    self = [super initWithNibName:nil bundle:nil];

    if(self) {
        self.modalPresentationStyle = UIModalPresentationOverCurrentContext;
        [self.view setBackgroundColor:[UIColor clearColor]];
    }

    return self;
}
1
Santiago

Une méthode complète testée sur iOS 7 et iOS 8.

@interface UIViewController (MBOverCurrentContextModalPresenting)

/// @warning Some method of viewControllerToPresent will called twice before iOS 8, e.g. viewWillAppear:.
- (void)MBOverCurrentContextPresentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion;

@end

@implementation UIViewController (MBOverCurrentContextModalPresenting)

- (void)MBOverCurrentContextPresentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {
    UIViewController *presentingVC = self;

    // iOS 8 before
    if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1) {
        UIViewController *root = presentingVC;
        while (root.parentViewController) {
            root = root.parentViewController;
        }

        [presentingVC presentViewController:viewControllerToPresent animated:YES completion:^{
            [viewControllerToPresent dismissViewControllerAnimated:NO completion:^{
                UIModalPresentationStyle orginalStyle = root.modalPresentationStyle;
                if (orginalStyle != UIModalPresentationCurrentContext) {
                    root.modalPresentationStyle = UIModalPresentationCurrentContext;
                }
                [presentingVC presentViewController:viewControllerToPresent animated:NO completion:completion];
                if (orginalStyle != UIModalPresentationCurrentContext) {
                    root.modalPresentationStyle = orginalStyle;
                }
            }];
        }];
        return;
    }

    UIModalPresentationStyle orginalStyle = viewControllerToPresent.modalPresentationStyle;
    if (orginalStyle != UIModalPresentationOverCurrentContext) {
        viewControllerToPresent.modalPresentationStyle = UIModalPresentationOverCurrentContext;
    }
    [presentingVC presentViewController:viewControllerToPresent animated:YES completion:completion];
    if (orginalStyle != UIModalPresentationOverCurrentContext) {
        viewControllerToPresent.modalPresentationStyle = orginalStyle;
    }
}

@end
1
BB9z

Définissez la modalPresentationStyle de la navigation sur UIModalPresentationCustom

et définissez la couleur d'arrière-plan de votre contrôleur de vue présenté en tant que couleur claire.

0
user3693546

dans appdelegate:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [[_window rootViewController]setModalPresentationStyle:UIModalPresentationCurrentContext];
    return YES;
}

dans votre vue d’abord, le contrôleur à partir duquel vous devez charger la vue suivante:

  NextViewController *customvc = [[NextViewController alloc]init];
    [self presentViewController:customvc animated:YES completion:^{

    }];

dans votre nextViewController qui doit être ajouté de manière transparente:

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor clearColor];
    UIView* backView = [[UIView alloc] initWithFrame:self.view.frame];
    backView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.6];
    [self.view insertSubview:backView atIndex:0];
}
0
Zaraki

L'écran de connexion est un modal, ce qui signifie qu'il se trouve en haut de l'écran précédent. Jusqu’à présent, nous avons un arrière-plan flou, mais il n’a rien de flou; c'est juste un fond gris.

Nous devons configurer notre Modal correctement.

cible du lien image

  • Tout d’abord, nous devons modifier l’arrière-plan View Controller de View Controller en Clear color. Cela signifie simplement que cela devrait être transparent. Par défaut, cette vue est blanche.

  • Deuxièmement, nous devons sélectionner la Segue qui mène à l'écran de connexion et, dans l'inspecteur d'attributs, définir la présentation sur le contexte actuel. Cette option est uniquement disponible lorsque la disposition automatique et les classes de taille sont activées.

cible du lien image

0
tinkl