Як використовувати presentModalViewController для створення прозорого подання


80

Я відображаю модальний вигляд за допомогою

[self presentModalViewController:controller animated:YES];

Коли подання рухається вгору по екрану, воно прозоре відповідно до налаштування у файлі xib, з якого він створений, але як тільки він заповнює екран, він стає непрозорим.

Чи є спосіб зберегти прозорість зору?

Я підозрюю, що вид, на який він розміщується, не відображається, а не те, що модальний вигляд стає непрозорим.


1
Це питання зараз досить старе. Шукаючи відповіді, шукайте той, який стосується тієї версії iOS, якою ви користуєтесь.
Darryl Braaten

Це stackoverflow.com/q/27598846/1603234 змушує мене посміхатися, тепер ваша черга :)
Hemang

Ви можете використовуватиviewcontroller.modalPresentationStyle = .FormSheet
Джеральд

Відповіді:


63

Ваш вигляд все ще прозорий, але як тільки ваш модальний контролер знаходиться у верхній частині стека, вигляд за ним прихований (як у випадку з будь-яким верхнім контролером перегляду). Рішення полягає в тому, щоб самостійно анімувати вигляд; тоді контролер позаду viewController не буде прихований (оскільки у вас його не залишиться).


Дякую, це підтверджує те, що я підозрював.
Дарріл Браатен,

1
Я подав повідомлення про помилку в Apple щодо цієї проблеми. Відкритий радар: openradar.appspot.com/radar?id=1062402
теорія,

4
Проблема з цим полягає в тому, що представлений вигляд не може мати власні налаштування авторотації.
Шизам

85

Після iOS 3.2 існує спосіб зробити це без будь-яких “хитрощів” - див. Документацію до властивості modalPresentationStyle . У вас є rootViewController, який представлятиме ViewController. Отже, ось код успіху:

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

За допомогою цього методу фон viewController буде прозорим, а основний rootViewController буде видно. Зверніть увагу, що це, здається, працює лише на iPad, див. Коментарі нижче.


1
Привіт .. +1 з мого боку bcoz це чудово працює в iOS 4 та iPad. але як це буде працювати в iPhone 3.0. Дякую
Мітеш Хатрі

3
У мене це не працює (iOS 5.0), чи може хтось показати якийсь код?
Даніель

6
Здається, це не працює на iPhone / iPod, лише iPad (див. Документацію, на яку посилається у відповіді). @simpleBob: можливо, саме тому він не працює для вас?
Mac

1
FYI: Я поставив те саме питання в цьому дописі: stackoverflow.com/questions/14419395/…
Божевільний шимпанзе

1
Я тестував на iOS 8, і я повинен використовувати його Over Current Contextяк стиль презентації.
haxpor

48

Що мені потрібно, щоб це працювало:

self.window.rootViewController.modalPresentationStyle = UIModalPresentationCurrentContext;

1
це просто працює, дякую, я щойно тримав свій код на - (BOOL) додаток: (UIApplication *) додаток didFinishLaunchingWithOptions: (NSDictionary *) метод launchOptions та вуаля
Kshitiz Ghimire

2
Ви також можете просто встановити значення modalPresentationStyleна контролері подання подання (а не на вікні rootViewController).
mbinna

Дякую!! навіть працює, коли ви це робите, [self performSegueWithIdentifier: @ "open" sender: self];
DogCoffee

Ви завжди можете анімувати вигляд від руки при завантаженні модального перегляду.
Jacob K

2
Зауважте, що, як сказав пан Т нижче, modalPresentationStyle повинен бути встановлений на контролері перегляду, який наразі має весь екран.
jessepinho

24

Для тих, хто хоче бачити якийсь код, ось що я додав до контролера мого прозорого подання:

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

Єдина проблема у мене з цим рішенням - це управління пам’яттю. Наприклад: TestViewController * testView = [[TestViewController alloc] initWithNibName: набір @ "TestView": nil]; [testView presentWithSuperview: self.navigationController.view]; Працюватиме нормально, однак, якщо ви спробуєте звільнити або автоматично випустити View, будь-які виклики методів у цьому поданні призведуть до винятків із поганим доступом
Мік Уокер,

Ти впевнений? Я думаю, що подання збереже [superview addSubview:], тож ви зможете його звільнити.
Крістофер Джонсон,

Якщо ви зателефонуєте presentWithSuperview, випустіть testView, що він аварійно завершує роботу. Він також відображає дисплеї "під" будь-якими панелями інструментів NavigationController тощо
Райан Букер

Це давно працювало для мене, але це вже не працює в альбомному режимі. У когось є така сама проблема?
Джеффрі ван де Вурст,

19

Затверджений Apple спосіб зробити це в iOS 8 - встановити modalPresentationStyle на 'UIModalPresentationOverCurrentContext'.

