Jak używać presentModalViewController do tworzenia przezroczystego widoku


80

Wyświetlam widok modalny z

[self presentModalViewController:controller animated:YES];

Kiedy widok przesuwa się w górę ekranu, jest przezroczysty zgodnie z ustawieniem w pliku xib, z którego jest tworzony, ale po wypełnieniu ekranu staje się nieprzezroczysty.

Czy w ogóle można zachować przejrzystość widoku?

Podejrzewam, że widok, nad którym jest umieszczany, nie jest renderowany, a nie, że widok modalny staje się nieprzejrzysty.


1
To pytanie jest teraz dość stare. Szukając odpowiedzi, poszukaj odpowiedzi, która dotyczy używanej wersji systemu iOS.
Darryl Braaten

Ten stackoverflow.com/q/27598846/1603234 sprawia, że ​​się uśmiecham, teraz twoja kolej :)
Hemang,

możesz użyćviewcontroller.modalPresentationStyle = .FormSheet
Gerald

Odpowiedzi:


63

Twój widok jest nadal przezroczysty, ale gdy kontroler modalny znajduje się na szczycie stosu, widok za nim jest ukryty (tak jak w przypadku każdego kontrolera widoku znajdującego się najwyżej). Rozwiązaniem jest samodzielne ręczne animowanie widoku; wtedy behind-viewController nie będzie ukryty (ponieważ nie zostawisz go).


Dzięki, to potwierdza to, co podejrzewałem.
Darryl Braaten

1
Złożyłem raport o błędzie w Apple dotyczący tego problemu. Otwarty radar: openradar.appspot.com/radar?id=1062402
teoria

4
Problem polega na tym, że prezentowany widok nie może mieć własnych ustawień autorotacji.
Shizam

85

Po iOS 3.2 istnieje metoda, która pozwala to zrobić bez żadnych „sztuczek” - zobacz dokumentację dotyczącą właściwości modalPresentationStyle . Masz rootViewController, który przedstawi ViewController. Oto kod do sukcesu:

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

Dzięki tej metodzie tło viewControllera będzie przezroczyste, a bazowy rootViewController będzie widoczny. Należy pamiętać, że wydaje się, że działa to tylko na iPadzie, patrz komentarze poniżej.


1
Cześć ... +1 z mojej strony bcoz działa dobrze na iOS 4 i iPadzie. ale jak to będzie działać w iPhonie 3.0. Dzięki
Mitesh Khatri

3
U mnie nie działa (iOS 5.0), czy ktoś może pokazać jakiś kod?
Daniel,

6
Wygląda na to, że nie działa na iPhonie / iPodzie, tylko na iPadzie (zobacz dokumentację, do której link znajduje się w odpowiedzi). @simpleBob: może dlatego to nie działa dla Ciebie?
Mac,

1
FYI: Zadałem to samo pytanie w tym poście: stackoverflow.com/questions/14419395/…
The Crazy Chimp

1
Testowałem na iOS 8 i muszę użyć Over Current Contextjako stylu prezentacji.
haxpor

48

Co potrzebowałem, aby to zadziałało:

self.window.rootViewController.modalPresentationStyle = UIModalPresentationCurrentContext;

1
on proste prace, dzięki Właśnie dotrzymałem kodu - (BOOL) Zastosowanie: (*) didFinishLaunchingWithOptions UIApplication Zastosowanie: (NSDictionary *) metodę launchOptions i voila
Kshitiz Ghimire

2
Możesz także ustawić po prostu modalPresentationStylena kontrolerze widoku prezentacji (zamiast na oknie rootViewController).
mbinna

Dzięki!! działa nawet wtedy, gdy to zrobisz, [self performSegueWithIdentifier: @ "open" sender: self];
DogCoffee

Zawsze możesz ręcznie animować widok w viewdidload modalu.
Jacob K

2
Zauważ, że, jak powiedział pan T poniżej, modalPresentationStyle musi być ustawiony na kontrolerze widoku, który obecnie ma pełny ekran.
jessepinho

24

