2011-08-18 10 views
35

मैं आईओएस में एक संक्रमण एनीमेशन बनाने की कोशिश कर रहा हूं जहां पूरी स्क्रीन भरने के लिए एक व्यू या व्यू कंट्रोलर विस्तारित प्रतीत होता है, फिर पूरा होने पर उसकी पिछली स्थिति पर वापस अनुबंध करें। मुझे यकीन नहीं है कि इस प्रकार के संक्रमण को आधिकारिक तौर पर किस प्रकार बुलाया जाता है, लेकिन आप आईपैड के लिए यूट्यूब ऐप में एक उदाहरण देख सकते हैं। जब आप ग्रिड पर खोज परिणाम थंबनेल में से किसी एक को टैप करते हैं, तो यह थंबनेल से फैलता है, फिर जब आप खोज पर वापस आते हैं तो थंबनेल में वापस अनुबंध करते हैं।आईओएस पर विचारों के बीच मैं विस्तार/अनुबंध संक्रमण कैसे कर सकता हूं?

मैं इस के दो पहलुओं में दिलचस्पी रखता हूँ:

  1. कैसे जब एक दूसरे को देखने और के बीच संक्रमण आप इस प्रभाव होगा? दूसरे शब्दों में, यदि ए को स्क्रीन के कुछ क्षेत्र को ले जाता है, तो आप बी को देखने के लिए इसे कैसे परिवर्तित करेंगे, जो पूरी स्क्रीन लेता है, और इसके विपरीत?

  2. इस तरह आप एक मोडल व्यू में कैसे बदलाव करेंगे? दूसरे शब्दों में, यदि UIViewController C वर्तमान में दिखा रहा है और इसमें दृश्य डी शामिल है जो स्क्रीन का हिस्सा लेता है, तो आप इसे कैसे देखते हैं जैसे डी डी UIViewController E में बदल रहा है जिसे सी के शीर्ष पर सामान्य रूप से प्रस्तुत किया जाता है?

संपादित करें: मैं अगर है कि इस सवाल का अधिक प्यार हो जाता है देखने के लिए एक इनाम जोड़ रहा।

संपादित करें: मुझे कुछ स्रोत कोड मिला है जो यह करता है, और एनोमी का विचार कुछ परिशोधन के साथ एक आकर्षण की तरह काम करता है। मैंने पहली बार मोडल कंट्रोलर के दृश्य (ई) को एनिमेट करने का प्रयास किया था, लेकिन यह महसूस करने का असर नहीं हुआ कि आप स्क्रीन में ज़ूम कर रहे हैं, क्योंकि यह थंबनेल व्यू (सी) में सभी चीजों का विस्तार नहीं कर रहा था। तो फिर मैंने मूल नियंत्रक के दृश्य (सी) को एनिमेट करने का प्रयास किया, लेकिन इसे झटकेदार एनीमेशन के लिए बनाया गया, और पृष्ठभूमि बनावट जैसी चीजें ठीक से ज़ूम नहीं हुईं। तो मैं जो कर रहा हूं वह मूल दृश्य नियंत्रक (सी) की छवि ले रहा है और मॉड्यूल व्यू (ई) के अंदर ज़ूम कर रहा है। यह विधि मेरे मूल की तुलना में काफी जटिल है, लेकिन यह अच्छा लग रहा है! मुझे लगता है कि आईओएस को इसके आंतरिक संक्रमण भी करना चाहिए। वैसे भी, यह कोड है, जिसे मैंने UIViewController पर एक श्रेणी के रूप में लिखा है।

UIViewController + Transitions.h:

#import <Foundation/Foundation.h> 

@interface UIViewController (Transitions) 

// make a transition that looks like a modal view 
// is expanding from a subview 
- (void)expandView:(UIView *)sourceView 
     toModalViewController:(UIViewController *)modalViewController; 

// make a transition that looks like the current modal view 
// is shrinking into a subview 
- (void)dismissModalViewControllerToView:(UIView *)view; 

@end 

UIViewController + Transitions.m:

#import "UIViewController+Transitions.h" 

... 