З документації UIViewController:

UIModalPresentationOverCurrentContext

Стиль презентації, де вміст відображається лише над вмістом батьківського перегляду. Подання під представленим вмістом не видаляються з ієрархії подань, коли презентація закінчується. Отже, якщо представлений контролер перегляду не заповнює екран непрозорим вмістом, основний вміст відображається.

Під час представлення контролера перегляду у поповері цей стиль презентації підтримується лише у тому випадку, якщо стиль переходу - UIModalTransitionStyleCoverVertical. Спроба використовувати інший стиль переходу викликає виняток. Однак ви можете використовувати інші стилі переходу (крім часткового переходу curl), якщо батьківський контролер подання не знаходиться в popover.

Доступно в iOS 8.0 та новіших версіях.

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

Відео "Перегляд досягнень контролера в iOS 8" від WWDC 2014 детально описує це.

Обов’язково надайте представленому контролеру подання чіткий колір фону (інакше він все одно буде виглядати непрозорим).


це дає мені чорний екран на iOS8 та iOS9
Amit Hooda

Насправді це працює, якщо ви встановите для виду вашого контролера перегляду колір тла із значенням альфа нижче 1
П'єр,

16

Є ще один варіант: перед тим, як показати модальний контролер, зробіть знімок екрана всього вікна. Вставте захоплене зображення у UIImageView і додайте подання зображення до подання контролера, яке ви збираєтеся показати. Потім відправити назад. Вставте інший вид над зображенням (чорний фон, альфа 0,7). Покажіть свій модальний контролер, і схоже, він був прозорим. Просто спробував на iPhone 4 під управлінням iOS 4.3.1. Як чарівність.


хороший, я реалізую це, це було здорово для мене. додайте свій код, який відповідає цій концепції.
Ішу

Ніхто не казав, що це буде легко! :-)
Крумелур

Як би ви підтримали обертання пристрою за такого підходу?
Kyle Clegg

10

це досить давно, але я вирішив таку ж проблему наступним чином: оскільки мені потрібно представити контролер навігації в iPhone, додавання підпрогляду не було життєздатним рішенням.

Отже, що я зробив:

1) Перш ніж представляти контролер перегляду, зробіть знімок екрана поточного екрана:

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

2) Створіть контролер подання, який ви хочете презентувати, і додайте фон як підпрогляд, відправивши його назад.

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

3) Представте свій контролер перегляду, але перед цим у новому контролері перегляду додайте прозорий вигляд у viewDidLoad (я використовував ILTranslucentView)

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

І це все!


Приголомшливо Все добре працювало поряд із напівпрозорим видом, що спричинило зникнення знімка екрану.
Michal Shatz,

Це чудове рішення! Краще тримати код контролера для спливаючого вікна окремо від батьківського контролера подання.
1kmonkies

Для мене працювало, але налаштування фону повинно було бути на
увазіWillAppear

5

Я записав свої висновки з цього приводу в іншому питанні , але суть цього полягає в тому, що ви повинні зателефонувати modalPresentationStyle = UIModalPresentationCurrentContextза тим, хто на даний момент володіє повним екраном. Здебільшого це все, що є [UIApplication sharedApplication] .delegate.window rootViewController. Це також може бути новий UIViewController, який був представленийmodalPresentationStyle = UIModalPresentationFullScreen .

Будь ласка, прочитайте інший набагато детальніший допис, якщо вам цікаво, як я конкретно вирішив цю проблему. Удачі!


5

Здається, це порушено в IOS 8, я використовую навігаційний контролер, і контекст, який відображається, є контекстом меню навігації, який у нашому випадку є розсувним контролером меню.

Ми використовуємо стручок 'TWTSideMenuViewController', '0.3' не перевірили, чи це ще проблема з бібліотекою, чи метод, описаний вище.


5

Це працювало для мене в iOS 8-9:

1- Встановіть фон контролера перегляду за допомогою альфа-версії

2- додати цей код:

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

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

4

Я знаю, що це досить давнє запитання. Я застряг у цьому питанні, і мені вдалося отримати підказку з цієї теми. Тож розміщення тут, як я це отримав, спрацювало :). Я використовую раскадровку, і я переходжу до ViewController, який буде представлений. Контролер подання має прозорий колір фону. Тепер в інспекторі атрибутів секції я встановив для презентації значення "Над поточним контекстом". І це спрацювало для мене. Я розробляю для iPhone.

Інспектор атрибутів сеги


спасибі, також працює від набору UIModalPresentationStyle.OverCurrentContext програмно
guoleii

3

Я створив відкриту бібліотеку сортування MZFormSheetController, щоб представити модальний аркуш форми в додатковому вікні UIW. Ви можете використовувати його для представлення прозорості модального контролера перегляду, навіть регулювати розмір представленого контролера перегляду.


