Cómo usar presentModalViewController para crear una vista transparente

80

Estoy mostrando una vista modal con

[self presentModalViewController:controller animated:YES];

Cuando la vista se mueve hacia arriba en la pantalla, es transparente según la configuración del archivo xib desde el que se creó, pero una vez que llena la pantalla, se vuelve opaca.

¿Existe alguna forma de mantener la vista transparente?

Sospecho que la vista sobre la que se coloca no se está renderizando en lugar de que la vista modal se esté volviendo opaca.

Darryl Braaten
fuente
1
Esta pregunta ahora es bastante antigua. Cuando busque respuestas, busque una que se aplique a la versión de iOS que usa.
Darryl Braaten
Este stackoverflow.com/q/27598846/1603234 me hace sonreír, ahora es tu turno :)
Hemang
puede usarviewcontroller.modalPresentationStyle = .FormSheet
Gerald

Respuestas:

63

Su vista sigue siendo transparente, pero una vez que su controlador modal está en la parte superior de la pila, la vista detrás de él está oculta (como es el caso con cualquier controlador de vista superior). La solución es animar manualmente una vista usted mismo; entonces el controlador de vista posterior no estará oculto (ya que no lo habrá 'dejado').

Ben Gottlieb
fuente
Gracias, eso confirma lo que sospechaba.
Darryl Braaten
1
He presentado un informe de error a Apple sobre este problema. Open Radar: openradar.appspot.com/radar?id=1062402
teoría
4
El problema con esto es que la vista presentada no puede tener su propia configuración de autorrotación.
Shizam
85

Después de iOS 3.2, existe un método para hacer esto sin ningún "truco"; consulte la documentación de la propiedad modalPresentationStyle . Tiene un rootViewController que presentará el viewController. Así que aquí está el código del éxito:

viewController.view.backgroundColor = [UIColor clearColor];
rootViewController.modalPresentationStyle = UIModalPresentationCurrentContext;
[rootViewController presentModalViewController:viewController animated:YES];

Con este método, el fondo de viewController será transparente y el rootViewController subyacente será visible. Tenga en cuenta que esto solo parece funcionar en el iPad, consulte los comentarios a continuación.

Posibilidades infinitas
fuente
1
Hola ... +1 de mi lado porque funciona bien en iOS 4 y iPad. pero cómo funcionará en iPhone 3.0. Gracias
Mitesh Khatri
3
No me funciona (iOS 5.0), ¿alguien puede mostrar algún código?
Daniel
6
Esto no parece funcionar en iPhone / iPod, solo iPad (consulte la documentación vinculada en la respuesta). @simpleBob: ¿quizás por eso no funciona para ti?
Mac
1
FYI: He hecho la misma pregunta en esta publicación: stackoverflow.com/questions/14419395/…
The Crazy Chimp
1
Probé en iOS 8, y lo tengo que usar Over Current Contextcomo estilo de presentación.
haxpor
48

Lo que necesitaba para que esto funcionara:

self.window.rootViewController.modalPresentationStyle = UIModalPresentationCurrentContext;
medianoche David
fuente
1
funciona de forma sencilla, gracias, acabo de mantener mi código activado - (BOOL) aplicación: (UIApplication *) aplicación didFinishLaunchingWithOptions: (NSDictionary *) método launchOptions y listo
Kshitiz Ghimire
2
También puede configurar el modalPresentationStyleen el controlador de vista de presentación (en lugar de en el de la ventana rootViewController).
mbinna
¡¡Gracias!! incluso funciona cuando lo hace, [self performSegueWithIdentifier: @ "open" sender: self];
DogCoffee
Siempre puede animar la vista a mano en viewdidload del modal.
Jacob K
2
Tenga en cuenta que, como dijo el Sr. T a continuación, modalPresentationStyle debe configurarse en el controlador de vista que actualmente tiene la pantalla completa.
jessepinho
24

Para aquellos que quieran ver algo de código, esto es lo que agregué al controlador de mi vista transparente:

// Add this view to superview, and slide it in from the bottom
- (void)presentWithSuperview:(UIView *)superview {
    // Set initial location at bottom of superview
    CGRect frame = self.view.frame;
    frame.origin = CGPointMake(0.0, superview.bounds.size.height);
    self.view.frame = frame;
    [superview addSubview:self.view];            

    // Animate to new location
    [UIView beginAnimations:@"presentWithSuperview" context:nil];
    frame.origin = CGPointZero;
    self.view.frame = frame;
    [UIView commitAnimations];
}