- (void)userDidTapThumbnail { 

    DetailViewController *detail = 
    [[DetailViewController alloc] 
     initWithNibName:nil bundle:nil]; 

    [self expandView:thumbnailView toModalViewController:detail]; 

    [detail release]; 
} 

- (void)dismissModalViewControllerAnimated:(BOOL)animated { 
    if (([self.modalViewController isKindOfClass:[DetailViewController class]]) && 
     (animated)) { 

    [self dismissModalViewControllerToView:thumbnailView]; 

    } 
    else { 
    [super dismissModalViewControllerAnimated:animated]; 
    } 
} 

:

#import "UIViewController+Transitions.h" 

@implementation UIViewController (Transitions) 

// capture a screen-sized image of the receiver 
- (UIImageView *)imageViewFromScreen { 
    // make a bitmap copy of the screen 
    UIGraphicsBeginImageContextWithOptions(
    [UIScreen mainScreen].bounds.size, YES, 
    [UIScreen mainScreen].scale); 
    // get the root layer 
    CALayer *layer = self.view.layer; 
    while(layer.superlayer) { 
    layer = layer.superlayer; 
    } 
    // render it into the bitmap 
    [layer renderInContext:UIGraphicsGetCurrentContext()]; 
    // get the image 
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 
    // close the context 
    UIGraphicsEndImageContext(); 
    // make a view for the image 
    UIImageView *imageView = 
    [[[UIImageView alloc] initWithImage:image] 
     autorelease]; 

    return(imageView); 
} 

// make a transform that causes the given subview to fill the screen 
// (when applied to an image of the screen) 
- (CATransform3D)transformToFillScreenWithSubview:(UIView *)sourceView { 
    // get the root view 
    UIView *rootView = sourceView; 
    while (rootView.superview) rootView = rootView.superview; 
    // convert the source view's center and size into the coordinate 
    // system of the root view 
    CGRect sourceRect = [sourceView convertRect:sourceView.bounds toView:rootView]; 
    CGPoint sourceCenter = CGPointMake(
    CGRectGetMidX(sourceRect), CGRectGetMidY(sourceRect)); 
    CGSize sourceSize = sourceRect.size; 
    // get the size and position we're expanding it to 
    CGRect screenBounds = [UIScreen mainScreen].bounds; 
    CGPoint targetCenter = CGPointMake(
    CGRectGetMidX(screenBounds), 
    CGRectGetMidY(screenBounds)); 
    CGSize targetSize = screenBounds.size; 
    // scale so that the view fills the screen 
    CATransform3D t = CATransform3DIdentity; 
    CGFloat sourceAspect = sourceSize.width/sourceSize.height; 
    CGFloat targetAspect = targetSize.width/targetSize.height; 
    CGFloat scale = 1.0; 
    if (sourceAspect > targetAspect) 
    scale = targetSize.width/sourceSize.width; 
    else 
    scale = targetSize.height/sourceSize.height; 
    t = CATransform3DScale(t, scale, scale, 1.0); 
    // compensate for the status bar in the screen image 
    CGFloat statusBarAdjustment = 
    (([UIApplication sharedApplication].statusBarFrame.size.height/2.0) 
    /scale); 
    // transform to center the view 
    t = CATransform3DTranslate(t, 
    (targetCenter.x - sourceCenter.x), 
    (targetCenter.y - sourceCenter.y) + statusBarAdjustment, 
    0.0); 

    return(t); 
} 

- (void)expandView:(UIView *)sourceView 
     toModalViewController:(UIViewController *)modalViewController { 

    // get an image of the screen 
    UIImageView *imageView = [self imageViewFromScreen]; 

    // insert it into the modal view's hierarchy 
    [self presentModalViewController:modalViewController animated:NO]; 
    UIView *rootView = modalViewController.view; 
    while (rootView.superview) rootView = rootView.superview; 
    [rootView addSubview:imageView]; 

    // make a transform that makes the source view fill the screen 
    CATransform3D t = [self transformToFillScreenWithSubview:sourceView]; 

    // animate the transform 
    [UIView animateWithDuration:0.4 
    animations:^(void) { 
     imageView.layer.transform = t; 
    } completion:^(BOOL finished) { 
     [imageView removeFromSuperview]; 
    }]; 
} 