Ви можете пояснити, як ним користуватися? Тому що, коли я намагаюся викликати код, це викликає SIGABRT.
Матросов Олександр

3

У моєму стані я переглядаю той самий viewController. Тож створіть новий контролер подання для проведення UIView. Зробіть цей вигляд прозорим, встановивши його альфа-властивість. Потім натисканням кнопки я написав цей код. Виглядає добре.

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

І це показує, чого я хочу. сподіваюся, це допоможе комусь.


2

Ось категорія, яку я створив, вирішить проблему.

    //
    //  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
До уваги, немає необхідності самостійно переглядати підзаголовки методом звільнення. Фреймворк зробить це за вас за допомогою [self.view viewWithTag: 253];
SteveB

Категорія Дейва Вуда на цьому прикладі відображає ваш погляд таким, яким він є. Отже, ви також повинні встановити тло альфа-налаштування вигляду xib нижче 1, щоб отримати напівпрозорий ефект.
Василь Бурк

Ця категорія має проблему під час подання в прокручуваному поданні, наприклад у вигляді таблиці: Прокрутка працює. Це погано. Користувач може прокручувати представлений вигляд за межі екрану. Вміст основного представлення подання виходить на екран.
Basil Bourque

З тих пір я оновив цю категорію. Ви можете отримати код тут: gist.github.com/4423365 . Я використовую його для перегляду таблиць та переглядів колекцій без проблеми прокрутки, яку ви описуєте.
Dave Wood

2

Після багатьох досліджень, схоже, це вирішить наше питання і послужить нашій меті.

створити сегу від вихідного ВК до цільового ВК з ідентифікатором.

наприклад "goToDestinationViewController" добре, щоб полегшити життя, давайте розглянемо поточний контролер подання, тобто той, який ви хочете за своїм прозорим видом подати як джерело, а пункт призначення як пункт призначення

Зараз у вихідному ВК у viewDidLoad: або view

performSegueWithIdentifier("goToDestinationViewController", sender: nil)

добре, ми пройшли половину шляху. Тепер перейдіть до своєї розкадровки. Клацніть на сегу. який повинен виглядати так: segue

змінити параметри на показані.

Тепер настає реальне рішення.

додайте цей код у viewDidLoad контролера вигляду призначення.

self.modalPresentationStyle = .Custom

.................................................. ....................... ТОГО ЛЕГКО ......................... .........................................


1
self.modalPresentationStyle = .customМені вдається налаштувати модальне представлення фактичного контролера перегляду з прозорим фоном у Swift 3, Xcode 8. Єдина проблема полягає в тому, що якщо контролер подання подання вбудований у контролер панелі вкладок, панель вкладок з’являється перед прозорим фоновий вид.
mtso

Отже - якщо використовується панель вкладок - для контролера подання подання tabBarController.tabBar.isHiddenслід встановити значення true.
mtso

1

Альтернативний спосіб - використання "перегляду контейнера". Просто зробіть альфа нижче 1 і вставте з секвесом. XCode 5, орієнтована на iOS7.

не можу показати зображення, недостатньо репутації)))

Перегляд контейнера доступний з iOS6.


1

Цей код чудово працює на iPhone під iOS6 та iOS7:

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

Але на цьому шляху ви втрачаєте анімацію "ковзання знизу".


Це не працює для мене на iPhone. Вигляд знизу все ще зникає. Я вважаю, що modalPresentationStyle застосовується лише до iPad.
jessepinho

Якщо ви хочете, я можу надіслати вам екрани результату ефекту. Можливо, щось не так із вашим впровадженням.
malex

А ... це було тому, що я не знав, що контролер перегляду повинен бути повноекранним. (Див. Коментар пана Т.).
jessepinho

1

Я знайшов це елегантне та просте рішення для iOS 7 та новіших версій!

Для iOS 8 Apple додала UIModalPresentationOverCurrentContext, але він не працює для iOS 7 і раніше, тому я не міг використовувати його для свого випадку.

Будь ласка, створіть категорію та введіть наступний код.

.h файл

typedef void(^DismissBlock)(void);

@interface UIViewController (Ext)

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

@end

.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

Примітка: він також працює для NaviContoller, tabBarController.

Приклад використання:

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

Насолоджуйся цим! і будь ласка, залиште кілька відгуків.


0

Це найкращий і найчистіший спосіб, який я знайшов на даний момент:

@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

Я намагаюся використовувати кілька методів для вирішення, але все ж не вдалося, наступний код реалізований остаточно.

Дозвіл Свіфта:

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

потім у контролері перегляду B:

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

Ви повинні зробити фоновий колір подання ViewController теж прозорим. view.backgroundColor = UIColor.clear
Майкл Генрі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.