New Features of iOS7 ViewController transition (3) Switch of custom View Controller container --- non-interactive
@ Continue with the previous content. This chapter describes how to switch the view VC on the custom ViewController container. the container controller UINavigationController and UITabBarController provided by the system have an NSArray attribute viewControllers. Obviously, the view VC that needs to be switched is stored. similarly, we defineContainerViewController is a direct subclass of UIViewController. It is used as container support. For details about other attributes, see the code. (PS: In the past, I used to switch multiple custom views using VC. I placed a UIScrollView, and then increased the X coordinates of the views of all childViewController by 320, as you can imagine, this is a bad thing. I feel that all the VC instances are materialized once loaded, and will not be released because they are switched to the unavailable ones)
@ Lazy: Use storyboard to create five childVC instances
// ContainerViewController @ interface alert () @ property (strong, nonatomic) FTPhotoSenderViewController * photoSenderViewController; @ property (strong, nonatomic) FTVideoSenderViewController * videoSenderViewController; @ property (strong, nonatomic) FTFileSenderViewController * fileSenderViewController; @ property (strong, nonatomic) FTContactSenderViewController * contactSenderViewCont Roller; @ property (strong, nonatomic) FTClipboardSenderViewController * clipboardSenderViewController; @ property (strong, nonatomic) UIViewController * selectedViewController; // the currently selected VC @ property (strong, nonatomic) NSArray * viewControllers; // childVC array @ property (assign, nonatomic) NSInteger currentControllerIndex; // The name of the currently selected VC array @ end @ implementation FTContainerViewController # pragma mark-ViewLifecy Cle Methods-(void) viewDidLoad {[super viewDidLoad]; // childVC self. photoSenderViewController = [self. storyboard instantiateViewControllerWithIdentifier: @ FTPhotoSenderViewController]; self. videoSenderViewController = [self. storyboard instantiateViewControllerWithIdentifier: @ FTVideoSenderViewController]; self. fileSenderViewController = [self. storyboard instantiateViewControllerWithIdentifier: @ FTFi LeSenderViewController]; self. contactSenderViewController = [self. storyboard instantiateViewControllerWithIdentifier: @ FTContactSenderViewController]; self. clipboardSenderViewController = [self. storyboard instantiateViewControllerWithIdentifier: @ FTClipboardSenderViewController]; // store the array self of childVC. viewControllers = @ [_ photoSenderViewController, _ videoSenderViewController, _ fileSenderViewController, _ ContactSenderViewController, _ clipboardSenderViewController]; // VC self. selectedViewController = self. selectedViewController with the default subscript 0?: Self. viewControllers [0]; self. currentControllerIndex = 0 ;}
@ Still, implement the Animator class of the UIViewControllerAnimatedTransitioning protocol, but change the animation effect inside and use the spring animation effect added by iOS7:
# Import FTMthTransitionAnimator. h @ implementation FTMthTransitionAnimatorstatic CGFloat const kChildViewPadding = 16; static CGFloat const kDamping = 0.5; // the damping parameter represents the elastic damping. As the damping value approaches 0.0, the elasticity of the animation becomes more and more obvious, if the damping value is set to 1.0, the view animation will not have an elastic effect static CGFloat const kInitialSpringVelocity = 0.5; // initialize the Spring Speed-(NSTimeInterval) transitionDuration :( id
) TransitionContext {return 1.0;}-(void) animateTransition :( id
) TransitionContext {/***-viewControllerForKey: we can access the two viewcontrollers in the transition through this interface. *-ContainerView: containerView of two viewcontrollers. *-InitialFrameForViewController and finalFrameForViewController are the frames of each ViewController at the beginning and end of the transition. */UIViewController * fromViewController = [transitionContext viewControllerForKey: role]; UIViewController * toViewController = [transitionContext viewControllerForKey: role]; [[transitionContext containerView] addSubview: toViewController. view]; toViewController. view. alpha = 0; BOOL goingRight = ([transitionContext initialFrameForV IewController: toViewController]. origin. x <[transitionContext finalFrameForViewController: toViewController]. origin. x); CGFloat transDistance = [transitionContext containerView]. bounds. size. width + kChildViewPadding; CGAffineTransform transform = CGAffineTransformMakeTranslation (goingRight? TransDistance:-transDistance, 0); // CGAffineTransformInvert reverses toViewController. view. transform = CGAffineTransformInvert (transform); // toViewController. view. transform = CGAffineTransformTranslate (toViewController. view. transform, (goingRight? TransDistance:-transDistance), 0);/*** ---------- spring animation... ------- * use the time series curve 'animations' described by the spring motion '. When 'dampingratio 'is set to 1, the animation slows down smoothly until its final model value does not oscillate. When the damping ratio is less than 1 to completely stop, the system will oscillate more and more. You can use the initial velocity of the spring to specify the speed before the object at the end of the simulation spring is moved to attach it. This is a Unit coordinate system, where 1 refers to the animation of the total distance of travel in the second place. So, if you change the position of an object from 200PT in this animation, and the animation you want appears as if the object is moving, before the animation starts in 100PT/s, you will pass the 0.5. You usually want to pass the speed of 0. */[UIView Duration: [self transitionDuration: transitionContext] delay: 0 usingSpringWithDamping: kDamping initialSpringVelocity: kInitialSpringVelocity options: 0x00 animations: ^ {fromViewController. view. transform = transform; fromViewController. view. alpha = 0; // CGAffineTransformIdentity reset, initialize toViewController. view. transform = CGAffineTransformIdentity; toViewController. view. alpha = 1 ;} Completion: ^ (BOOL finished) {fromViewController. view. transform = CGAffineTransformIdentity; // declare the end of the transition --> remember, do not forget to call completeTransition at the end of the transition: This method. [TransitionContext completeTransition :! [TransitionContext transitionWasCancelled];}] ;}@ end
@ The following code is the key to implementing custom container switching. generally, when we use the built-in class of the system, the system framework creates a transfer context object for us and passes it to the animation controller. However, in this case, we need to customize the transfer animation, so we need to assume the responsibility of the system framework and create the transfer context object on our own.
@ Interface FTMthTransitionContext: NSObject
-(Instancetype) parameters :( UIViewController *) fromViewController toViewController :( UIViewController *) toViewController goingRight :( BOOL) goingRight; @ property (nonatomic, copy) void (^ completionBlock) (BOOL didComplete ); @ property (nonatomic, assign, getter = isAnimated) BOOL animated; @ property (nonatomic, assign, getter = isInteractive) BOOL interactive; // interactive @ property (nonatomic, stro Ng) NSDictionary * privateViewControllers; @ property (nonatomic, assign) CGRect regular; @ property (nonatomic, assign) CGRect regular; @ property (nonatomic, assign) CGRect privateDisappearingToRect; @ property (nonatomic, assign) CGRect privateAppearingToRect; @ property (nonatomic, weak) UIView * containerView; @ property (nonatomic, assign) UIModalPresentationStyle pres EntationStyle; @ end @ implementation FTMthTransitionContext-(instancetype) usage :( UIViewController *) fromViewController toViewController :( UIViewController *) toViewController goingRight :( BOOL) goingRight {if (self = [super init]) {self. presentationStyle = UIModalPresentationCustom; self. containerView = fromViewController. view. superview; self. privateViewControllers =@{ UITransitio NContextFromViewControllerKey: fromViewController, UITransitionContextToViewControllerKey: toViewController,}; // Set the view frame properties which make sense in our specialized ContainerViewController context. views appear from and disappear to the sides, corresponding to where the icon buttons are positioned. so tapping a button to the right of the currently selected, makes the view disappear The left and the new view appear from the right. the animator object can choose to use this to determine whether the transition shoshould be going left to right, or right to left, for example. CGFloat travelDistance = (goingRight? -Self. containerView. bounds. size. width: self. containerView. bounds. size. width); self. privateDisappearingFromRect = self. privateAppearingToRect = self. containerView. bounds; self. privateDisappearingToRect = CGRectOffset (self. containerView. bounds, travelDistance, 0); self. privateAppearingFromRect = CGRectOffset (self. containerView. bounds,-travelDistance, 0);} return self;}-(CGRect) initialFrameForViewController :( UIViewController *) viewController {if (viewController = [self viewControllerForKey: Signature]) {return self. privateDisappearingFromRect;} else {return self. privateAppearingFromRect;}-(CGRect) finalFrameForViewController :( UIViewController *) viewController {if (viewController = [self viewControllerForKey: UITransitionContextFromViewControllerKey]) {return self. privateDisappearingToRect;} else {return self. privateAppearingToRect; }}- (UIViewController *) viewControllerForKey :( NSString *) key {return self. privateViewControllers [key];}-(void) completeTransition :( BOOL) didComplete {if (self. completionBlock) {self. completionBlock (didComplete) ;}// NO is returned for non-interactive operations. If NO interaction is allowed, the operation progress cannot be canceled-(BOOL) transitionWasCancelled {return NO ;} // non-interactive, no operation directly. Only interaction is required. The following three protocol methods are meaningful. You can refer to the interaction controller defined by the system. // @ interface UIPercentDrivenInteractiveTransition: NSObject
-(Void) updateInteractiveTransition :( CGFloat) percentComplete {}-(void) finishInteractiveTransition {}-(void) cancelInteractiveTransition {} @ end
@ OK: the preparations are complete. To follow the UIScrollView sliding switch, but because the current display is non-interactive, we define a swip gesture.
UISwipeGestureRecognizer *leftGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swapController:)]; [leftGesture setDirection:UISwipeGestureRecognizerDirectionLeft]; [self.view addGestureRecognizer:leftGesture]; UISwipeGestureRecognizer *rightGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swapController:)]; [rightGesture setDirection:UISwipeGestureRecognizerDirectionRight]; [self.view addGestureRecognizer:rightGesture];
// Response gesture method-(void) swapViewControllers :( UISwipeGestureRecognizer *) swipeGestureRecognizer {if (swipeGestureRecognizer. direction = uiswipegesturerecognizerdireleft) {if (_ currentControllerIndex <4) {_ currentControllerIndex ++;} NSLog (@ _ currentControllerIndex = % ld, (long) _ currentControllerIndex ); UIViewController * selectedViewController = self. viewControllers [_ currentControllerIndex]; NSLog (@ % S __% d __| % @ ,__ FUNCTION __,__ LINE __, @ right); self. selectedViewController = selectedViewController;} else if (swipeGestureRecognizer. direction = uiswipegesturerecognizerdireright) {NSLog (@ % s __% d __|%@,__ FUNCTION __,__ LINE __, @ left); if (_ currentControllerIndex> 0) {_ currentControllerIndex --;} UIViewController * selectedViewController = self. viewControllers [_ currentControllerIndex]; self. selectedView Controller = selectedViewController; }}// rewrite the setter-(void) setSelectedViewController of selectedViewController :( UIViewController *) selectedViewController {NSParameterAssert (selectedViewController); [self _ Controller: selectedViewController]; _ selectedViewController = selectedViewController;} // switch operation (custom, Lenovo I switched to the Netease tab bar in the previous article, the transitionFromViewController provided by the system is a truth)-(void) _ transitionToChil DViewController :( UIViewController *) toViewController {UIViewController * fromViewController = self. childViewControllers. count> 0? Self. childViewControllers [0]: nil; if (toViewController = fromViewController) {return;} UIView * toView = toViewController. view; [toView setTranslatesAutoresizingMaskIntoConstraints: YES]; toView. autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; toView. frame = self. view. bounds; // For custom container switching, addChildViewController is the key. It ensures that the VC you want to display can be loaded into the container. // the so-called animation and context, just for the animation effect of the transfer // Therefore, even if you use UIScrollView to switch, addChildViewController cannot be missing. Remember! Remember! [FromViewController willMoveToParentViewController: nil]; [self addChildViewController: toViewController]; if (! FromViewController) {[self. view addSubview: toViewController. view]; [toViewController didMoveToParentViewController: self]; return;} // Animator FTMthTransitionAnimator * transitionAnimator = [[FTMthTransitionAnimator alloc] init]; NSUInteger fromIndex = [self. viewControllers indexOfObject: fromViewController]; NSUInteger toIndex = [self. viewControllers indexOfObject: toViewController]; // Context FTMthTransitionContext * transitionContext = [[internalloc] metadata: fromViewController toViewController: toViewController goingRight :( toIndex> fromIndex)]; transitionContext. animated = YES; transitionContext. interactive = NO; transitionContext. completionBlock = ^ (BOOL didComplete) {// because it is non-interactive, fromVC can directly remove its parent's children controllers array [fromViewController. view removeFromSuperview]; [fromViewController Preview]; [toViewController preview: self]; if ([Response respondsToSelector: @ selector (animationEnded :)]) {[Define animationEnded: didComplete];}; // transition animation must be based on the transition Context. Because we use a custom Context, we need to manually set [transitionAnimator animateTransition: transitionContext];}
Success.
The above shows a non-interactive switch of a basic custom container. What about the interactive switch? From the above definition of gesture as swip rather than pan, we can also see that non-interactive transfer does not fully implement the paging Effect of UIScrollView, fromVC and toVC are switched in the form of similar percentages, because we lack an interactive controller. in a custom container, the system does not provide the protocol for returning the interactive controller to us. I have found a lot of information and have not found a clear method. I think, similar to implementing the transfer context, the interactive protocol should be customized based on the system method. we need to think about how the system builds the environment.
The Demo of the response will be provided later. Currently, the study is .......