Правильный способ отображения последовательных модальных представлений

У меня есть два представления, которые нужно показывать модально, одно за другим. Это не работает, если мы отклоняем и показываем последовательно, например так:

[rootController dismissModalViewControllerAnimated: YES];
[rootController presentModalViewController: psvc animated: YES];

Второй модальный вид просто не отображается.

Я видел исправление, которое было что-то вроде этого:

[rootController dismissModalViewControllerAnimated: YES];
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
[self performSelector: @selector(seekModal) withObject: nil afterDelay: 0.5];
[[UIApplication sharedApplication] endIgnoringInteractionEvents];

Проблема в том, что это не будет работать все время (иногда требуется больше задержки).

Другим возможным исправлением будет устранение анимации:

[rootController dismissModalViewControllerAnimated: NO];
[rootController presentModalViewController: psvc animated: YES];

Но мне бы очень хотелось сохранить анимацию, чтобы не было ощущения, что первый модал не нужен. Какие-либо предложения?

 yonel21 нояб. 2010 г., 17:49
Вы на 100% уверены, что удаление 1-го модального вида и открытие 2-го вида выполняются в контексте основного потока?
 bpapa21 апр. 2010 г., 04:27
Почему бы просто не использовать Modal View Controller, который меняет его вид? Два контроллера модального вида подряд будут немного раздражать.
 tc.21 нояб. 2010 г., 08:18
Если они "последовательные", рассмотрите возможность использования навигации.

Ответы на вопрос(7)

Я нахожу, что использование модального представления -viewDidDissapear для вызова методов представления контроллера работы очень хорошо. Одним из преимуществ является возможность задержки освобождения на контроллере модального представления. Пожалуйста, опубликуйте любые улучшения, которые я могу сделать. Мое вдохновение для создания этого протокола пришло из iOS 5 «dismissViewControllerAnimated: завершение:» дополнение к UIViewController. Я хотел эту функциональность в iOS 4.3.

PresentorDelegateProtocol.h

@protocol PresentorDelegateProtocol <NSObject>
@optional

/* 

Extra protocol methods defined in protocol for flexibility.  
Main methods are:
- (void)dismissPresentingModalViewController:(id)modalView animated:(BOOL)animated; 
- (void)modalViewDissapeared:(id)modalView;  //used in modal view's -viewDidDissapear

*/

- (void)dismissPresentingModalViewController:(id)modalView animated:(BOOL)animated;
- (void)modalViewDissapeared:(id)modalView; 

// use the block in this method send messages to save state, etc.  This is the one I like to use.
- (void)dismissPresentingModalViewController:(id)modalView animated:(BOOL)animated withBlock:(void(^)())block;

// use in other classes that are not controlling dismissal of the modal view
- (void)executeBlockOnModalDissapearance: (void(^)())block;

@end

PresentingViewController.h

#import "PresentorDelegateProtocol.h"
@interface PresentingViewController : UIViewController <PresentorDelegateProtocol>
- (void)showModalVC;
@end

ModalViewController.h

#import "PresentorDelegateProtocol.h"
@interface ModalViewController : UIViewController
@property (nonatomic, assign) id <PresentorDelegateProtocol> presentorDelegate;
- (void)close;
@end

PresentingViewController.m

#import "PresentingViewController.h"
#import "ModalViewController.h"
@implementation PresentingModalViewController
- (void)showModalVC
{
    ModalViewController *modalVC = [[ModalViewController alloc] initWithNibName:@"ModalViewController" bundle:nil];
    modalVC.presentorDelegate = self;
    [self presentModalViewController:modalVC animated:YES];
}
- (void)dismissPresentingModalViewController:(id)modalView animated:(BOOL)animated
{
    if ([modalView isKindOfClass:[ModalViewController class]]) {
        NSLog(@"Can invoke based on class"); 
    }
    [self dismissModalViewControllerAnimated:animated];    
}
- (void)dismissPresentingModalViewController:(id)modalView animated:(BOOL)animated withBlock:(void(^)())block
{
    block();  
    /* execute block before or after calling to dismiss modal view */
    [self dismissPresentingModalViewController:modalView animated:animated];
    //block();
}
- (void)modalViewDissapeared:(id)modalView
{
    if ([modalView isKindOfClass:[ModalViewController class]]) {
        NSLog(@"Do stuff based on class.");
    }
}
- (void)executeBlockOnModalDissapearance: (void(^)())block
{
    block();
    NSLog(@"This delay's dealloc on modal view until block completes");
}
@end

ModalViewController.m

#import "ModalViewController.h"
@implementation ModalViewController
@synthesize presentorDelegate;

