/HWPanModal

HWPanModal presents controller from bottom and drag to dismiss, high customize. iOS13 default modalPresentationStyle. 任意形式的底部弹框动画;头条、知乎、抖音弹出评论效果;地图浮层,iOS13 present默认模态效果。

Primary LanguageObjective-CMIT LicenseMIT

HWPanModal 👍

codebeat badge

HWPanModal is used to present controller and drag to dismiss. Similar with iOS13 default present Modal style.

Inspired by PanModal, thanks.

中文文档说明

My another project for pop controller:HWPopController

Special Thanks

Special Thanks to JetBrains! I use AppCode IDE to develop my open source project.

Snapshoot

Basic Blur background Keyboard handle App demo

Index

Features

  1. Supports any type of UIViewController to present.
  2. Support View which inherit from HWPanModalContentView to present.
  3. Seamless transition between modal and content.
  4. Support two kinds of dismissal gestureRecognizer interaction
    1. Pan gesture direction up&down
    2. Pan gesture direction right, you can swipe on screen edge to dismiss controller.
  5. Support write your own animation for presenting VC.
  6. Support config animation Duration, AnimationOptions, springDamping.
  7. Support config background alpha or blur background. Note: Dynamic change blur effect ONLY works on iOS9.0+.
  8. Show / hide corner, indicator.
  9. Auto handle UIKeyboard show/hide.
  10. Hight customize indicator view.
  11. Touch event response can pass through to presenting VC.
  12. Config presented view shadow style.

More config pls see HWPanModalPresentable.h declare.

What's different between UIViewController and HWPanModalContentView to present ?

From version 0.6.0, this framework support using HWPanModalContentView to present from bottom, that means we can add subview(inherit from HWPanModalContentView) to the target view that you want to show.

The different is HWPanModalContentView is just a view, and support some animations, unlike present ViewController, you will got ViewController life circle, and navigation stack.

HWPanModalContentView limit:

  • Currently not support screen rotation.
  • Not support edge horizontal pan to dismiss.
  • Not support customize presentingVC animation. (There is no presentingVC for view).

TODO

  • Handle keyboard show&dismiss.
  • High customize indicator view.
  • Edge Interactive dismissal can work on full screen and configable distance to left edge.
  • Touch event can response to presenting VC, working on it.
  • Strip the presented view container view, make it can use directly.

Compatibility

iOS 8.0+, support Objective-C & Swift.

Installation

pod 'HWPanModal', '~> 0.6.0'

How to use

How to present UIViewController from bottom

Your UIViewController need to conform HWPanModalPresentable. If you use default, nothing more will be written.

#import <HWPanModal/HWPanModal.h>
@interface HWBaseViewController () <HWPanModalPresentable>

@end

@implementation HWBaseViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

#pragma mark - HWPanModalPresentable
- (PanModalHeight)longFormHeight {
    return PanModalHeightMake(PanModalHeightTypeMaxTopInset, 44);
}
@end

Where you need to present this Controller.

#import <HWPanModal/HWPanModal.h>
[self presentPanModal:[HWBaseViewController new]];

yeah! Easy.

Change state, scrollView contentOffset, reload layout. IMPORTANT!

When You present you Controller, you can change the UI. Refer to UIViewController+Presentation.h.

  • Change the state between short and long form. call - (void)hw_panModalTransitionTo:(PresentationState)state;
  • Change ScrollView ContentOffset. call - (void)hw_panModalSetContentOffset:(CGPoint)offset;
  • Reload layout. call - (void)hw_panModalSetNeedsLayoutUpdate;
    • Note: When your scrollable changed it's contentSize, you MUST reload the layout.

Custom Presenting VC Animation

Some guys want to animate Presenting VC when present/dismiss.

  1. Create object conforms HWPresentingViewControllerAnimatedTransitioning .

    @interface HWMyCustomAnimation : NSObject <HWPresentingViewControllerAnimatedTransitioning>
    
    @end
    
    @implementation HWMyCustomAnimation
    
    
    - (void)presentAnimateTransition:(id<HWPresentingViewControllerContextTransitioning>)transitionContext {
        NSTimeInterval duration = [transitionContext transitionDuration];
        UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
        // replace it.
        [UIView animateWithDuration:duration delay:0 usingSpringWithDamping:0.8 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            fromVC.view.transform = CGAffineTransformMakeScale(0.95, 0.95);
        } completion:^(BOOL finished) {
            
        }];
    }
    
    - (void)dismissAnimateTransition:(id<HWPresentingViewControllerContextTransitioning>)transitionContext {
        // no need for using animating block.
        UIViewController *toVC = [context viewControllerForKey:UITransitionContextToViewControllerKey];
        toVC.view.transform = CGAffineTransformIdentity;
    }
    
    @end
  2. Overwrite below two method.

    - (PresentingViewControllerAnimationStyle)presentingVCAnimationStyle {
        return PresentingViewControllerAnimationStyleCustom;
    }
    
    - (id<HWPresentingViewControllerAnimatedTransitioning>)customPresentingVCAnimation {
        return self.customAnimation;
    }
    
    - (HWMyCustomAnimation *)customAnimation {
        if (!_customAnimation) {
            _customAnimation = [HWMyCustomAnimation new];
        }
        return _customAnimation;
    }

Custom your own indicator view

You just need to create your own UIView, then adopt HWPanModalIndicatorProtocol.

In your presented controller, return it:

- (nullable UIView <HWPanModalIndicatorProtocol> *)customIndicatorView {
    HWTextIndicatorView *textIndicatorView = [HWTextIndicatorView new];
    return textIndicatorView;
}

Here is HWTextIndicatorView code:

@interface HWTextIndicatorView : UIView <HWPanModalIndicatorProtocol>

@end

@interface HWTextIndicatorView ()
@property (nonatomic, strong) UILabel *stateLabel;
@end

@implementation HWTextIndicatorView

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        // init the _stateLabel
        [self addSubview:_stateLabel];
    }
    return self;
}


- (void)didChangeToState:(HWIndicatorState)state {
    switch (state) {
        case HWIndicatorStateNormal: {
            self.stateLabel.text = @"Please pull down to dismiss";
            self.stateLabel.textColor = [UIColor whiteColor];
        }
            break;
        case HWIndicatorStatePullDown: {
            self.stateLabel.text = @"Keep pull down to dismiss";
            self.stateLabel.textColor = [UIColor colorWithRed:1.000 green:0.200 blue:0.000 alpha:1.00];
        }
            break;
    }
}

- (CGSize)indicatorSize {
    return CGSizeMake(200, 18);
}

- (void)setupSubviews {
    self.stateLabel.frame = self.bounds;
}

@end

How to use HWPanModalContentView

You should always inherit from HWPanModalContentView. HWPanModalContentView conforms HWPanModalPresentable like the way using UIViewController.

@interface HWSimplePanModalView : HWPanModalContentView

@end

@implementation HWSimplePanModalView

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        // add view and layout.
    }
    
    return self;
}

// present it.
HWSimplePanModalView *simplePanModalView = [HWSimplePanModalView new];
[simplePanModalView presentInView:nil];

Example

  1. Clone this git.
  2. open the terminal, run pod install
  3. Double click HWPanModal.xcworkspace, and select a target to run.
I have wrote both pure Objective-C & SwiftExamples , for most of the framework functions.

Contact Me

Heath Wang yishu.jay@gmail.com

WeChat

Change Log

Click Me

License

HWPanModal is released under a MIT License. See LICENSE file for details.