連続したモーダルビューを示す正しい方法
-
30-09-2019 - |
質問
次々とモダンに表示する必要がある2つのビューがあります。これは、このように連続的に却下して表示する場合、これは機能しません。
[rootController dismissModalViewControllerAnimated: YES];
[rootController presentModalViewController: psvc animated: YES];
2番目のモーダルビューは単に表示されません。
このような修正を見てきました。
[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];
しかし、私は本当にアニメーションを維持し、最初のモーダルが邪魔にならないと感じ続けたいと思います。助言がありますか?
解決
編集: iOS5+でこれを行う「正しい」メカニズムは、 – dismissViewControllerAnimated:completion:
メソッド、および完了ブロックからシーケンシャルビューコントローラーを提示します。
モーダルに表示されているViewControllerは、そのviewdiddisappear:animated:Modal-dismissal-animationが完了したら呼び出されます。 Afikこれは、その後のPresentModalViewController:Animated:Callを開始するためにフックできる唯一の場所です。
モーダルビューコントローラーを提示するために使用するクラスがあり、解雇が完了したら表示ビューコントローラーへのコールバックを介して探しているロジックを実装します。このクラスを使用するには、単にインスタンスを割り当てる/initを開始し、通常のspressviewcontroller:animated:callを使用して表示します。表示ビューコントローラーに次の方法を実装してください。
- (void) modalViewControllerDidDismiss:(UIViewController *)modalViewController
これはすぐに呼び出されます。モーダルビューコントローラーはなくなり、この時点で新しいモーダルビューコントローラーを提示できます。
1つの良いことも - このクラスはuinavigationControllerの専門であるため、NavigationBarをオン/オフにすることを好きなように構成できます。また、クラスには、必要に応じて、却下ボタンを表示するための組み込みロジックもあります。
これがクラスの定義です:
@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];
}
そして、ここに新しいモーダルビューコントローラーを提示する解雇コールバックメソッドがあります。
- (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];
}
他のヒント
rootControllerは、その上にある最後のモーダルビューコントローラーの最後のコントローラーがいつ消えたかを知ることができます。その後のView ControllerのPresentModalviewControllerをリンクしてみましたか?
複数のビューアニメーションを一緒にチェーンしたい場合は、実際にアニメーションロジックを自分で処理することをお勧めします。それはあまりにも注意が必要であり、それからあなたはビューの提示方法を細かく制御することができます。私はここで別の質問のために似たような何かを書きました:
iOS-モーダルビューコントローラーのサイズをどのように制御しますか?
ビューをアニメーション化するだけで、ビューをアニメーション化し、AnimationDidstopセレクターが呼び出されたら、2番目のビューをアニメーション化します。これの良い部分は、ビューの不透明度とアニメーションの方向性を使用してプレイしたり、ビューがいつ表示されるかを正確に決定できることです。たとえば、最初のビューが滑り落ちているので、2番目のビューを最初のビューの上に上げてもらうことができます。最初のものがアニメーションを完了するのを待つ必要はありません。
あなたの問題は「モーダルビュー内のモーダルビューを表示する」に関連していますか?これについての答えをここに投稿します:別のモーダルビュー内のiPhoneモーダルビュー?
私がこのようなもので見つけた最良の解決策(それらがすべて親ビューの子供である場合)は、ページングを有効にしてビューをuiscrollviewにパッチすることです(下部にページコントロールを追加して、それを明確にし、ナビゲーションのためにページコントロールを追加できます)次に、コントローラーのビューを画面上に表示するときにページビューにビューを追加し、スクリーンをオフスクリーンにするときに削除します。
また、コントローラーがこれに依存している場合は、viewwillappearと-viewwilldisapearを誘導する必要がある場合があります。すべてをコーディングするときはちょっとハックっぽく感じますが、動作すると、滑らかで自然に見え、1つのビューをアニメーション化することに関連する待機がありません。なくなっている。
Modal Viewの-viewdiddissapearを使用して、表示ビューコントローラーの作業でメソッドを呼び出すと思います。利点の1つは、モーダルビューコントローラーの取引を遅らせる機能です。私ができる改善点を投稿してください。このプロトコルを作成するための私のインスピレーションは、iOS 5の「DismissViewControllerAnimated:完了: "UiviewControllerに追加されました。 iOS 4.3でこの機能を望んでいました。
presenteLegateProtocol.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
発表viewcontroller.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
発表viewcontroller.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;
// 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];
}
}];