- (void)close
{
    if (1 == 0 /*need to do something before dealloc*/){
        [self.presentorDelegate dismissPresentingModalViewController:self animated:YES withBlock:^{
            NSLog(@"Do stuff with block.  Save, animate, etc");
        }];

    } else {
        [self.presentorDelegate dismissPresentingModalViewController:self animated:YES];
    }
}

- (void)viewDidDisappear:(BOOL)animated
{
    if (1 == 0 /*stuff to do*/){
        [self.presentorDelegate executeBlockOnModalDissapearance:^{
        // do stuff before modal view is deallocated
        }];
    }
    [self.presentorDelegate modalViewDissapeared:self];

    presentorDelegate = nil;
    [super viewDidDisappear:animated];
}
@end;
Решение Вопроса

РЕДАКТИРОВАТЬ: «Правильный» механизм для этого в iOS5 + заключается в использовании– dismissViewControllerAnimated:completion: метод и представить контроллер последовательного просмотра из блока завершения.

Контроллер представления, который отображается модально, будет иметь свой метод viewDidDisappear: animated:, вызываемый после завершения анимации модального освобождения. AFIK, это единственное место, где вы можете подключиться, чтобы инициировать последующий presentModalViewController: animated: call.

У меня есть класс, который я использую для представления контроллеров модального представления, и он реализует логику, которую вы ищете через обратный вызов для представления контроллера представления после завершения увольнения. Чтобы использовать этот класс, просто выделите / инициализируйте экземпляр и представьте, используя обычный presentViewController: animated: call. Реализуйте следующий метод на представительном контроллере представления:

- (void) modalViewControllerDidDismiss:(UIViewController *)modalViewController

Это будет вызвано сразу после того, как модальный контроллер вида исчезнет, ​​и вы можете представить новый модальный контроллер вида в это время.

И еще одна приятная вещь - поскольку этот класс является специализацией UINavigationController, вы можете настроить навигационную панель вкл / выкл по своему усмотрению. Класс также имеет встроенную логику для отображения кнопки отклонения, как вам нравится.

Вот определение класса:

@protocol TSModalViewControllerDelegate

- (void) modalViewControllerDidDismiss: (UIViewController*) modalViewController;

@end

@interface TSModalViewController : UINavigationController 
{
    UIViewController*   _originalParentViewController;
}
@property BOOL dismissButtonHidden;

- (id) initWithViewController: (UIViewController*) vc;
- (id) initWithClass: (Class) c;
- (id) initWithClass: (Class) c nibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;

@end

И реализация класса:

@implementation TSModalViewController
@synthesize dismissButtonHidden;

- (id) initWithViewController: (UIViewController *)vc
{
    return [super initWithRootViewController: vc];
}

- (id) initWithClass:(Class)c
{
    UIViewController* vc = [[[c alloc] init] autorelease];
    return [self initWithViewController: vc];
}

- (id) initWithClass: (Class) c nibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    UIViewController* vc = [[[c alloc] initWithNibName:nibNameOrNil bundle:nibBundleOrNil] autorelease];
    return [self initWithViewController: vc];
}

- (void) viewDidAppear: (BOOL) animated
{
    [super viewDidAppear: animated];

    [_originalParentViewController release];
    _originalParentViewController = [self.parentViewController retain];

    if (!self.dismissButtonHidden)
    {
        UIBarButtonItem* dismissButton = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem: UIBarButtonSystemItemStop
                                                                                        target: self 
                                                                                        action: @selector(onDismiss:)] autorelease];

        UIViewController* rootViewController = [self.viewControllers objectAtIndex:0];

        rootViewController.navigationItem.leftBarButtonItem = dismissButton;
        self.navigationBarHidden = NO;
    }   
}

- (void) viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear: animated];
    if ( [_originalParentViewController respondsToSelector: @selector(modalViewControllerDidDismiss:)] )
    {
        [_originalParentViewController performSelector: @selector(modalViewControllerDidDismiss:) withObject: self];
    }
}

- (void) dismissModalViewControllerAnimated:(BOOL)animated
{
    return [self.parentViewController dismissModalViewControllerAnimated: animated];
}

- (void) onDismiss: (id) sender
{
    [self.parentViewController dismissModalViewControllerAnimated: YES];
}

- (void) didReceiveMemoryWarning 
{
    [super didReceiveMemoryWarning];
}

- (void) viewDidUnload 
{
    [super viewDidUnload];
}

- (void)dealloc 
{
    [_originalParentViewController release];
    [super dealloc];
}

@end

и вот как вы можете использовать его (в контексте некоторого обычного контроллера представления):