- (void)dismissModalViewControllerToView:(UIView *)view { 

    // take a snapshot of the current screen 
    UIImageView *imageView = [self imageViewFromScreen]; 

    // insert it into the root view 
    UIView *rootView = self.view; 
    while (rootView.superview) rootView = rootView.superview; 
    [rootView addSubview:imageView]; 

    // make the subview initially fill the screen 
    imageView.layer.transform = [self transformToFillScreenWithSubview:view]; 
    // remove the modal view 
    [self dismissModalViewControllerAnimated:NO]; 

    // animate the screen shrinking back to normal 
    [UIView animateWithDuration:0.4 
    animations:^(void) { 
     imageView.layer.transform = CATransform3DIdentity; 
    } 
    completion:^(BOOL finished) { 
     [imageView removeFromSuperview]; 
    }]; 
} 

@end 

आप इसे एक UIViewController उपवर्ग में कुछ इस तरह इस्तेमाल कर सकते हैं संपादित करें: अच्छा, यह पता चला है कि नहीं करता है पोर्ट्रेट के अलावा इंटरफेस उन्मुखता वास्तव में संभाल नहीं है। इसलिए मुझे रोटेशन के साथ पास करने के लिए व्यू कंट्रोलर का उपयोग करके UIWindow में संक्रमण को एनिमेट करने के लिए स्विच करना पड़ा।

UIViewController + Transitions.m:

@interface ContainerViewController : UIViewController { } 
@end 

@implementation ContainerViewController 
    - (BOOL)shouldAutorotateToInterfaceOrientation: 
      (UIInterfaceOrientation)toInterfaceOrientation { 
    return(YES); 
    } 
@end 

... 

// get the screen size, compensating for orientation 
- (CGSize)screenSize { 
    // get the size of the screen (swapping dimensions for other orientations) 
    CGSize size = [UIScreen mainScreen].bounds.size; 
    if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) { 
    CGFloat width = size.width; 
    size.width = size.height; 
    size.height = width; 
    } 
    return(size); 
} 

// capture a screen-sized image of the receiver 
- (UIImageView *)imageViewFromScreen { 

    // get the root layer 
    CALayer *layer = self.view.layer; 
    while(layer.superlayer) { 
    layer = layer.superlayer; 
    } 
    // get the size of the bitmap 
    CGSize size = [self screenSize]; 
    // make a bitmap to copy the screen into 
    UIGraphicsBeginImageContextWithOptions(
    size, YES, 
    [UIScreen mainScreen].scale); 
    CGContextRef context = UIGraphicsGetCurrentContext(); 
    // compensate for orientation 
    if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft) { 
    CGContextTranslateCTM(context, size.width, 0); 
    CGContextRotateCTM(context, M_PI_2); 
    } 
    else if (self.interfaceOrientation == UIInterfaceOrientationLandscapeRight) { 
    CGContextTranslateCTM(context, 0, size.height); 
    CGContextRotateCTM(context, - M_PI_2); 
    } 
    else if (self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) { 
    CGContextTranslateCTM(context, size.width, size.height); 
    CGContextRotateCTM(context, M_PI); 
    } 
    // render the layer into the bitmap 
    [layer renderInContext:context]; 
    // get the image 
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 
    // close the context 
    UIGraphicsEndImageContext(); 
    // make a view for the image 
    UIImageView *imageView = 
    [[[UIImageView alloc] initWithImage:image] 
     autorelease]; 
    // done 
    return(imageView); 
} 