// Method called when removeFromSuperviewWithAnimation's animation completes
- (void)animationDidStop:(NSString *)animationID
                finished:(NSNumber *)finished
                 context:(void *)context {
    if ([animationID isEqualToString:@"removeFromSuperviewWithAnimation"]) {
        [self.view removeFromSuperview];
    }
}

// Slide this view to bottom of superview, then remove from superview
- (void)removeFromSuperviewWithAnimation {
    [UIView beginAnimations:@"removeFromSuperviewWithAnimation" context:nil];

    // Set delegate and selector to remove from superview when animation completes
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];

    // Move this view to bottom of superview
    CGRect frame = self.view.frame;
    frame.origin = CGPointMake(0.0, self.view.superview.bounds.size.height);
    self.view.frame = frame;

    [UIView commitAnimations];    
}
Kristopher Johnson
fuente
El único problema que tengo con esta solución es la gestión de la memoria. Por ejemplo: TestViewController * testView = [[TestViewController alloc] initWithNibName: @ "TestView" bundle: nil]; [testView presentWithSuperview: self.navigationController.view]; Funcionará bien, sin embargo, si intenta liberar o liberar automáticamente la Vista, cualquier llamada a métodos dentro de esa vista resultará en excepciones de acceso incorrecto
Mick Walker
¿Estás seguro? Creo que la vista será retenida por [superview addSubview:], por lo que debería poder liberarla.
Kristopher Johnson
Si llama a presentWithSuperview y luego suelte testView, se bloquea. También muestra pantallas "debajo" de cualquier barra de herramientas de NavigationController, etc.
Ryan Booker
Esto me ha funcionado bien durante mucho tiempo, pero ya no funciona en modo horizontal. ¿Alguien tiene este mismo problema?
Jeffry van de Vuurst
19

La forma aprobada por Apple de hacer esto en iOS 8 es establecer modalPresentationStyle en 'UIModalPresentationOverCurrentContext'.

De la documentación de UIViewController:

UIModalPresentationOverCurrentContext

Un estilo de presentación donde el contenido se muestra solo sobre el contenido del controlador de vista principal. Las vistas debajo del contenido presentado no se eliminan de la jerarquía de vistas cuando finaliza la presentación. Entonces, si el controlador de vista presentado no llena la pantalla con contenido opaco, el contenido subyacente se muestra.

Al presentar un controlador de vista en una ventana emergente, este estilo de presentación solo se admite si el estilo de transición es UIModalTransitionStyleCoverVertical. Intentar utilizar un estilo de transición diferente desencadena una excepción. Sin embargo, puede usar otros estilos de transición (excepto la transición de curvatura parcial) si el controlador de la vista principal no está en una ventana emergente.

Disponible en iOS 8.0 y posterior.

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/

El video 'Ver los avances del controlador en iOS 8' de la WWDC 2014 trata esto con cierto detalle.

Asegúrese de darle al controlador de vista presentado un color de fondo claro (de lo contrario, seguirá apareciendo opaco).

Jeff C.
fuente
me da una pantalla negra en iOS8 y iOS9
Amit Hooda
En realidad, funciona si configura la vista del controlador de vista con un color de fondo con un valor alfa por debajo de 1
Pierre
16

Hay otra opción: antes de mostrar el controlador modal, capture una captura de pantalla de toda la ventana. Inserte la imagen capturada en un UIImageView y agregue la vista de imagen a la vista del controlador que está a punto de mostrar. Luego envía al reverso. Inserte otra vista sobre la vista de la imagen (fondo negro, alfa 0.7). Muestre su controlador modal y parece que es transparente. Lo acabo de probar en iPhone 4 con iOS 4.3.1. Como encanto.

Krumelur
fuente
bueno, lo implementé, fue genial para mí. Agregue mi código que sigue este concepto.
Ishu
¡Nadie dijo que iba a ser fácil! :-)
Krumelur
¿Cómo apoyaría la rotación de dispositivos con este enfoque?
Kyle Clegg
10

esto es bastante antiguo, pero resolví el mismo problema de la siguiente manera: como necesito presentar un controlador de navegación en el iPhone, agregar una subvista no era una solución viable.

Entonces, lo que hice:

1) Antes de presentar el controlador de vista, tome una captura de pantalla de su pantalla actual:

UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, self.view.opaque, 0.0);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * backgroundImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

2) Cree el controlador de vista que desea presentar y agregue el fondo como una subvista, enviándolo al fondo.

UIViewController * presentingVC = [UIViewController new];
UIImageView * backgroundImageOfPreviousScreen = [[UIImageView alloc] initWithImage:backgroundImage];
[presentingVC.view addSubview:backgroundImageOfPreviousScreen];
[presentingVC.view sendSubviewToBack:backgroundImageOfPreviousScreen];