Dla tych, którzy chcą zobaczyć kod, oto co dodałem do kontrolera mojego przezroczystego widoku:

// 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];    
}

Jedyny problem jaki mam z tym rozwiązaniem to zarządzanie pamięcią. Na przykład: TestViewController * testView = [[TestViewControllerocation] initWithNibName: @ "TestView" bundle: nil]; [testView presentWithSuperview: self.navigationController.view]; Będzie działać dobrze, jednak jeśli spróbujesz zwolnić lub automatycznie zwolnić Widok, wszelkie wywołania metod w tym widoku spowodują wyjątki złego dostępu
Mick Walker

Jesteś pewny? Myślę, że widok zostanie zachowany przez [superview addSubview:], więc powinieneś móc go zwolnić.
Kristopher Johnson,

Jeśli wywołasz presentWithSuperview, a następnie zwolnisz testView, nastąpi awaria. Wyświetla również „pod” dowolnymi paskami narzędzi NavigationController itp.
Ryan Booker

To działało dobrze przez długi czas, ale nie działa już w trybie poziomym. Czy ktoś ma ten sam problem?
Jeffry van de Vuurst

19

Zatwierdzonym przez Apple sposobem na zrobienie tego w systemie iOS 8 jest ustawienie modalPresentationStyle na „UIModalPresentationOverCurrentContext”.

Z dokumentacji UIViewController:

UIModalPresentationOverCurrentContext

Styl prezentacji, w którym zawartość jest wyświetlana tylko nad zawartością nadrzędnego kontrolera widoku. Widoki poniżej prezentowanej treści nie są usuwane z hierarchii widoków po zakończeniu prezentacji. Jeśli więc prezentowany kontroler widoku nie wypełnia ekranu nieprzezroczystą treścią, pojawia się podstawowa zawartość.

Podczas prezentowania kontrolera widoku w popover ten styl prezentacji jest obsługiwany tylko wtedy, gdy styl przejścia to UIModalTransitionStyleCoverVertical. Próba użycia innego stylu przejścia wyzwala wyjątek. Możesz jednak użyć innych stylów przejścia (z wyjątkiem częściowego przejścia zwijanego), jeśli nadrzędny kontroler widoku nie znajduje się w oknie popover.

Dostępne w iOS 8.0 i nowszych.

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

Film „Zobacz postępy kontrolera w iOS 8” z WWDC 2014 omawia to bardziej szczegółowo.

Upewnij się, że kontroler prezentowanego widoku ma wyraźny kolor tła (w przeciwnym razie nadal będzie on nieprzezroczysty).


daje mi czarny ekran na iOS8 i iOS9
Amit Hooda

Właściwie to działa, jeśli ustawisz widok kontrolera widoku na kolor tła z wartością alfa poniżej 1
Pierre

16

Jest jeszcze jedna opcja: przed pokazaniem kontrolera modalnego zrób zrzut ekranu całego okna. Wstaw przechwycony obraz do UIImageView i dodaj widok obrazu do widoku kontrolera, który chcesz pokazać. Następnie odeślij do tyłu. Wstaw inny widok nad widokiem obrazu (czarne tło, alfa 0,7). Pokaż swój kontroler modalny i wygląda na to, że był przezroczysty. Właśnie wypróbowałem to na iPhonie 4 z systemem iOS 4.3.1. Jak urok.


dobry, wdrażam to, było dla mnie świetne. dodaj mój kod, który jest zgodny z tą koncepcją.
Ishu,

Nikt nie powiedział, że to będzie łatwe! :-)
Krumelur

W jaki sposób wspierałbyś rotację urządzeń przy takim podejściu?
Kyle Clegg,

10

to jest dość stare, ale rozwiązałem ten sam problem w następujący sposób: ponieważ muszę zaprezentować kontroler nawigacyjny w iPhonie, dodanie widoku podrzędnego nie było opłacalnym rozwiązaniem.

Więc co zrobiłem:

1) Przed zaprezentowaniem kontrolera widoku zrób zrzut ekranu bieżącego ekranu:

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