// make a transform that causes the given subview to fill the screen 
// (when applied to an image of the screen) 
- (CATransform3D)transformToFillScreenWithSubview:(UIView *)sourceView 
       includeStatusBar:(BOOL)includeStatusBar { 
    // get the root view 
    UIView *rootView = sourceView; 
    while (rootView.superview) rootView = rootView.superview; 
    // by default, zoom from the view's bounds 
    CGRect sourceRect = sourceView.bounds; 
    // convert the source view's center and size into the coordinate 
    // system of the root view 
    sourceRect = [sourceView convertRect:sourceRect toView:rootView]; 
    CGPoint sourceCenter = CGPointMake(
    CGRectGetMidX(sourceRect), CGRectGetMidY(sourceRect)); 
    CGSize sourceSize = sourceRect.size; 
    // get the size and position we're expanding it to 
    CGSize targetSize = [self screenSize]; 
    CGPoint targetCenter = CGPointMake(
    targetSize.width/2.0, 
    targetSize.height/2.0); 

    // scale so that the view fills the screen 
    CATransform3D t = CATransform3DIdentity; 
    CGFloat sourceAspect = sourceSize.width/sourceSize.height; 
    CGFloat targetAspect = targetSize.width/targetSize.height; 
    CGFloat scale = 1.0; 
    if (sourceAspect > targetAspect) 
    scale = targetSize.width/sourceSize.width; 
    else 
    scale = targetSize.height/sourceSize.height; 
    t = CATransform3DScale(t, scale, scale, 1.0); 
    // compensate for the status bar in the screen image 
    CGFloat statusBarAdjustment = includeStatusBar ? 
    (([UIApplication sharedApplication].statusBarFrame.size.height/2.0) 
    /scale) : 0.0; 
    // transform to center the view 
    t = CATransform3DTranslate(t, 
    (targetCenter.x - sourceCenter.x), 
    (targetCenter.y - sourceCenter.y) + statusBarAdjustment, 
    0.0); 

    return(t); 
} 