3) Presente su controlador de vista, pero antes de eso en el nuevo controlador de vista, agregue una vista transparente en viewDidLoad (usé ILTranslucentView)

-(void)viewDidLoad
{
    [super viewDidLoad];
    ILTranslucentView * translucentView = [[ILTranslucentView alloc] initWithFrame:self.view.frame];
    [self.view addSubview:translucentView];
    [self.view sendSubviewToBack:translucentView];
}

¡Y eso es todo!

lucaslt89
fuente
Increíble. Todo funcionó bien al lado de la vista translúcida que provocó la desaparición de la captura de pantalla.
Michal Shatz
¡Esta es una gran solución! Es mejor mantener el código del controlador para la ventana emergente separado del controlador de vista principal.
1kmonkies
Para mí funcionó, pero el fondo de configuración tenía que estar a la vista WillAppear
Leszek Zarna
5

Escribí mis hallazgos sobre esto en una pregunta diferente , pero la esencia es que debes llamar modalPresentationStyle = UIModalPresentationCurrentContexta quien sea que tenga la pantalla completa en este momento. La mayoría de las veces, es lo que sea el rootViewController de [UIApplication sharedApplication] .delegate.window. También podría ser un nuevo UIViewController que se presentó modalPresentationStyle = UIModalPresentationFullScreen.

Por favor, lea mi otra publicación mucho más detallada si se pregunta cómo resolví este problema específicamente. ¡Buena suerte!

Sr. T
fuente
5

Esto parece estar roto en IOS 8, estoy usando un controlador de navegación y el contexto que se muestra es el contexto de los menús de navegación, que en nuestro caso es un controlador de menú deslizante.

Estamos usando el pod 'TWTSideMenuViewController', '0.3' no ha verificado si esto es un problema con la biblioteca o el método descrito anteriormente.

Constantino
fuente
5

Esto funcionó para mí en iOS 8-9:

1- Configura el fondo de tu controlador de vista con un alfa

2- agrega este código:

TranslucentViewController *tvc = [[TranslucentViewController alloc] init];
self.providesPresentationContextTransitionStyle = YES;
self.definesPresentationContext = YES;
[tvc setModalPresentationStyle:UIModalPresentationOverCurrentContext];

[self.navigationController presentViewController:tvc animated:YES completion:nil];
mkai
fuente
4

Sé que esta es una pregunta bastante antigua. Estaba atascado en este problema y pude obtener una pista de este hilo. Así que poniendo aquí cómo lo hice funcionar :). Estoy usando un guión gráfico y tengo una transición al ViewController que se va a presentar. El controlador de vista tiene un color de fondo transparente. Ahora, en el inspector de atributos del segue, configuré la presentación en "Sobre el contexto actual". Y funcionó para mí. Estoy desarrollando para iPhone.

Inspector de atributos del segue

Rojil Thomas
fuente
gracias, también funciona con el set UIModalPresentationStyle.OverCurrentContext programáticamente
guoleii
3

Creé la biblioteca de soruce abierta MZFormSheetController para presentar la hoja de formulario modal en UIWindow adicional. Puede usarlo para presentar el controlador de vista modal de transparencia, incluso ajustar el tamaño del controlador de vista presentado.

Michal Zaborowski
fuente
¿Puedes explicar cómo se usa? Porque cuando intento invocar el código, causa SIGABRT.
Matrosov Alexander
3

En mi condición, estoy viendo el mismo viewController. Así que cree un nuevo controlador de vista para mantener UIView. Haga que esa vista sea transparente estableciendo su propiedad alfa. Luego, en un botón, haga clic en y escribí este código. Se ve bien.