2) Utwórz kontroler widoku, który chcesz zaprezentować, i dodaj tło jako widok podrzędny, wysyłając je z powrotem.

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

3) Przedstaw swój kontroler widoku, ale przed tym w nowym kontrolerze widoku dodaj przezroczysty widok w viewDidLoad (użyłem ILTranslucentView)

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

I to wszystko!


Niesamowite. Wszystko działało dobrze poza przezroczystym widokiem, który spowodował zniknięcie zrzutu ekranu.
Michal Shatz

To świetne rozwiązanie! Lepiej jest przechowywać kod kontrolera wyskakującego okienka oddzielnie od nadrzędnego kontrolera widoku.
1kmonkies

U mnie działało, ale tło musiało być widoczne WillAppear
Leszek Zarna

5

Spisałem swoje ustalenia na ten temat w innym pytaniu , ale sedno jest takie, że musisz wywołać modalPresentationStyle = UIModalPresentationCurrentContextwszystko, co w tej chwili posiada pełny ekran. W większości przypadków jest to coś, czym jest rootViewController [UIApplication sharedApplication] .delegate.window. Może to być również nowy kontroler UIViewController, który został przedstawiony modalPresentationStyle = UIModalPresentationFullScreen.

Przeczytaj mój inny, znacznie bardziej szczegółowy post, jeśli zastanawiasz się, jak konkretnie rozwiązałem ten problem. Powodzenia!


5

Wygląda na to, że jest zepsuty w IOS 8, używam kontrolera nawigacyjnego, a kontekst, który jest wyświetlany, to kontekst menu nawigacji, który w naszym przypadku jest przesuwanym kontrolerem menu.

Używamy pod 'TWTSideMenuViewController', '0.3' nie sprawdziliśmy, czy jest to problem z biblioteką, czy z metodą opisaną powyżej.


5

To zadziałało w iOS 8-9:

1- Ustaw tło kontrolera widoku za pomocą alfy

2- Dodaj ten kod:

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

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

4

Wiem, że to dość stare pytanie. Utknąłem w tej kwestii i udało mi się uzyskać trop z tego wątku. Więc umieszczając tutaj jak to zrobiłem, zadziałało :). Używam storyboardu i mam przejście do ViewController, który ma zostać przedstawiony. Kontroler widoku ma przezroczysty kolor tła. Teraz w inspektorze atrybutów ścieżki ustawiłem prezentację na „Over current context”. I to zadziałało. Tworzę dla iPhone'a.

Inspektor atrybutów ścieżki


dzięki, działa również programowo przez set UIModalPresentationStyle.OverCurrentContext
guoleii

3

Stworzyłem otwartą bibliotekę MZFormSheetController w celu zaprezentowania modalnego arkusza formularza na dodatkowym UIWindow. Możesz go użyć do prezentacji kontrolera widoku przezroczystości, a nawet dostosować rozmiar kontrolera prezentowanego widoku.


Czy możesz wyjaśnić, jak go używać? Ponieważ kiedy próbuję wywołać kod, powoduje to SIGABRT.
Matrosov Alexander

3

W moim stanie mam widok na ten sam viewController. Stwórz więc nowy kontroler widoku do przechowywania UIView. Uczyń ten widok przezroczystym, ustawiając jego właściwość alfa. Następnie jednym kliknięciem napisałem ten kod. Wygląda dobrze.

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];

I pokazuje, czego chcę. mam nadzieję, że to komuś pomoże.


2

Oto kategoria, którą utworzyłem, która rozwiąże problem.

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

4
Do Twojej wiadomości, nie musisz samodzielnie przechodzić przez podglądy w metodzie zwalniania. Framework zrobi to za Ciebie z [self.view viewWithTag: 253];
SteveB

Kategoria Dave'a Wooda w tym przykładzie wyświetla Twój widok tak, jak jest. Musisz więc również ustawić tło alfa widoku xib na mniej niż 1, aby uzyskać efekt półprzezroczystości.
Basil Bourque