- (void)expandView:(UIView *)sourceView 
     toModalViewController:(UIViewController *)modalViewController { 

    // get an image of the screen 
    UIImageView *imageView = [self imageViewFromScreen]; 
    // show the modal view 
    [self presentModalViewController:modalViewController animated:NO]; 
    // make a window to display the transition on top of everything else 
    UIWindow *window = 
    [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 
    window.hidden = NO; 
    window.backgroundColor = [UIColor blackColor]; 
    // make a view controller to display the image in 
    ContainerViewController *vc = [[ContainerViewController alloc] init]; 
    vc.wantsFullScreenLayout = YES; 
    // show the window 
    [window setRootViewController:vc]; 
    [window makeKeyAndVisible]; 
    // add the image to the window 
    [vc.view addSubview:imageView]; 

    // make a transform that makes the source view fill the screen 
    CATransform3D t = [self 
    transformToFillScreenWithSubview:sourceView 
    includeStatusBar:(! modalViewController.wantsFullScreenLayout)]; 

    // animate the transform 
    [UIView animateWithDuration:0.4 
    animations:^(void) { 
     imageView.layer.transform = t; 
    } completion:^(BOOL finished) { 
     // we're going to crossfade, so change the background to clear 
     window.backgroundColor = [UIColor clearColor]; 
     // do a little crossfade 
     [UIView animateWithDuration:0.25 
     animations:^(void) { 
      imageView.alpha = 0.0; 
     } 
     completion:^(BOOL finished) { 
      window.hidden = YES; 
      [window release]; 
      [vc release]; 
     }]; 
    }]; 
} 

- (void)dismissModalViewControllerToView:(UIView *)view { 

    // temporarily remove the modal dialog so we can get an accurate screenshot 
    // with orientation applied 
    UIViewController *modalViewController = [self.modalViewController retain]; 
    [self dismissModalViewControllerAnimated:NO]; 

    // capture the screen 
    UIImageView *imageView = [self imageViewFromScreen]; 
    // put the modal view controller back 
    [self presentModalViewController:modalViewController animated:NO]; 
    [modalViewController release]; 

    // make a window to display the transition on top of everything else 
    UIWindow *window = 
    [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 
    window.hidden = NO; 
    window.backgroundColor = [UIColor clearColor]; 
    // make a view controller to display the image in 
    ContainerViewController *vc = [[ContainerViewController alloc] init]; 
    vc.wantsFullScreenLayout = YES; 
    // show the window 
    [window setRootViewController:vc]; 
    [window makeKeyAndVisible]; 
    // add the image to the window 
    [vc.view addSubview:imageView]; 

    // make the subview initially fill the screen 
    imageView.layer.transform = [self 
    transformToFillScreenWithSubview:view 
    includeStatusBar:(! self.modalViewController.wantsFullScreenLayout)]; 

    // animate a little crossfade 
    imageView.alpha = 0.0; 
    [UIView animateWithDuration:0.15 
    animations:^(void) { 
     imageView.alpha = 1.0; 
    } 
    completion:^(BOOL finished) { 
     // remove the modal view 
     [self dismissModalViewControllerAnimated:NO]; 
     // set the background so the real screen won't show through 
     window.backgroundColor = [UIColor blackColor]; 
     // animate the screen shrinking back to normal 
     [UIView animateWithDuration:0.4 
     animations:^(void) { 
      imageView.layer.transform = CATransform3DIdentity; 
     } 
     completion:^(BOOL finished) { 
      // hide the transition stuff 
      window.hidden = YES; 
      [window release]; 
      [vc release]; 
     }]; 
    }]; 

} 

वाह नीचे और अधिक जटिल संस्करण मिलते हैं! लेकिन अब यह किसी भी प्रतिबंधित एपीआई का उपयोग किए बिना ऐप्पल के संस्करण की तरह दिखता है। साथ ही, यह मोडल व्यू के सामने होने पर अभिविन्यास बदलता है, भले ही यह काम करता है।

उत्तर

12
  1. प्रभाव बनाना सरल है।आप पूर्ण आकार के दृश्य को लेते हैं, इसे transform और center को थंबनेल के शीर्ष पर रखने के लिए प्रारंभ करें, इसे उचित पर्यवेक्षण में जोड़ें, और फिर एनीमेशन ब्लॉक में transform और center को अंतिम स्थिति में रखने के लिए रीसेट करें। दृश्य को खारिज करने के लिए, बस विपरीत करें: एनीमेशन ब्लॉक में transform और center सेट करें ताकि इसे थंबनेल के शीर्ष पर रखा जा सके और फिर इसे पूर्णता ब्लॉक में पूरी तरह हटा दें।

    ध्यान दें कि एक बिंदु से ज़ूम करने की कोशिश कर रहा है (यानी 0 चौड़ाई और 0 ऊंचाई के साथ एक आयताकार) चीजों को पेंच कर देगा। यदि आप ऐसा करना चाहते हैं, तो आयत से ज़ूम करें चौड़ाई/ऊंचाई के साथ 0.00001 जैसे कुछ।

  2. एक तरीका # 1 जैसा ही करना होगा, और फिर एनीमेशन पूर्ण होने पर वास्तविक दृश्य नियंत्रक को प्रस्तुत करने के लिए presentModalViewController:animated: पर कॉल करें (जो, यदि सही हो, तो परिणामस्वरूप कोई अंतर नहीं होगा presentModalViewController:animated: कॉल पर)। और dismissModalViewControllerAnimated: NO के साथ # 1 के रूप में खारिज करने के लिए।

    या आप सीधे # 1 में के रूप में मॉडल दृश्य नियंत्रक के दृश्य में हेरफेर सकता है, और स्वीकार करते हैं कि parentViewController, interfaceOrientation, और कुछ अन्य सामान सिर्फ सही मॉडल दृश्य नियंत्रक में काम नहीं करेगा क्योंकि एप्पल हमें समर्थन नहीं करता हमारे अपने कंटेनर दृश्य नियंत्रक।

+0

धन्यवाद। मैं अस्पष्ट हूं कि हम भाग 2 में एनिमेटिंग करेंगे। क्या यह मोडल व्यू कंट्रोलर के दृश्य को सामान्य रूप से प्रस्तुत करने से पहले एनिमेट करना संभव है? इस मामले में इसे वर्तमान में देखने वाले व्यू कंट्रोलर के सबव्यूव के रूप में जोड़ा जाना होगा, है ना? मुझे इसके लिए कुछ स्रोत कोड देखने में बहुत दिलचस्पी होगी। –

+0

@ जेसे क्रॉसन: # 2 में पहली बार, आप कुछ दृश्य एनिमेट कर रहे होंगे जो मोडल व्यू कंट्रोलर के दृश्य की तरह दिखता है। कैलियर के 'रेंडरइन कॉन्टेक्स्ट:' का उपयोग करके बनाया गया सिर्फ "स्क्रीनशॉट" हो सकता है। दूसरी तरफ, आप सीधे व्यू कंट्रोलर के दृश्य में हेरफेर करेंगे। – Anomie

+0

स्पष्टीकरण के लिए धन्यवाद! मैंने पाया है कि व्यू कंट्रोलर के गैर-मानक हेरफेर अभ्यास में कांटेदार हो सकते हैं, इसलिए मैंने स्रोत कोड के बारे में पूछा।मैं देखता हूं कि यह कैसे काम करता है और आपको बताता है। –

9

यूट्यूब आईपैड एनीमेशन देखने के बाद, मुझे लगा कि यह सिर्फ एक भ्रम है। आइए मान लें कि खोज परिणामों के लिए SearchViewController और वीडियो के लिए DetailViewController और वीडियो की अतिरिक्त जानकारी है।

DetailViewController की एक विधि है जैसे - (id)initWithFullscreen जो वीडियो के साथ पूर्ण स्क्रीन स्थान का उपयोग कर व्यू कंट्रोलर शुरू करता है।

तो अनुक्रम इस प्रकार है:

  1. SearchViewController इसके परिणामों को प्रस्तुत करता है।
  2. उपयोगकर्ता एक वीडियो पर क्लिक करता है।
  3. DetailViewController initWithFullscreen के साथ बनाया गया है, लेकिन
  4. प्रस्तुत नहीं किया गया "ज़ूम इन" एनीमेशन शुरू होता है। (ध्यान दें कि हम अभी भी SearchViewController पर हैं, और यह एनीमेशन सिर्फ एक साधारण दृश्य एनीमेशन है)
  5. "ज़ूम इन" एनीमेशन समाप्त होता है, animated:NO (जैसा कि एनोमी उल्लेख किया गया है) के साथ DetailViewController प्रस्तुत करता है।
  6. विस्तार दृश्यवर्तक अब प्रस्तुत किया गया है, और पूर्ण स्थान का उपयोग कर रहा है।

ऐसा प्रतीत नहीं होता है कि यूट्यूब ऐप कुछ फैनसीयर कर रहा है, दे दिया गया था कि पूरा वीडियो पेश करने से पहले "ज़ूम इन" एनीमेशन ब्लैक स्क्वायर पर ज़ूम करता है।

+0

अच्छी आंख! यह लैंडस्केप मोड में उस तरह से दिखता है, लेकिन इसे चित्र में आज़माएं। प्रभाव अविश्वसनीय रूप से चिकनी है, थंबनेल के पाठ विवरण अधिक विस्तृत विवरण में संक्रमण के साथ। मुझे लगता है कि वे दो अलग-अलग तकनीकों का उपयोग कर रहे हैं, जहां लैंडस्केप मोड में स्क्रीन को कवर करने के लिए एक ब्लैक बॉक्स का विस्तार किया जाता है और एक मोडल व्यू के साथ बदल दिया जाता है, और पोर्ट्रेट मोड में थंबनेल को अपने मूल दृश्य की सीमाओं को भरने के लिए विस्तारित किया जाता है, तो विस्तार दृश्य है नेविगेशन स्टैक पर कोई एनीमेशन (या शायद सिर्फ एक छोटा क्रॉसफ़ेड) के साथ धक्का दिया। –

+0

बीटीडब्ल्यू, यह उत्तर अब वास्तव में पुराना है, आप इन सभी शांत पूर्ण-स्क्रीन एनिमेशन को स्नैपशॉट्स, कस्टम मोडल/पुश संक्रमण या यहां तक ​​कि कस्टम UIStoryboardSegues के माध्यम से प्राप्त कर सकते हैं – Can

संबंधित मुद्दे