UIGraphicsBeginImageContext(objAppDelegate.window.frame.size);
    [objAppDelegate.window.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();



    UIViewController *controllerForBlackTransparentView=[[[UIViewController alloc] init] autorelease];
    [controllerForBlackTransparentView setView:viewForProfanity];

    UIImageView *imageForBackgroundView=[[UIImageView alloc] initWithFrame:CGRectMake(0, -20, 320, 480)];
    [imageForBackgroundView setImage:viewImage];

    [viewForProfanity insertSubview:imageForBackgroundView atIndex:0];

    [self.navigationController presentModalViewController:controllerForBlackTransparentView animated:YES];

Y muestra lo que quiero. Espero que ayude a alguien.

Ishu
fuente
2

Aquí hay una categoría que he creado que resolverá el problema.

    //
    //  UIViewController+Alerts.h
    //

    #import <UIKit/UIKit.h>

    @interface UIViewController (Alerts)

    - (void)presentAlertViewController:(UIViewController *)alertViewController animated:(BOOL)animated;
    - (void)dismissAlertViewControllerAnimated:(BOOL)animated;

    @end


    //
    //  UIViewController+Alerts.m
    //

    #import "UIViewController+Alerts.h"

    @implementation UIViewController (Alerts)

    - (void)presentAlertViewController:(UIViewController *)alertViewController animated:(BOOL)animated
    {
            // Setup frame of alert view we're about to display to just off the bottom of the view
            [alertViewController.view setFrame:CGRectMake(0, self.view.frame.size.height, alertViewController.view.frame.size.width, alertViewController.view.frame.size.height)];

            // Tag this view so we can find it again later to dismiss
            alertViewController.view.tag = 253;

            // Add new view to our view stack
            [self.view addSubview:alertViewController.view];

            // animate into position
            [UIView animateWithDuration:(animated ? 0.5 : 0.0) animations:^{
                    [alertViewController.view setFrame:CGRectMake(0, (self.view.frame.size.height - alertViewController.view.frame.size.height) / 2, alertViewController.view.frame.size.width, alertViewController.view.frame.size.height)];
            }];      
    }

    - (void)dismissAlertViewControllerAnimated:(BOOL)animated
    {
            UIView *alertView = nil;

            // find our tagged view
            for (UIView *tempView in self.view.subviews)
            {
                    if (tempView.tag == 253)
                    {
                            alertView = tempView;
                            break;
                    }
            }

            if (alertView)
            {
                    // clear tag
                    alertView.tag = 0;

                    // animate out of position
                    [UIView animateWithDuration:(animated ? 0.5 : 0.0) animations:^{
                            [alertView setFrame:CGRectMake(0, self.view.frame.size.height, alertView.frame.size.width, alertView.frame.size.height)];
                    }];      
            }
    }

    @end
Dave Wood
fuente
4
Para su información, no es necesario recorrer las subvistas por su cuenta en el método de descarte. El marco lo hará por usted con [self.view viewWithTag: 253];
SteveB
La categoría de Dave Wood en este ejemplo muestra su vista tal como está. Por lo tanto, también debe establecer el fondo de la configuración Alfa de la vista de su xib en un valor inferior a 1 para obtener el efecto translúcido.
Basil Bourque
Esta categoría tiene un problema cuando se presenta sobre una vista desplazable, como una vista de tabla: el desplazamiento funciona. Eso es malo. El usuario puede desplazar la vista presentada fuera de la pantalla. El contenido de la vista de presentación subyacente aparece en la pantalla.
Basil Bourque
Desde entonces he actualizado esta categoría. Puede obtener el código aquí: gist.github.com/4423365 . Lo uso sobre vistas de tabla y vistas de colección sin el problema de desplazamiento que describe.
Dave Wood
2

Después de mucha investigación, parece que esto resolverá nuestro problema y cumplirá nuestro propósito.

cree una transición desde el VC de origen al VC de destino con un identificador.

por ejemplo, "goToDestinationViewController" está bien para facilitar la vida, consideremos el controlador de vista actual, es decir, el que desea detrás de su vista transparente como origen y el destino como destino

Ahora en fuente VC en viewDidLoad: o vista

performSegueWithIdentifier("goToDestinationViewController", sender: nil)

bueno, estamos a mitad de camino. Ahora ve a tu guión gráfico. Haga clic en el segue. que debería verse así: segue

cambie las opciones a lo que se muestra.

Ahora viene la verdadera solución.

en la vista de destino del controlador viewDidLoad agregue este código.

self.modalPresentationStyle = .Custom

.................................................. .......................ASÍ DE FÁCIL......................... .........................................

Karthik Vinnakota
fuente
1
La configuración self.modalPresentationStyle = .customfunciona para mí en la presentación modal de un controlador de vista real con un fondo transparente en Swift 3, Xcode 8. El único problema es que si el controlador de vista de presentación está incrustado en un controlador de barra de pestañas, la barra de pestañas aparece frente al transparente vista de fondo.
mtso
Por lo tanto, si se usa una barra de pestañas, el controlador de la vista de presentación tabBarController.tabBar.isHiddendebe establecerse en true.
mtso
1

Una forma alternativa es utilizar una "vista de contenedor". Simplemente haga alfa por debajo de 1 e incruste con seque. XCode 5, objetivo iOS7.

no se puede mostrar la imagen, no hay suficiente reputación)))

Vista de contenedor disponible desde iOS6.

Mike Glukhov
fuente
1

Este código funciona bien en iPhone con iOS6 e iOS7:

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

Pero a lo largo de este camino se pierde la animación de "deslizamiento desde abajo".

