IOSプログラムでスワイプしたいビューがいくつかあります。現時点では、クロスディゾルブアニメーションを使用して、モーダルスタイルを使用してそれらの間をスワイプしています。ただし、ホーム画面などで見られるようなスワイプ/スライドアニメーションが必要です。このような遷移をコーディングする方法がわかりません。アニメーションスタイルは利用可能なモーダル遷移スタイルではありません。誰でもコードの例を教えてもらえますか?モーダルモデルなどである必要はありません。最も簡単であることがわかりました。
IOS 7以降、2つのView Controller間のトランジションをアニメーション化する場合、WWDC 2013ビデオ View Controllerを使用したカスタムトランジション で説明されているように、カスタムトランジションを使用します。たとえば、新しいView Controllerのプレゼンテーションをカスタマイズするには、次のようにします。
宛先View Controllerは、プレゼンテーションアニメーションのself.modalPresentationStyle
およびtransitioningDelegate
を指定します:
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
self.modalPresentationStyle = UIModalPresentationCustom;
self.transitioningDelegate = self;
}
return self;
}
このデリゲート(この例では、View Controller自体)はUIViewControllerTransitioningDelegate
に準拠して実装します。
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented
presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
return [[PresentAnimator alloc] init];
}
// in iOS 8 and later, you'd also specify a presentation controller
- (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source {
return [[PresentationController alloc] initWithPresentedViewController:presented presentingViewController:presenting];
}
目的のアニメーションを実行するアニメーターを実装します。
@interface PresentAnimator : NSObject <UIViewControllerAnimatedTransitioning>
@end
@implementation PresentAnimator
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return 0.5;
}
// do whatever animation you want below
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
[[transitionContext containerView] addSubview:toViewController.view];
CGFloat width = fromViewController.view.frame.size.width;
CGRect originalFrame = fromViewController.view.frame;
CGRect rightFrame = originalFrame; rightFrame.Origin.x += width;
CGRect leftFrame = originalFrame; leftFrame.Origin.x -= width / 2.0;
toViewController.view.frame = rightFrame;
toViewController.view.layer.shadowColor = [[UIColor blackColor] CGColor];
toViewController.view.layer.shadowRadius = 10.0;
toViewController.view.layer.shadowOpacity = 0.5;
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromViewController.view.frame = leftFrame;
toViewController.view.frame = originalFrame;
toViewController.view.layer.shadowOpacity = 0.5;
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
@end
また、ビュー階層のクリーンアップを処理するプレゼンテーションコントローラーも実装します。この場合、表示ビューを完全にオーバーレイしているため、遷移が完了したら階層から削除できます。
@interface PresentationController: UIPresentationController
@end
@implementation PresentationController
- (BOOL)shouldRemovePresentersView {
return true;
}
@end
オプションで、このジェスチャーをインタラクティブにしたい場合は、次のこともできます。
対話コントローラー(通常はUIPercentDrivenInteractiveTransition
)を作成します。
UIViewControllerAnimatedTransitioning
もinteractionControllerForPresentation
を実装します。これは明らかに前述の対話コントローラーを返します。
interactionController
を更新するジェスチャー(またはあなたが持っているもの)を持っている
これはすべて前述の View Controllerを使用したカスタムトランジション で説明されています。
Navigation Controllerのプッシュ/ポップをカスタマイズする例については、 Navigation controller custom transition animation を参照してください
以下に、カスタムトランジションより前の、元の回答のコピーを見つけてください。
@sooperの答えは正しいです。CATransitionはあなたが探している効果を生み出すことができます。
ところで、背景が白でない場合、kCATransitionPush
のCATransition
には、トランジションの終わりに奇妙なフェードインとフェードアウトがあり、気が散ることがあります(間を移動するとき)画像、特に、それはわずかにちらつき効果を与えます)。これに苦しんでいる場合、この単純な遷移が非常に優雅であることがわかりました。「次のビュー」を画面のすぐ右側にあるように準備し、同時に現在のビューを画面の左側に移動しながらアニメーション化できます次のビューをアニメートして、現在のビューがあった場所に移動します。私の例では、1つのView Controller内でメインビューの内外でサブビューをアニメーション化していることに注意してください。
float width = self.view.frame.size.width;
float height = self.view.frame.size.height;
// my nextView hasn't been added to the main view yet, so set the frame to be off-screen
[nextView setFrame:CGRectMake(width, 0.0, width, height)];
// then add it to the main view
[self.view addSubview:nextView];
// now animate moving the current view off to the left while the next view is moved into place
[UIView animateWithDuration:0.33f
delay:0.0f
options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction
animations:^{
[nextView setFrame:currView.frame];
[currView setFrame:CGRectMake(-width, 0.0, width, height)];
}
completion:^(BOOL finished){
// do whatever post processing you want (such as resetting what is "current" and what is "next")
}];
明らかに、コントロールをすべて設定する方法のためにこれを微調整する必要がありますが、これは非常にシンプルなトランジション、フェードやそのようなものはなく、ニースのスムーズなトランジションをもたらします。
警告:最初に、この例もCATransition
の例も、SpringBoardのホーム画面アニメーション(説明した)とはまったく異なり、連続的です(つまり、スワイプの途中で、停止して戻ることができます)これらのトランジションは、一度開始するとそれらが発生するものです。リアルタイムの対話が必要な場合は、それも行うことができますが、それは異なります。
更新:
ユーザーの指を追跡する連続ジェスチャーを使用する場合は、UIPanGestureRecognizer
ではなくUISwipeGestureRecognizer
を使用できます。animateWithDuration
はCATransition
よりも優れていると思いますその場合。 handlePanGesture
を変更してframe
座標をユーザーのジェスチャーに合わせて変更し、ユーザーが離したときにアニメーションを完了するように上記のコードを変更しました。かなりうまくいきます。 CATransition
で簡単にできるとは思いません。
たとえば、コントローラーのメインビューでジェスチャーハンドラーを作成できます。
[self.view addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]];
ハンドラーは次のようになります。
- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
// transform the three views by the amount of the x translation
CGPoint translate = [gesture translationInView:gesture.view];
translate.y = 0.0; // I'm just doing horizontal scrolling
prevView.frame = [self frameForPreviousViewWithTranslate:translate];
currView.frame = [self frameForCurrentViewWithTranslate:translate];
nextView.frame = [self frameForNextViewWithTranslate:translate];
// if we're done with gesture, animate frames to new locations
if (gesture.state == UIGestureRecognizerStateCancelled ||
gesture.state == UIGestureRecognizerStateEnded ||
gesture.state == UIGestureRecognizerStateFailed)
{
// figure out if we've moved (or flicked) more than 50% the way across
CGPoint velocity = [gesture velocityInView:gesture.view];
if (translate.x > 0.0 && (translate.x + velocity.x * 0.25) > (gesture.view.bounds.size.width / 2.0) && prevView)
{
// moving right (and/or flicked right)
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
prevView.frame = [self frameForCurrentViewWithTranslate:CGPointZero];
currView.frame = [self frameForNextViewWithTranslate:CGPointZero];
}
completion:^(BOOL finished) {
// do whatever you want upon completion to reflect that everything has slid to the right
// this redefines "next" to be the old "current",
// "current" to be the old "previous", and recycles
// the old "next" to be the new "previous" (you'd presumably.
// want to update the content for the new "previous" to reflect whatever should be there
UIView *tempView = nextView;
nextView = currView;
currView = prevView;
prevView = tempView;
prevView.frame = [self frameForPreviousViewWithTranslate:CGPointZero];
}];
}
else if (translate.x < 0.0 && (translate.x + velocity.x * 0.25) < -(gesture.view.frame.size.width / 2.0) && nextView)
{
// moving left (and/or flicked left)
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
nextView.frame = [self frameForCurrentViewWithTranslate:CGPointZero];
currView.frame = [self frameForPreviousViewWithTranslate:CGPointZero];
}
completion:^(BOOL finished) {
// do whatever you want upon completion to reflect that everything has slid to the left
// this redefines "previous" to be the old "current",
// "current" to be the old "next", and recycles
// the old "previous" to be the new "next". (You'd presumably.
// want to update the content for the new "next" to reflect whatever should be there
UIView *tempView = prevView;
prevView = currView;
currView = nextView;
nextView = tempView;
nextView.frame = [self frameForNextViewWithTranslate:CGPointZero];
}];
}
else
{
// return to original location
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
prevView.frame = [self frameForPreviousViewWithTranslate:CGPointZero];
currView.frame = [self frameForCurrentViewWithTranslate:CGPointZero];
nextView.frame = [self frameForNextViewWithTranslate:CGPointZero];
}
completion:NULL];
}
}
}
これは、おそらく希望するUX用に定義するこれらの簡単なframe
メソッドを使用します。
- (CGRect)frameForPreviousViewWithTranslate:(CGPoint)translate
{
return CGRectMake(-self.view.bounds.size.width + translate.x, translate.y, self.view.bounds.size.width, self.view.bounds.size.height);
}
- (CGRect)frameForCurrentViewWithTranslate:(CGPoint)translate
{
return CGRectMake(translate.x, translate.y, self.view.bounds.size.width, self.view.bounds.size.height);
}
- (CGRect)frameForNextViewWithTranslate:(CGPoint)translate
{
return CGRectMake(self.view.bounds.size.width + translate.x, translate.y, self.view.bounds.size.width, self.view.bounds.size.height);
}
特定の実装は間違いなく異なりますが、これがアイデアを示していることを願っています。
このすべてを説明したので(この古い答えを補足し、明確にします)、この手法はもう使用しないことに注意する必要があります。最近では、一般的にUIScrollView
(「ページング」がオンになっている)または(iOS 6では)UIPageViewController
を使用しています。これにより、この種のジェスチャハンドラを記述するビジネスから抜け出します(スクロールバー、バウンスなどの追加機能を利用できます)。 UIScrollView
実装では、scrollViewDidScroll
イベントに応答するだけで、必要なサブビューを遅延ロードしていることを確認します。
CATransition
アニメーションを作成できます。以下は、現在のビューを押し出しながら、2番目のビュー(左から)を画面にスライドさせる方法の例です。
UIView *theParentView = [self.view superview];
CATransition *animation = [CATransition animation];
[animation setDuration:0.3];
[animation setType:kCATransitionPush];
[animation setSubtype:kCATransitionFromLeft];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[theParentView addSubview:yourSecondViewController.view];
[self.view removeFromSuperview];
[[theParentView layer] addAnimation:animation forKey:@"showSecondViewController"];
ページを切り替えるときにスプリングボードと同じページスクロール/スワイプ効果が必要な場合は、なぜUIScrollView
を使用しないのですか?
CGFloat width = 320;
CGFloat height = 400;
NSInteger pages = 3;
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0,0,width,height)];
scrollView.contentSize = CGSizeMake(width*pages, height);
scrollView.pagingEnabled = YES;
そして、UIPageControl
を使用してこれらのドットを取得します。 :)