Effect:
Set mode customization
- UIModalPresentationStyle is used to customize transitions when setting modes.
typedefNS_ENUM(NSInteger.UIModalPresentationStyle) {
UIModalPresentationFullScreen =0.// Full screen coverage from bottom to top
UIModalPresentationPageSheet.// In portrait it is FullScreen, in landscape it is FormSheet.
UIModalPresentationFormSheet.// Shrinks the window to the middle of the screen. It's the same in Portrait and landscape, but note that the window position will change if the soft keyboard appears in landscape.
UIModalPresentationCurrentContext.// In this mode, the Presenting VC presented in the same manner as the presenting VC's parent.
UIModalPresentationCustom.// Custom view presentation style, consisting of a custom presentation controller and one or more custom animation objects. In accordance with UIViewControllerTransitioningDelegate agreement. Use the view controller's transitioningDelegate to set your custom transformation.
UIModalPresentationOverFullScreen.// If the view is not filled, the underlying view can pass through
UIModalPresentationOverCurrentContext.// All views are passed through
UIModalPresentationPopover.UIModalPresentationNone};Copy the code
- We set up custom modes and proxies in the ViewController.
// Set the proxy and present style
self.transitioningDelegate = self;
self.modalPresentationStyle = UIModalPresentationCustom;
Copy the code
- Add a gesture to slide the previous page
// Add gestures
UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc] init];
pan.delegate = self;
[pan addTarget:self action:@selector(panGestureRecognizerAction:)];
self.view.tag = 10001;
[self.view addGestureRecognizer:pan];
Copy the code
- Gestures handle sliding logic
- (void)panGestureRecognizerAction:(UIPanGestureRecognizer *)pan
{
// Generate percentage
CGFloat process = ([pan translationInView:self.view].y) / ([UIScreen mainScreen].bounds.size.height);
process = MIN(1.0, (MAX(0.0, process)));
// Start sliding
if (pan.state == UIGestureRecognizerStateBegan)
{
self.interactiveTransition = [UIPercentDrivenInteractiveTransition new];
// Trigger the dismiss animation
[self dismissViewControllerAnimated:YES completion:nil];
}
// When the gesture slides
else if (pan.state == UIGestureRecognizerStateChanged) {[self.interactiveTransition updateInteractiveTransition:process];
}
// Swipe to end or cancel
else if (pan.state == UIGestureRecognizerStateEnded
|| pan.state == UIGestureRecognizerStateCancelled)
{
// Slide percentage greater than 0.3 to complete the animation
if (process >= 0.3) {[self.interactiveTransition finishInteractiveTransition];
}
else{[self.interactiveTransition cancelInteractiveTransition];
}
self.interactiveTransition = nil; }}Copy the code
- Implement proxy UIViewControllerTransitioningDelegate
#pragma mark - UIViewControllerTransitioningDelegate
// Sets the properties of the custom class that inherits from UIPresentationController
- (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source
{
ZJPresentationController *presentVC = [[ZJPresentationController alloc]initWithPresentedViewController:presented presentingViewController:presenting];
// Set the height from the top
presentVC.height = STATUSBAR_H;
return presentVC;
}
/ / controller please create animation (returning an implementation class UIViewControllerAnimatedTransitioning agreement)
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{
/ / create the implementation UIViewControllerAnimatedTransitioning agreement (named AnimatedTransitioning)
ZJPresentAnimation *animation = [[ZJPresentAnimation alloc] init];
// Change its state to appear
animation.presented = YES;
return animation;
}
/ / controller destruction of execution of the animation (returning an implementation class UIViewControllerAnimatedTransitioning agreement)
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed{
/ / / / to create UIViewControllerAnimatedTransitioning agreement class (named AnimatedTransitioning)
ZJPresentAnimation *animation = [[ZJPresentAnimation alloc] init];
// Change its state to appear
animation.presented = NO;
return animation;
}
/ / returns an interactive object (achieve UIViewControllerInteractiveTransitioning agreement)
-(id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator{
return self.interactiveTransition;
}
Copy the code
Custom transitions to animation
- Implement UIViewControllerAnimatedTransitioning agreement, contains the animation time and specific implementation.
// Customize the transition animation time
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext{
return 0.34f;
}
// Custom convert to animation concrete implementation
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{
// To which vc
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
// From which vc
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
// Get the transition container view
UIView *contentView = [transitionContext containerView];;
// Remove the previous one
for (UIView *subView in [contentView subviews]) {
if (subView.tag == 1001) { [subView removeFromSuperview]; }}UIView * transView = nil;
// Present
if (_presented) {
// Record the present view
transView = toViewController.view;
}
else {
transView = fromViewController.view;
}
/ / y value
CGFloat y = transView.frame.origin.y;
transView.frame = CGRectMake(0, _presented ?SCREEN_H :y, SCREEN_W.SCREEN_H);
// Black background
UIView *viewCover = [[UIView alloc]initWithFrame:CGRectMake(0.0.SCREEN_W.SCREEN_H)];
viewCover.backgroundColor = [UIColor blackColor];
viewCover.alpha = 0.5;
viewCover.tag = 1001;
[contentView insertSubview:viewCover belowSubview:transView];
if (_presented) {
// Execute the animation process
[UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:0.8 initialSpringVelocity:2 options:UIViewAnimationOptionLayoutSubviews animations:^{
transView.frame = CGRectMake(0, y , SCREEN_W.SCREEN_H);
} completion:^(BOOL finished) {
[transitionContext completeTransition:!transitionContext.transitionWasCancelled];
}];
}
else
{
// Pull down the drawing
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
transView.frame = CGRectMake(0.SCREEN_H.SCREEN_W.SCREEN_H);
viewCover.alpha= 0;
} completion:^(BOOL finished) {
if (!transitionContext.transitionWasCancelled) {
[viewCover removeFromSuperview];
}
[transitionContext completeTransition:!transitionContext.transitionWasCancelled];
}];
}
}
Copy the code
Handling of gesture conflicts
- The way you want to trigger the slide dismiss gesture when you slide the top of the popup ViewController that contains the UITableView.
#pragma mark --UIGestureRecognizerDelegate
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
// Support multiple gestures
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer NS_AVAILABLE_IOS(7 _0)
{
// This method returns YES. If the first gesture and the second are mutually exclusive, the first gesture will be invalidated
if ([otherGestureRecognizer.view isKindOfClass:[UITableView class]]) {
return YES;
}
return NO;
}
Copy the code
- Logic when dealing with UITableView sliding to the top
#pragma mark --UIScrollViewDelegate
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
[self setGestureRecognizerEnable:YES scrollView:scrollView];
}
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self setGestureRecognizerEnable:YES scrollView:scrollView];
}
-(void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
// Gestures are available at the end
[self setGestureRecognizerEnable:YES scrollView:scrollView];
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// Set gesture invalidation when sliding to the top
if (scrollView.contentOffset.y <=0) {[self setGestureRecognizerEnable:NO scrollView:scrollView];
}
else
{
// Gestures are available
[self setGestureRecognizerEnable:YES scrollView:scrollView];
}
}
-(void)setGestureRecognizerEnable:(BOOL)isEnable scrollView:(UIScrollView *)scrollView
{
for (UIGestureRecognizer *gesRec in scrollView.gestureRecognizers) {
if ([gesRec isKindOfClass:[UIPanGestureRecognizer class]]) { gesRec.enabled =isEnable; }}}Copy the code
The way to do that is when you swipe to the top, disable the UITableview gesture, and then trigger the gesture in the current ViewController to slide the gesture on a page.
The DEMO address