malex
fuente
Esto no funciona para mí en iPhone. La vista inferior aún desaparece. Creo que modalPresentationStyle solo se aplica a iPad.
jessepinho
Si quieres puedo enviarte pantallas del efecto resultante. Tal vez algo esté mal con su implementación.
malex
Ah ... fue porque no sabía que el controlador de vista tenía que ser de pantalla completa. (Ver comentario del Sr. T.)
jessepinho
1

¡Encontré esta solución elegante y simple para iOS 7 y superior!

Para iOS 8, Apple agregó UIModalPresentationOverCurrentContext, pero no funciona para iOS 7 y versiones anteriores, por lo que no pude usarlo para mi caso.

Por favor, cree la categoría y ponga el siguiente código.

archivo .h

typedef void(^DismissBlock)(void);

@interface UIViewController (Ext)

- (DismissBlock)presentController:(UIViewController *)controller
              withBackgroundColor:(UIColor *)color
                         andAlpha:(CGFloat)alpha
                presentCompletion:(void(^)(void))presentCompletion;

@end

archivo .m

#import "UIViewController+Ext.h"

@implementation UIViewController (Ext)

- (DismissBlock)presentController:(UIViewController *)controller
              withBackgroundColor:(UIColor *)color
                         andAlpha:(CGFloat)alpha
                presentCompletion:(void(^)(void))presentCompletion
{
    controller.modalPresentationStyle = UIModalPresentationCustom;

    UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
    __block UIView *overlay = [[UIView alloc] initWithFrame:keyWindow.bounds];
    if (color == nil) {
        color = [UIColor blackColor];
    }
    overlay.backgroundColor = color;
    overlay.alpha = alpha;

    if (self.navigationController != nil) {
        [self.navigationController.view addSubview:overlay];
    }
    else if (self.tabBarController != nil) {
        [self.tabBarController.view addSubview:overlay];
    }
    else {
        [self.view addSubview:overlay];
    }

    self.modalPresentationStyle = UIModalPresentationCurrentContext;
    [self presentViewController:controller
                       animated:true
                     completion:presentCompletion];

    DismissBlock dismissBlock = ^(void) {
        [self dismissViewControllerAnimated:YES completion:nil];
        [UIView animateWithDuration:0.25
                         animations:^{
                             overlay.alpha = 0;
                         } completion:^(BOOL finished) {
                             [overlay removeFromSuperview];
                         }];
    };
    return dismissBlock;
}

@end

Nota: también funciona para navigationContoller, tabBarController.

Ejemplo de uso:

       // Please, insure that your controller has clear background
       ViewController *controller = [ViewController instance];
        __block DismissBlock dismissBlock = [self presentController:controller
                                                withBackgroundColor:[UIColor blackColor]
                                                           andAlpha:0.5
                                                  presentCompletion:nil];
        // Supposed to be your controller's closing callback
        controller.dismissed = ^(void) {
            dismissBlock();
        };

¡Disfrútala! y por favor, deja algunos comentarios.

Karen Lusinyan
fuente
0

Esta es la mejor y más limpia forma que encontré hasta ahora:

@protocol EditLoginDelegate <NSObject>

- (void)dissmissEditLogin;

@end

- (IBAction)showtTransparentView:(id)sender {

    UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"foo bar"
                                                         delegate:self
                                                cancelButtonTitle:@"cancel"
                                           destructiveButtonTitle:@"destructive"
                                                otherButtonTitles:@"ok", nil];

    [actionSheet showInView:self.view];

}

- (void)willPresentActionSheet:(UIActionSheet *)actionSheet{

    UIStoryboard *loginStoryboard     = [UIStoryboard storyboardWithName:@"Login" bundle:nil];
    self.editLoginViewController      = [loginStoryboard instantiateViewControllerWithIdentifier:@"EditLoginViewController"];

    self.editLoginViewController.delegate = self;

    [self.editLoginViewController viewWillAppear:NO];
    [actionSheet addSubview:self.editLoginViewController.view];
    [self.editLoginViewController viewDidAppear:NO];
}
MUH Mobile Inc.
fuente
0

Intento usar varios métodos para resolver, pero aún así fallé, el siguiente código se implementó finalmente.

La resolución de Swift:

// A.swift init method
modalPresentationStyle = .currentContext // or overCurrentContent
modalTransitionStyle = .crossDissolve // dissolve means overlay

luego en el controlador de vista B:

// B.swift
let a = A()
self.present(a, animated: true, completion: nil)
Jone
fuente
Tienes que hacer que el backgroundColor de la vista de ViewController sea transparente también. view.backgroundColor = UIColor.clear
Michael Henry