W tej kategorii występuje problem podczas prezentowania w przewijalnym widoku, takim jak widok tabeli: Przewijanie działa. To źle. Użytkownik może przewijać prezentowany widok poza ekranem. Zawartość widoku prezentacji pojawi się na ekranie.
Basil Bourque

Od tego czasu zaktualizowałem tę kategorię. Możesz pobrać kod tutaj: gist.github.com/4423365 . Używam go do widoków tabel i widoków kolekcji bez problemu przewijania, który opisujesz.
Dave Wood

2

Po wielu badaniach wygląda na to, że rozwiąże to nasz problem i spełni nasz cel.

utwórz wypływ ze źródłowego VC do docelowego VC z identyfikatorem.

na przykład „goToDestinationViewController” w porządku, aby ułatwić życie, rozważmy bieżący kontroler widoku, tj. ten, który chcesz za przezroczystym widokiem jako źródło i miejsce docelowe jako miejsce docelowe

Teraz w źródłowym VC w viewDidLoad: lub view

performSegueWithIdentifier("goToDestinationViewController", sender: nil)

dobrze, że jesteśmy w połowie. Teraz przejdź do swojego storyboardu. Kliknij ścieżkę. który powinien wyglądać tak: segue

zmień opcje na pokazane.

Teraz jest prawdziwe rozwiązanie.

w widoku docelowym kontrolera viewDidLoad dodaj ten kod.

self.modalPresentationStyle = .Custom

.................................................. .......................TAK PROSTE......................... .........................................


1
Ustawienie self.modalPresentationStyle = .customdziała dla mnie modalnie prezentując rzeczywisty kontroler widoku z przezroczystym tłem w Swift 3, Xcode 8. Jedynym problemem jest to, że jeśli kontroler widoku prezentacji jest osadzony w kontrolerze paska zakładek, pasek zakładek pojawia się przed przezroczystym widok tła.
mtso

Zatem - jeśli używany jest pasek kart - kontroler widoku prezentacji tabBarController.tabBar.isHiddenpowinien być ustawiony na true.
mtso

1

Alternatywnym sposobem jest użycie „widoku kontenera”. Wystarczy zrobić alfa poniżej 1 i osadzić za pomocą seque. XCode 5, docelowo iOS7.

nie można pokazać obrazu, za mało reputacji)))

Widok kontenera dostępny od iOS6.


1

Ten kod działa dobrze na iPhonie pod iOS6 i iOS7:

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

Ale w ten sposób tracisz animację „przesuwania od dołu”.


To nie działa dla mnie na iPhonie. Widok od spodu nadal znika. Uważam, że modalPresentationStyle ma zastosowanie tylko do iPada.
jessepinho

Jeśli chcesz, mogę wysłać screeny z uzyskanym efektem. Może coś jest nie tak z Twoją implementacją.
malex

Ach ... to dlatego, że nie wiedziałem, że kontroler widoku musi być na pełnym ekranie. (Zobacz komentarz pana T.)
jessepinho

1

Znalazłem to eleganckie i proste rozwiązanie na iOS 7 i nowsze!

Dla iOS 8 Apple dodał UIModalPresentationOverCurrentContext, ale nie działa na iOS 7 i wcześniejszych, więc nie mogłem go użyć w moim przypadku.

Utwórz kategorię i umieść poniższy kod.

plik .h

typedef void(^DismissBlock)(void);

@interface UIViewController (Ext)

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

@end

plik .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

Uwaga: działa również dla navigationContoller, tabBarController.

Przykład użycia:

       // 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();
        };

Ciesz się tym! i proszę zostawić informacje zwrotne.


0

To najlepszy i najczystszy sposób, jaki do tej pory znalazłem:

@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];
}


0

Próbuję użyć wielu metod do rozwiązania, ale nadal nie udało mi się, następujący kod został wdrożony w końcu.

Rezolucja Swift:

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

następnie w B wyświetl kontroler :

// B.swift
let a = A()
self.present(a, animated: true, completion: nil)

Musisz sprawić, by backgroundColor widoku ViewController również był przezroczysty. view.backgroundColor = UIColor.clear
Michael Henry
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.