- (void) onShowIt:(id)sender
{
    TSModalViewController* mvc = [[[TSModalViewController alloc] initWithClass: [MyModalViewController class] nibName: @"MyModalViewController" bundle:nil] autorelease];
    mvc.dismissButtonHidden = YES;  // set to no if you don't want an "automatic" close button

    [self presentModalViewController: mvc animated: YES];
}

и вот метод обратного вызова dismissal, который представляет новый контроллер модального представления:

- (void) modalViewControllerDidDismiss:(UIViewController *)modalViewController
{
    MyModalViewController* vc = [[[MyModalViewController alloc] initWithNibName: @"MyModalViewController" bundle:nil] autorelease];
    vc.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;

    TSModalViewController* mvc = [[[TSModalViewController alloc] initWithViewController: vc] autorelease];

    [self presentModalViewController: mvc animated: YES];
}

когда последний из модальных контроллеров представления сверху этого исчез, потому что он получит viewDidAppear :. Вы пытались связать presentModalViewController: последующего контроллера представления с этим?

 tc.21 нояб. 2010 г., 08:19
Всегда естьperformSelector:foo afterDelay:0 который может работать в viewDidAppear ...
 Kenny Winker20 нояб. 2010 г., 13:17
Вы все еще сталкиваетесь с проблемами синхронизации ... viewDidAppear вызывается прежде, чем представление фактически появилось, очевидно.
// present modal view inside another presented modal view

    FirstViewController *firstVC = [[FirstViewController alloc] initWithNibName:@"FirstViewController" bundle:nil];
    UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController: firstVC];

    // Note: you can use your viewcontroller instead self.window.rootViewController

    [self.window.rootViewController presentViewController:navController animated:YES completion:^{
                //code...
                    SecondViewController *secondVC = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil];

                    [navController presentViewController: secondVC animated:YES completion:nil];

                }
            }];

которое я нашел для чего-то подобного (если они все равны дочерним элементам родительского представления), - это прикрепить их представления к UIScrollView с включенной подкачкой страниц (вы можете добавить элемент управления страницы внизу, чтобы сделать его понятным и для навигации ) затем добавьте представления контроллеров в представление страницы по мере их появления на экране, удалите их по мере удаления от экрана.

Вам также может понадобиться использовать фиктивные вызовы -viewWillAppear и -viewWillDisappear, если контроллеры полагаются на этот вызываемый вызов. Когда вы все это кодируете, это выглядит как хакерство, но как только оно работает, оно выглядит гладким и естественным, и нет никаких ожиданий, связанных с анимацией одного вида, а затем с анимацией следующего за раз. ушел

показать модальное представление в модальном представлении"? Я отправил ответ об этом здесь:iPho, ne модальный вид внутри другого модального вида?

 Kenny Winker21 нояб. 2010 г., 01:20
На самом деле, нет. Речь идет о закрытии одного модального вида и открытии другого. iOS, похоже, захлебывается, когда вы закрываете и представляете представление слишком близко друг к другу. Это, в частности, проблема со сборщиком камеры, потому что он имеет большое время загрузки / выгрузки. Если вы хотите отменить выбор камеры и показать что-то еще, вы можете столкнуться со всеми видами странного поведения.

е, я бы фактически предложил просто обработать логику анимации самостоятельно. Это не так сложно, и тогда вы сможете точно контролировать представление представлений. Я просто написал нечто подобное для другого вопроса здесь:

iOS - как вы контролируете размер контроллера модального вида?

Вы можете просто анимировать представление, отключить его, и когда вызывается ваш селектор animationDidStop, анимируется ваш второй вид. Приятной частью этого является то, что вы также можете играть с непрозрачностью вида и направлением анимации, а также решить, когда именно должны появиться виды. Например, вы можете сделать так, чтобы второй вид скользил вверх по первому, а первый вид скользил; не нужно ждать, пока первый завершит анимацию.

 atticus21 нояб. 2010 г., 08:02
MVC не обязательно означает один из каждого; на самом деле речь идет о том, чтобы отделить эти компоненты друг от друга и позволить каждому выполнять свою роль. На самом деле довольно часто один контроллер представления управляет несколькими представлениями. В конце концов, многие / большинство компонентов пользовательского интерфейса, которые вы добавляете к представлению контроллера представления (кнопки, изображения и т. Д.) И анимируйте, являются просто представлениями. Лучше думать о каждом контроллере представления как о представлении одного полноэкранного фрагмента вашей программы и о мозге, стоящем за этим разделом приложения. Контроллер - это логика, которая связывает модели и представления вместе.
 Kenny Winker21 нояб. 2010 г., 01:16
Интересная идея. Похоже, что это вынуждает вас выйти из MVC, правда? Как будто вы получите два вида и один контроллер.

Ваш ответ на вопрос