添加HWPanModal和FLAnimatedImage
This commit is contained in:
37
Pods/HWPanModal/Sources/View/HWBackgroundConfig.h
generated
Normal file
37
Pods/HWPanModal/Sources/View/HWBackgroundConfig.h
generated
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// HWBackgroundConfig.h
|
||||
// Pods
|
||||
//
|
||||
// Created by heath wang on 2020/4/17.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSUInteger, HWBackgroundBehavior) {
|
||||
HWBackgroundBehaviorDefault, // use background alpha
|
||||
HWBackgroundBehaviorSystemVisualEffect, // use system UIVisualEffect object
|
||||
HWBackgroundBehaviorCustomBlurEffect, // use custom blur
|
||||
};
|
||||
|
||||
@interface HWBackgroundConfig : NSObject
|
||||
|
||||
@property (nonatomic, assign) HWBackgroundBehavior backgroundBehavior;
|
||||
// ONLY works for backgroundBehavior = HWBackgroundBehaviorDefault
|
||||
@property (nonatomic, assign) CGFloat backgroundAlpha; // default is 0.7
|
||||
// ONLY works for backgroundBehavior = HWBackgroundBehaviorSystemVisualEffect
|
||||
@property (nonatomic, strong) UIVisualEffect *visualEffect; // default is UIBlurEffectStyleLight
|
||||
|
||||
// ONLY works for backgroundBehavior = HWBackgroundBehaviorCustomBlurEffect
|
||||
@property (nonatomic, strong) UIColor *blurTintColor; // default is white color
|
||||
@property (nonatomic, assign) CGFloat backgroundBlurRadius; // default is 10
|
||||
|
||||
- (instancetype)initWithBehavior:(HWBackgroundBehavior)backgroundBehavior;
|
||||
|
||||
+ (instancetype)configWithBehavior:(HWBackgroundBehavior)backgroundBehavior;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
57
Pods/HWPanModal/Sources/View/HWBackgroundConfig.m
generated
Normal file
57
Pods/HWPanModal/Sources/View/HWBackgroundConfig.m
generated
Normal file
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// HWBackgroundConfig.m
|
||||
// Pods
|
||||
//
|
||||
// Created by heath wang on 2020/4/17.
|
||||
//
|
||||
|
||||
#import "HWBackgroundConfig.h"
|
||||
|
||||
@implementation HWBackgroundConfig
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.backgroundBehavior = HWBackgroundBehaviorDefault;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBehavior:(HWBackgroundBehavior)backgroundBehavior {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.backgroundBehavior = backgroundBehavior;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)configWithBehavior:(HWBackgroundBehavior)backgroundBehavior {
|
||||
return [[self alloc] initWithBehavior:backgroundBehavior];
|
||||
}
|
||||
|
||||
#pragma mark - Setter
|
||||
|
||||
- (void)setBackgroundBehavior:(HWBackgroundBehavior)backgroundBehavior {
|
||||
_backgroundBehavior = backgroundBehavior;
|
||||
|
||||
switch (backgroundBehavior) {
|
||||
case HWBackgroundBehaviorSystemVisualEffect: {
|
||||
self.visualEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
|
||||
}
|
||||
break;
|
||||
case HWBackgroundBehaviorCustomBlurEffect: {
|
||||
self.backgroundBlurRadius = 10;
|
||||
self.blurTintColor = [UIColor whiteColor];
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
self.backgroundAlpha = 0.7;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
41
Pods/HWPanModal/Sources/View/HWDimmedView.h
generated
Normal file
41
Pods/HWPanModal/Sources/View/HWDimmedView.h
generated
Normal file
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// HWDimmedView.h
|
||||
// HWPanModal
|
||||
//
|
||||
// Created by heath wang on 2019/4/26.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
@class HWBackgroundConfig;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSInteger, DimState) {
|
||||
DimStateMax,
|
||||
DimStateOff,
|
||||
DimStatePercent,
|
||||
};
|
||||
|
||||
typedef void(^didTap)(UITapGestureRecognizer *recognizer);
|
||||
|
||||
@interface HWDimmedView : UIView
|
||||
|
||||
@property (nonatomic, assign) DimState dimState;
|
||||
@property (nonatomic, assign) CGFloat percent;
|
||||
@property (nullable, nonatomic, copy) didTap tapBlock;
|
||||
@property (nullable, nonatomic, strong) UIColor *blurTintColor;
|
||||
|
||||
@property (nonatomic, readonly) HWBackgroundConfig *backgroundConfig;
|
||||
|
||||
/**
|
||||
* init with the max dim alpha & max blur radius.
|
||||
*/
|
||||
- (instancetype)initWithDimAlpha:(CGFloat)dimAlpha blurRadius:(CGFloat)blurRadius;
|
||||
|
||||
- (instancetype)initWithBackgroundConfig:(HWBackgroundConfig *)backgroundConfig;
|
||||
|
||||
- (void)reloadConfig:(HWBackgroundConfig *)backgroundConfig;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
202
Pods/HWPanModal/Sources/View/HWDimmedView.m
generated
Normal file
202
Pods/HWPanModal/Sources/View/HWDimmedView.m
generated
Normal file
@@ -0,0 +1,202 @@
|
||||
//
|
||||
// HWDimmedView.m
|
||||
// HWPanModal
|
||||
//
|
||||
// Created by heath wang on 2019/4/26.
|
||||
//
|
||||
|
||||
#import "HWDimmedView.h"
|
||||
#import "HWVisualEffectView.h"
|
||||
#import "HWBackgroundConfig.h"
|
||||
|
||||
@interface HWDimmedView ()
|
||||
|
||||
@property (nonatomic, strong) UIView *backgroundView;
|
||||
@property (nonatomic, strong) HWVisualEffectView *blurView;
|
||||
@property (nonatomic, strong) HWBackgroundConfig *backgroundConfig;
|
||||
|
||||
@property (nonatomic, assign) CGFloat maxDimAlpha;
|
||||
@property (nonatomic, assign) CGFloat maxBlurRadius;
|
||||
@property (nonatomic, assign) CGFloat maxBlurTintAlpha;
|
||||
@property (nonatomic, strong) UITapGestureRecognizer *tapGestureRecognizer;
|
||||
@property (nonatomic, assign) BOOL isBlurMode;
|
||||
|
||||
@end
|
||||
|
||||
@implementation HWDimmedView
|
||||
|
||||
- (instancetype)initWithDimAlpha:(CGFloat)dimAlpha blurRadius:(CGFloat)blurRadius {
|
||||
self = [super initWithFrame:CGRectZero];
|
||||
if (self) {
|
||||
_maxBlurRadius = blurRadius;
|
||||
_maxDimAlpha = dimAlpha;
|
||||
[self commonInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
_maxDimAlpha = 0.7;
|
||||
[self commonInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBackgroundConfig:(HWBackgroundConfig *)backgroundConfig {
|
||||
self = [super initWithFrame:CGRectZero];
|
||||
if (self) {
|
||||
self.backgroundConfig = backgroundConfig;
|
||||
_maxDimAlpha = backgroundConfig.backgroundAlpha;
|
||||
_maxBlurRadius = backgroundConfig.backgroundBlurRadius;
|
||||
_blurTintColor = backgroundConfig.blurTintColor;
|
||||
|
||||
[self commonInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)commonInit {
|
||||
_dimState = DimStateOff;
|
||||
_maxBlurTintAlpha = 0.5;
|
||||
// default, max alpha.
|
||||
_percent = 1;
|
||||
_tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapView)];
|
||||
[self addGestureRecognizer:_tapGestureRecognizer];
|
||||
|
||||
[self setupView];
|
||||
}
|
||||
|
||||
- (void)setupView {
|
||||
self.isBlurMode = self.maxBlurRadius > 0 || self.backgroundConfig.visualEffect;
|
||||
if (self.isBlurMode) {
|
||||
[self addSubview:self.blurView];
|
||||
[self configBlurView];
|
||||
} else {
|
||||
[self addSubview:self.backgroundView];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - layout
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
// not call getter.
|
||||
_blurView.frame = self.bounds;
|
||||
_backgroundView.frame = self.bounds;
|
||||
}
|
||||
|
||||
#pragma mark - touch action
|
||||
|
||||
- (void)didTapView {
|
||||
self.tapBlock ? self.tapBlock(self.tapGestureRecognizer) : nil;
|
||||
}
|
||||
|
||||
#pragma mark - public method
|
||||
|
||||
- (void)reloadConfig:(HWBackgroundConfig *)backgroundConfig {
|
||||
|
||||
for (UIView *view in self.subviews) {
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
|
||||
self.backgroundConfig = backgroundConfig;
|
||||
_maxDimAlpha = backgroundConfig.backgroundAlpha;
|
||||
_maxBlurRadius = backgroundConfig.backgroundBlurRadius;
|
||||
_blurTintColor = backgroundConfig.blurTintColor;
|
||||
|
||||
[self setupView];
|
||||
|
||||
DimState state = self.dimState;
|
||||
self.dimState = state;
|
||||
}
|
||||
|
||||
#pragma mark - private method
|
||||
|
||||
- (void)updateAlpha {
|
||||
CGFloat alpha = 0;
|
||||
CGFloat blurRadius = 0;
|
||||
CGFloat blurTintAlpha = 0;
|
||||
|
||||
switch (self.dimState) {
|
||||
case DimStateMax:{
|
||||
alpha = self.maxDimAlpha;
|
||||
blurRadius = self.maxBlurRadius;
|
||||
blurTintAlpha = self.maxBlurTintAlpha;
|
||||
}
|
||||
break;
|
||||
case DimStatePercent: {
|
||||
CGFloat percent = MAX(0, MIN(1.0f, self.percent));
|
||||
alpha = self.maxDimAlpha * percent;
|
||||
blurRadius = self.maxBlurRadius * percent;
|
||||
blurTintAlpha = self.maxBlurTintAlpha * percent;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (self.isBlurMode) {
|
||||
if (self.backgroundConfig.visualEffect) return;
|
||||
|
||||
self.blurView.blurRadius = blurRadius;
|
||||
self.blurView.colorTintAlpha = blurTintAlpha;
|
||||
} else {
|
||||
self.backgroundView.alpha = alpha;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)configBlurView {
|
||||
if (self.backgroundConfig.visualEffect) {
|
||||
[_blurView updateBlurEffect:self.backgroundConfig.visualEffect];
|
||||
} else {
|
||||
_blurView.colorTint = [UIColor whiteColor];
|
||||
_blurView.colorTintAlpha = self.maxBlurTintAlpha;
|
||||
_blurView.userInteractionEnabled = NO;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Setter
|
||||
|
||||
- (void)setDimState:(DimState)dimState {
|
||||
_dimState = dimState;
|
||||
[self updateAlpha];
|
||||
}
|
||||
|
||||
- (void)setPercent:(CGFloat)percent {
|
||||
_percent = percent;
|
||||
[self updateAlpha];
|
||||
}
|
||||
|
||||
#pragma mark - Getter
|
||||
|
||||
- (UIView *)backgroundView {
|
||||
if (!_backgroundView) {
|
||||
_backgroundView = [UIView new];
|
||||
_backgroundView.userInteractionEnabled = NO;
|
||||
_backgroundView.alpha = 0;
|
||||
_backgroundView.backgroundColor = [UIColor blackColor];
|
||||
}
|
||||
return _backgroundView;
|
||||
}
|
||||
|
||||
- (HWVisualEffectView *)blurView {
|
||||
if (!_blurView) {
|
||||
_blurView = [HWVisualEffectView new];
|
||||
}
|
||||
return _blurView;
|
||||
}
|
||||
|
||||
#pragma mark - Setter
|
||||
|
||||
- (void)setBlurTintColor:(UIColor *)blurTintColor {
|
||||
_blurTintColor = blurTintColor;
|
||||
_blurView.colorTint = _blurTintColor;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
34
Pods/HWPanModal/Sources/View/HWPanContainerView.h
generated
Normal file
34
Pods/HWPanModal/Sources/View/HWPanContainerView.h
generated
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// HWPanContainerView.h
|
||||
// HWPanModal
|
||||
//
|
||||
// Created by heath wang on 2019/4/26.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface HWPanContainerView : UIView
|
||||
|
||||
/// the presented view should add to the content view.
|
||||
@property (nonatomic, strong, readonly) UIView *contentView;
|
||||
|
||||
- (instancetype)initWithPresentedView:(UIView *)presentedView frame:(CGRect)frame;
|
||||
|
||||
- (void)updateShadow:(UIColor *)shadowColor
|
||||
shadowRadius:(CGFloat)shadowRadius
|
||||
shadowOffset:(CGSize)shadowOffset
|
||||
shadowOpacity:(float)shadowOpacity;
|
||||
|
||||
- (void)clearShadow;
|
||||
|
||||
@end
|
||||
|
||||
@interface UIView (PanContainer)
|
||||
|
||||
@property (nullable, nonatomic, strong, readonly) HWPanContainerView *panContainerView;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
60
Pods/HWPanModal/Sources/View/HWPanContainerView.m
generated
Normal file
60
Pods/HWPanModal/Sources/View/HWPanContainerView.m
generated
Normal file
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// HWPanContainerView.m
|
||||
// HWPanModal
|
||||
//
|
||||
// Created by heath wang on 2019/4/26.
|
||||
//
|
||||
|
||||
#import "HWPanContainerView.h"
|
||||
|
||||
@interface HWPanContainerView ()
|
||||
|
||||
@property (nonatomic, strong) UIView *contentView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation HWPanContainerView
|
||||
|
||||
- (instancetype)initWithPresentedView:(UIView *)presentedView frame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
_contentView = [UIView new];
|
||||
|
||||
_contentView.frame = self.bounds;
|
||||
[self addSubview:_contentView];
|
||||
[_contentView addSubview:presentedView];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)updateShadow:(UIColor *)shadowColor shadowRadius:(CGFloat)shadowRadius shadowOffset:(CGSize)shadowOffset shadowOpacity:(float)shadowOpacity {
|
||||
|
||||
self.layer.shadowColor = shadowColor.CGColor;
|
||||
self.layer.shadowRadius = shadowRadius;
|
||||
self.layer.shadowOffset = shadowOffset;
|
||||
self.layer.shadowOpacity = shadowOpacity;
|
||||
}
|
||||
|
||||
- (void)clearShadow {
|
||||
self.layer.shadowColor = nil;
|
||||
self.layer.shadowRadius = 3.0;
|
||||
self.layer.shadowOffset = CGSizeZero;
|
||||
self.layer.shadowOpacity = 0.0;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation UIView (PanContainer)
|
||||
|
||||
- (HWPanContainerView *)panContainerView {
|
||||
for (UIView *subview in self.subviews) {
|
||||
if ([subview isKindOfClass:HWPanContainerView.class]) {
|
||||
return (HWPanContainerView *) subview;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
19
Pods/HWPanModal/Sources/View/HWPanIndicatorView.h
generated
Normal file
19
Pods/HWPanModal/Sources/View/HWPanIndicatorView.h
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// HWPanIndicatorView.h
|
||||
// HWPanModal
|
||||
//
|
||||
// Created by heath wang on 2019/5/16.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <HWPanModal/HWPanModalIndicatorProtocol.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface HWPanIndicatorView : UIView <HWPanModalIndicatorProtocol>
|
||||
|
||||
@property (nonnull, nonatomic, strong) UIColor *indicatorColor;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
114
Pods/HWPanModal/Sources/View/HWPanIndicatorView.m
generated
Normal file
114
Pods/HWPanModal/Sources/View/HWPanIndicatorView.m
generated
Normal file
@@ -0,0 +1,114 @@
|
||||
//
|
||||
// HWPanIndicatorView.m
|
||||
// HWPanModal
|
||||
//
|
||||
// Created by heath wang on 2019/5/16.
|
||||
//
|
||||
|
||||
#import "UIView+HW_Frame.h"
|
||||
#import <HWPanModal/HWPanIndicatorView.h>
|
||||
|
||||
@interface HWPanIndicatorView ()
|
||||
|
||||
@property (nonatomic, strong) UIView *leftView;
|
||||
@property (nonatomic, strong) UIView *rightView;
|
||||
|
||||
@property (nonatomic, assign) HWIndicatorState state;
|
||||
|
||||
@end
|
||||
|
||||
@implementation HWPanIndicatorView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:CGRectZero];
|
||||
if (self) {
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
[self addSubview:self.leftView];
|
||||
[self addSubview:self.rightView];
|
||||
self.indicatorColor = [UIColor colorWithRed:0.792 green:0.788 blue:0.812 alpha:1.00];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)animate:(void (^)(void))animations {
|
||||
[UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:1 initialSpringVelocity:1 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseOut animations:animations completion:^(BOOL finished) {
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - HWPanModalIndicatorProtocol
|
||||
|
||||
- (void)didChangeToState:(HWIndicatorState)state {
|
||||
self.state = state;
|
||||
}
|
||||
|
||||
- (CGSize)indicatorSize {
|
||||
return CGSizeMake(34, 13);
|
||||
}
|
||||
|
||||
- (void)setupSubviews {
|
||||
CGSize size = [self indicatorSize];
|
||||
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, size.width, size.height);
|
||||
CGFloat height = 5;
|
||||
CGFloat correction = height / 2;
|
||||
|
||||
self.leftView.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame) / 2 + correction, height);
|
||||
self.leftView.hw_centerY = self.hw_height / 2;
|
||||
self.leftView.layer.cornerRadius = MIN(self.leftView.hw_width, self.leftView.hw_height) / 2;
|
||||
|
||||
self.rightView.frame = CGRectMake(CGRectGetWidth(self.frame) / 2 - correction, 0, CGRectGetWidth(self.frame) / 2 + correction, height);
|
||||
self.rightView.hw_centerY = self.hw_height / 2;
|
||||
self.rightView.layer.cornerRadius = MIN(self.rightView.hw_width, self.rightView.hw_height) / 2;
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - Getter
|
||||
|
||||
- (UIView *)leftView {
|
||||
if (!_leftView) {
|
||||
_leftView = [UIView new];
|
||||
}
|
||||
return _leftView;
|
||||
}
|
||||
|
||||
- (UIView *)rightView {
|
||||
if (!_rightView) {
|
||||
_rightView = [UIView new];
|
||||
}
|
||||
return _rightView;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Setter
|
||||
|
||||
- (void)setIndicatorColor:(UIColor *)indicatorColor {
|
||||
_indicatorColor = indicatorColor;
|
||||
self.leftView.backgroundColor = indicatorColor;
|
||||
self.rightView.backgroundColor = indicatorColor;
|
||||
}
|
||||
|
||||
- (void)setState:(HWIndicatorState)state {
|
||||
|
||||
_state = state;
|
||||
|
||||
switch (state) {
|
||||
case HWIndicatorStateNormal: {
|
||||
CGFloat angle = 20 * M_PI / 180;
|
||||
[self animate:^{
|
||||
self.leftView.transform = CGAffineTransformMakeRotation(angle);
|
||||
self.rightView.transform = CGAffineTransformMakeRotation(-angle);
|
||||
}];
|
||||
}
|
||||
break;
|
||||
case HWIndicatorStatePullDown: {
|
||||
[self animate:^{
|
||||
self.leftView.transform = CGAffineTransformIdentity;
|
||||
self.rightView.transform = CGAffineTransformIdentity;
|
||||
}];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
36
Pods/HWPanModal/Sources/View/HWPanModalIndicatorProtocol.h
generated
Normal file
36
Pods/HWPanModal/Sources/View/HWPanModalIndicatorProtocol.h
generated
Normal file
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// HWPanModalIndicatorProtocol.h
|
||||
// HWPanModal
|
||||
//
|
||||
// Created by heath wang on 2019/8/9.
|
||||
//
|
||||
|
||||
typedef NS_ENUM(NSUInteger, HWIndicatorState) {
|
||||
HWIndicatorStateNormal NS_SWIFT_NAME(normal), // origin state
|
||||
HWIndicatorStatePullDown NS_SWIFT_NAME(pull), // drag down state
|
||||
};
|
||||
|
||||
static CGFloat const kIndicatorYOffset = 5;
|
||||
|
||||
@protocol HWPanModalIndicatorProtocol <NSObject>
|
||||
|
||||
/**
|
||||
* When user drags, the state will change.
|
||||
* You can change your UI here.
|
||||
* @param state The state when drag changed.
|
||||
*/
|
||||
- (void)didChangeToState:(HWIndicatorState)state;
|
||||
|
||||
/**
|
||||
* Tell the size of the indicator.
|
||||
*/
|
||||
- (CGSize)indicatorSize;
|
||||
|
||||
/**
|
||||
* You can layout your UI here if you need.
|
||||
* This method called when indicator added to super view
|
||||
*/
|
||||
- (void)setupSubviews;
|
||||
|
||||
@end
|
||||
|
||||
26
Pods/HWPanModal/Sources/View/HWPanModalShadow.h
generated
Normal file
26
Pods/HWPanModal/Sources/View/HWPanModalShadow.h
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// HWPanModalShadow.h
|
||||
// Pods
|
||||
//
|
||||
// Created by hb on 2023/8/3.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface HWPanModalShadow : NSObject
|
||||
|
||||
@property (nonatomic, strong) UIColor *shadowColor;
|
||||
@property (nonatomic, assign) CGFloat shadowRadius;
|
||||
@property (nonatomic, assign) CGSize shadowOffset;
|
||||
@property (nonatomic, assign) CGFloat shadowOpacity;
|
||||
|
||||
- (instancetype)initWithColor:(UIColor *)shadowColor shadowRadius:(CGFloat)shadowRadius shadowOffset:(CGSize)shadowOffset shadowOpacity:(CGFloat)shadowOpacity;
|
||||
|
||||
+ (instancetype)panModalShadowNil;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
28
Pods/HWPanModal/Sources/View/HWPanModalShadow.m
generated
Normal file
28
Pods/HWPanModal/Sources/View/HWPanModalShadow.m
generated
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// HWPanModalShadow.m
|
||||
// Pods
|
||||
//
|
||||
// Created by hb on 2023/8/3.
|
||||
//
|
||||
|
||||
#import "HWPanModalShadow.h"
|
||||
|
||||
@implementation HWPanModalShadow
|
||||
|
||||
- (instancetype)initWithColor:(UIColor *)shadowColor shadowRadius:(CGFloat)shadowRadius shadowOffset:(CGSize)shadowOffset shadowOpacity:(CGFloat)shadowOpacity {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_shadowColor = shadowColor;
|
||||
_shadowRadius = shadowRadius;
|
||||
_shadowOffset = shadowOffset;
|
||||
_shadowOpacity = shadowOpacity;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)panModalShadowNil {
|
||||
return [[HWPanModalShadow alloc] initWithColor:[UIColor clearColor] shadowRadius:0 shadowOffset:CGSizeZero shadowOpacity:0];
|
||||
}
|
||||
|
||||
@end
|
||||
39
Pods/HWPanModal/Sources/View/HWVisualEffectView.h
generated
Normal file
39
Pods/HWPanModal/Sources/View/HWVisualEffectView.h
generated
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// HWVisualEffectView.h
|
||||
// HWPanModal
|
||||
//
|
||||
// Created by heath wang on 2019/6/14.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface HWVisualEffectView : UIVisualEffectView
|
||||
|
||||
/**
|
||||
* tint color
|
||||
* default is nil
|
||||
*/
|
||||
@property (nullable, nonatomic, strong) UIColor *colorTint;
|
||||
/**
|
||||
* tint color alpha
|
||||
* default is 0.0
|
||||
*/
|
||||
@property (nonatomic, assign) CGFloat colorTintAlpha;
|
||||
/**
|
||||
* blur radius, change it to make blur affect
|
||||
* default is 0.0
|
||||
*/
|
||||
@property (nonatomic, assign) CGFloat blurRadius;
|
||||
/**
|
||||
* scale factor.
|
||||
* default is 1.0
|
||||
*/
|
||||
@property (nonatomic, assign) CGFloat scale;
|
||||
|
||||
- (void)updateBlurEffect:(UIVisualEffect *)effect;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
111
Pods/HWPanModal/Sources/View/HWVisualEffectView.m
generated
Normal file
111
Pods/HWPanModal/Sources/View/HWVisualEffectView.m
generated
Normal file
@@ -0,0 +1,111 @@
|
||||
//
|
||||
// HWVisualEffectView.m
|
||||
// HWPanModal
|
||||
//
|
||||
// Created by heath wang on 2019/6/14.
|
||||
//
|
||||
|
||||
#import "HWVisualEffectView.h"
|
||||
|
||||
NSString * const kInternalCustomBlurEffect = @"_UICustomBlurEffect";
|
||||
NSString * const kHWBlurEffectColorTintKey = @"colorTint";
|
||||
NSString * const kHWBlurEffectColorTintAlphaKey = @"colorTintAlpha";
|
||||
NSString * const kHWBlurEffectBlurRadiusKey = @"blurRadius";
|
||||
NSString * const kHWBlurEffectScaleKey = @"scale";
|
||||
|
||||
@interface HWVisualEffectView ()
|
||||
|
||||
@property (nonatomic, strong) UIVisualEffect *blurEffect;
|
||||
|
||||
@end
|
||||
|
||||
@implementation HWVisualEffectView
|
||||
|
||||
@synthesize colorTint = _colorTint;
|
||||
@synthesize colorTintAlpha = _colorTintAlpha;
|
||||
@synthesize blurRadius = _blurRadius;
|
||||
@synthesize scale = _scale;
|
||||
|
||||
#pragma mark - init
|
||||
|
||||
- (instancetype)initWithEffect:(UIVisualEffect *)effect {
|
||||
self = [super initWithEffect:effect];
|
||||
if (self) {
|
||||
self.scale = 1;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - public method
|
||||
|
||||
- (void)updateBlurEffect:(UIVisualEffect *)effect {
|
||||
self.blurEffect = effect;
|
||||
self.effect = self.blurEffect;
|
||||
}
|
||||
|
||||
#pragma mark - private method
|
||||
|
||||
- (nullable id)__valueForKey:(NSString *)key {
|
||||
if (![NSStringFromClass(self.blurEffect.class) isEqualToString:kInternalCustomBlurEffect]) {
|
||||
return @(0);
|
||||
}
|
||||
return [self.blurEffect valueForKey:key];
|
||||
}
|
||||
|
||||
- (void)__setValue:(id)value forKey:(NSString *)key {
|
||||
if (![NSStringFromClass(self.blurEffect.class) isEqualToString:kInternalCustomBlurEffect]) {
|
||||
self.effect = self.blurEffect;
|
||||
return;
|
||||
}
|
||||
[self.blurEffect setValue:value forKey:key];
|
||||
self.effect = self.blurEffect;
|
||||
}
|
||||
|
||||
#pragma mark - Getter & Setter
|
||||
|
||||
- (UIVisualEffect *)blurEffect {
|
||||
if (!_blurEffect) {
|
||||
if (NSClassFromString(kInternalCustomBlurEffect)) {
|
||||
_blurEffect = (UIBlurEffect *)[NSClassFromString(@"_UICustomBlurEffect") new];
|
||||
} else {
|
||||
_blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
|
||||
}
|
||||
}
|
||||
|
||||
return _blurEffect;
|
||||
}
|
||||
|
||||
- (UIColor *)colorTint {
|
||||
return [self __valueForKey:kHWBlurEffectColorTintKey];
|
||||
}
|
||||
|
||||
- (void)setColorTint:(UIColor *)colorTint {
|
||||
[self __setValue:colorTint forKey:kHWBlurEffectColorTintKey];
|
||||
}
|
||||
|
||||
- (CGFloat)colorTintAlpha {
|
||||
return ((NSNumber *)[self __valueForKey:kHWBlurEffectColorTintAlphaKey]).floatValue;
|
||||
}
|
||||
|
||||
- (void)setColorTintAlpha:(CGFloat)colorTintAlpha {
|
||||
[self __setValue:@(colorTintAlpha) forKey:kHWBlurEffectColorTintAlphaKey];
|
||||
}
|
||||
|
||||
- (CGFloat)blurRadius {
|
||||
return ((NSNumber *)[self __valueForKey:kHWBlurEffectBlurRadiusKey]).floatValue;
|
||||
}
|
||||
|
||||
- (void)setBlurRadius:(CGFloat)blurRadius {
|
||||
[self __setValue:@(blurRadius) forKey:kHWBlurEffectBlurRadiusKey];
|
||||
}
|
||||
|
||||
- (CGFloat)scale {
|
||||
return ((NSNumber *)[self __valueForKey:kHWBlurEffectScaleKey]).floatValue;
|
||||
}
|
||||
|
||||
- (void)setScale:(CGFloat)scale {
|
||||
[self __setValue:@(scale) forKey:kHWBlurEffectScaleKey];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
39
Pods/HWPanModal/Sources/View/PanModal/HWPanModalContainerView.h
generated
Normal file
39
Pods/HWPanModal/Sources/View/PanModal/HWPanModalContainerView.h
generated
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// HWPanModalContainerView.h
|
||||
// Pods
|
||||
//
|
||||
// Created by heath wang on 2019/10/17.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <HWPanModal/HWPanModalPresentable.h>
|
||||
|
||||
@class HWPanModalContentView;
|
||||
@class HWDimmedView;
|
||||
@class HWPanContainerView;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface HWPanModalContainerView : UIView
|
||||
|
||||
@property (nonatomic, readonly) HWDimmedView *backgroundView;
|
||||
@property (readonly) HWPanContainerView *panContainerView;
|
||||
@property (nonatomic, readonly) PresentationState currentPresentationState;
|
||||
|
||||
- (instancetype)initWithPresentingView:(UIView *)presentingView contentView:(HWPanModalContentView<HWPanModalPresentable> *)contentView;
|
||||
|
||||
- (void)show;
|
||||
|
||||
- (void)dismissAnimated:(BOOL)flag completion:(void (^)(void))completion;
|
||||
|
||||
- (void)setNeedsLayoutUpdate;
|
||||
|
||||
- (void)updateUserHitBehavior;
|
||||
|
||||
- (void)transitionToState:(PresentationState)state animated:(BOOL)animated;
|
||||
|
||||
- (void)setScrollableContentOffset:(CGPoint)offset animated:(BOOL)animated;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
546
Pods/HWPanModal/Sources/View/PanModal/HWPanModalContainerView.m
generated
Normal file
546
Pods/HWPanModal/Sources/View/PanModal/HWPanModalContainerView.m
generated
Normal file
@@ -0,0 +1,546 @@
|
||||
//
|
||||
// HWPanModalContainerView.m
|
||||
// Pods
|
||||
//
|
||||
// Created by heath wang on 2019/10/17.
|
||||
//
|
||||
|
||||
#import "HWPanModalContainerView.h"
|
||||
#import "HWPanModalContentView.h"
|
||||
#import "HWPanModalPresentableHandler.h"
|
||||
#import "HWDimmedView.h"
|
||||
#import "HWPanContainerView.h"
|
||||
#import "UIView+HW_Frame.h"
|
||||
#import "HWPanIndicatorView.h"
|
||||
#import "HWPanModalAnimator.h"
|
||||
|
||||
@interface HWPanModalContainerView () <HWPanModalPresentableHandlerDelegate, HWPanModalPresentableHandlerDataSource>
|
||||
|
||||
@property (nonatomic, strong) HWPanModalContentView<HWPanModalPresentable> *contentView;
|
||||
@property (nonatomic, weak) UIView *presentingView;
|
||||
|
||||
@property (nonatomic, strong) HWPanModalPresentableHandler *handler;
|
||||
|
||||
// 判断弹出的view是否在做动画
|
||||
@property (nonatomic, assign) BOOL isPresentedViewAnimating;
|
||||
@property (nonatomic, assign) PresentationState currentPresentationState;
|
||||
@property (nonatomic, assign) BOOL isPresenting;
|
||||
@property (nonatomic, assign) BOOL isDismissing;
|
||||
|
||||
// view
|
||||
@property (nonatomic, strong) HWDimmedView *backgroundView;
|
||||
@property (nonatomic, strong) HWPanContainerView *panContainerView;
|
||||
@property (nonatomic, strong) UIView<HWPanModalIndicatorProtocol> *dragIndicatorView;
|
||||
|
||||
@property (nonatomic, copy) void(^animationBlock)(void);
|
||||
|
||||
@property (nullable, nonatomic, strong) UISelectionFeedbackGenerator *feedbackGenerator API_AVAILABLE(ios(10.0));
|
||||
|
||||
@end
|
||||
|
||||
@implementation HWPanModalContainerView
|
||||
|
||||
- (instancetype)initWithPresentingView:(UIView *)presentingView contentView:(HWPanModalContentView<HWPanModalPresentable> *)contentView {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_presentingView = presentingView;
|
||||
_contentView = contentView;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)show {
|
||||
[self prepare];
|
||||
[self presentAnimationWillBegin];
|
||||
[self beginPresentAnimation];
|
||||
}
|
||||
|
||||
- (void)dismissAnimated:(BOOL)flag completion:(void (^)(void))completion {
|
||||
if (flag) {
|
||||
self.animationBlock = completion;
|
||||
[self dismiss:NO mode:PanModalInteractiveModeNone];
|
||||
} else {
|
||||
self.isDismissing = YES;
|
||||
[[self presentable] panModalWillDismiss];
|
||||
[self removeFromSuperview];
|
||||
[[self presentable] panModalDidDismissed];
|
||||
|
||||
completion ? completion() : nil;
|
||||
self.isDismissing = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)prepare {
|
||||
[self.presentingView addSubview:self];
|
||||
self.frame = self.presentingView.bounds;
|
||||
|
||||
_handler = [[HWPanModalPresentableHandler alloc] initWithPresentable:self.contentView];
|
||||
_handler.delegate = self;
|
||||
_handler.dataSource = self;
|
||||
|
||||
if (@available(iOS 10.0, *)) {
|
||||
_feedbackGenerator = [UISelectionFeedbackGenerator new];
|
||||
[_feedbackGenerator prepare];
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
}
|
||||
|
||||
- (void)willMoveToSuperview:(UIView *)newSuperview {
|
||||
[super willMoveToSuperview:newSuperview];
|
||||
if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
|
||||
[self.superview removeObserver:self forKeyPath:@"frame"];
|
||||
[newSuperview addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
|
||||
if (object == self.presentingView && [keyPath isEqualToString:@"frame"]) {
|
||||
self.frame = self.presentingView.bounds;
|
||||
[self setNeedsLayoutUpdate];
|
||||
[self updateDragIndicatorViewFrame];
|
||||
[self.contentView hw_panModalTransitionTo:self.contentView.hw_presentationState animated:NO];
|
||||
} else {
|
||||
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)presentAnimationWillBegin {
|
||||
[[self presentable] panModalTransitionWillBegin];
|
||||
[self layoutBackgroundView];
|
||||
|
||||
if ([[self presentable] originPresentationState] == PresentationStateLong) {
|
||||
self.currentPresentationState = PresentationStateLong;
|
||||
} else if ([[self presentable] originPresentationState] == PresentationStateMedium) {
|
||||
self.currentPresentationState = PresentationStateMedium;
|
||||
}
|
||||
|
||||
[self addSubview:self.panContainerView];
|
||||
[self layoutPresentedView];
|
||||
|
||||
[self.handler configureScrollViewInsets];
|
||||
[[self presentable] presentedViewDidMoveToSuperView];
|
||||
}
|
||||
|
||||
- (void)beginPresentAnimation {
|
||||
self.isPresenting = YES;
|
||||
CGFloat yPos = self.contentView.shortFormYPos;
|
||||
if ([[self presentable] originPresentationState] == PresentationStateLong) {
|
||||
yPos = self.contentView.longFormYPos;
|
||||
} else if ([[self presentable] originPresentationState] == PresentationStateMedium) {
|
||||
yPos = self.contentView.mediumFormYPos;
|
||||
}
|
||||
|
||||
// refresh layout
|
||||
[self configureViewLayout];
|
||||
[self adjustPresentedViewFrame];
|
||||
|
||||
self.panContainerView.hw_top = self.hw_height;
|
||||
|
||||
if ([[self presentable] isHapticFeedbackEnabled]) {
|
||||
if (@available(iOS 10.0, *)) {
|
||||
[self.feedbackGenerator selectionChanged];
|
||||
}
|
||||
}
|
||||
|
||||
[HWPanModalAnimator animate:^{
|
||||
self.panContainerView.hw_top = yPos;
|
||||
self.backgroundView.dimState = DimStateMax;
|
||||
} config:[self presentable] completion:^(BOOL completion) {
|
||||
self.isPresenting = NO;
|
||||
[[self presentable] panModalTransitionDidFinish];
|
||||
|
||||
if (@available(iOS 10.0, *)) {
|
||||
self.feedbackGenerator = nil;
|
||||
}
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
[self configureViewLayout];
|
||||
}
|
||||
|
||||
#pragma mark - public method
|
||||
|
||||
- (void)setNeedsLayoutUpdate {
|
||||
|
||||
[self configureViewLayout];
|
||||
[self updateBackgroundColor];
|
||||
[self.handler observeScrollable];
|
||||
[self adjustPresentedViewFrame];
|
||||
[self.handler configureScrollViewInsets];
|
||||
|
||||
[self updateContainerViewShadow];
|
||||
[self updateDragIndicatorView];
|
||||
[self updateRoundedCorners];
|
||||
}
|
||||
|
||||
- (void)updateUserHitBehavior {
|
||||
[self checkBackgroundViewEventPass];
|
||||
[self checkPanGestureRecognizer];
|
||||
}
|
||||
|
||||
- (void)transitionToState:(PresentationState)state animated:(BOOL)animated {
|
||||
|
||||
if (![self.presentable shouldTransitionToState:state]) return;
|
||||
|
||||
[self.dragIndicatorView didChangeToState:HWIndicatorStateNormal];
|
||||
[self.presentable willTransitionToState:state];
|
||||
|
||||
switch (state) {
|
||||
case PresentationStateLong: {
|
||||
[self snapToYPos:self.handler.longFormYPosition animated:animated];
|
||||
}
|
||||
break;
|
||||
case PresentationStateMedium: {
|
||||
[self snapToYPos:self.handler.mediumFormYPosition animated:animated];
|
||||
}
|
||||
break;
|
||||
case PresentationStateShort: {
|
||||
[self snapToYPos:self.handler.shortFormYPosition animated:animated];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
self.currentPresentationState = state;
|
||||
[[self presentable] didChangeTransitionToState:state];
|
||||
}
|
||||
|
||||
- (void)setScrollableContentOffset:(CGPoint)offset animated:(BOOL)animated {
|
||||
[self.handler setScrollableContentOffset:offset animated:animated];
|
||||
}
|
||||
|
||||
#pragma mark - layout
|
||||
|
||||
- (void)adjustPresentedViewFrame {
|
||||
CGRect frame = self.frame;
|
||||
CGSize size = CGSizeMake(CGRectGetWidth(frame), CGRectGetHeight(frame) - self.handler.anchoredYPosition);
|
||||
|
||||
self.panContainerView.hw_size = frame.size;
|
||||
self.panContainerView.contentView.frame = CGRectMake(0, 0, size.width, size.height);
|
||||
self.contentView.frame = self.panContainerView.contentView.bounds;
|
||||
[self.contentView setNeedsLayout];
|
||||
[self.contentView layoutIfNeeded];
|
||||
}
|
||||
|
||||
- (void)configureViewLayout {
|
||||
|
||||
[self.handler configureViewLayout];
|
||||
self.userInteractionEnabled = [[self presentable] isUserInteractionEnabled];
|
||||
}
|
||||
|
||||
- (void)layoutBackgroundView {
|
||||
[self addSubview:self.backgroundView];
|
||||
[self updateBackgroundColor];
|
||||
self.backgroundView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
NSArray *hCons = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[backgroundView]|" options:0 metrics:nil views:@{@"backgroundView": self.backgroundView}];
|
||||
NSArray *vCons = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[backgroundView]|" options:0 metrics:nil views:@{@"backgroundView": self.backgroundView}];
|
||||
[NSLayoutConstraint activateConstraints:hCons];
|
||||
[NSLayoutConstraint activateConstraints:vCons];
|
||||
}
|
||||
|
||||
- (void)updateBackgroundColor {
|
||||
self.backgroundView.blurTintColor = [self.presentable backgroundConfig].blurTintColor;
|
||||
}
|
||||
|
||||
- (void)layoutPresentedView {
|
||||
if (!self.presentable)
|
||||
return;
|
||||
|
||||
self.handler.presentedView = self.panContainerView;
|
||||
|
||||
if ([[self presentable] allowsTouchEventsPassingThroughTransitionView]) {
|
||||
[self.panContainerView addGestureRecognizer:self.handler.panGestureRecognizer];
|
||||
} else {
|
||||
[self addGestureRecognizer:self.handler.panGestureRecognizer];
|
||||
}
|
||||
|
||||
[self setNeedsLayoutUpdate];
|
||||
[self adjustPanContainerBackgroundColor];
|
||||
}
|
||||
|
||||
- (void)adjustPanContainerBackgroundColor {
|
||||
self.panContainerView.contentView.backgroundColor = self.contentView.backgroundColor ? : [self.presentable panScrollable].backgroundColor;
|
||||
}
|
||||
|
||||
- (void)updateDragIndicatorView {
|
||||
if ([self.presentable showDragIndicator]) {
|
||||
[self addDragIndicatorView];
|
||||
} else {
|
||||
self.dragIndicatorView.hidden = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addDragIndicatorView {
|
||||
// if has been add, won't update it.
|
||||
self.dragIndicatorView.hidden = NO;
|
||||
|
||||
if (self.dragIndicatorView.superview == self.panContainerView) {
|
||||
[self updateDragIndicatorViewFrame];
|
||||
[self.dragIndicatorView didChangeToState:HWIndicatorStateNormal];
|
||||
return;
|
||||
}
|
||||
|
||||
self.handler.dragIndicatorView = self.dragIndicatorView;
|
||||
[self.panContainerView addSubview:self.dragIndicatorView];
|
||||
[self updateDragIndicatorViewFrame];
|
||||
|
||||
[self.dragIndicatorView setupSubviews];
|
||||
[self.dragIndicatorView didChangeToState:HWIndicatorStateNormal];
|
||||
}
|
||||
|
||||
- (void)updateDragIndicatorViewFrame {
|
||||
CGSize indicatorSize = [self.dragIndicatorView indicatorSize];
|
||||
self.dragIndicatorView.frame = CGRectMake((self.panContainerView.hw_width - indicatorSize.width) / 2, -kIndicatorYOffset - indicatorSize.height, indicatorSize.width, indicatorSize.height);
|
||||
}
|
||||
|
||||
- (void)updateContainerViewShadow {
|
||||
HWPanModalShadow *shadow = [[self presentable] contentShadow];
|
||||
if (shadow.shadowColor) {
|
||||
[self.panContainerView updateShadow:shadow.shadowColor shadowRadius:shadow.shadowRadius shadowOffset:shadow.shadowOffset shadowOpacity:shadow.shadowOpacity];
|
||||
} else {
|
||||
[self.panContainerView clearShadow];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateRoundedCorners {
|
||||
if ([self.presentable shouldRoundTopCorners]) {
|
||||
[self addRoundedCornersToView:self.panContainerView.contentView];
|
||||
} else {
|
||||
[self resetRoundedCornersToView:self.panContainerView.contentView];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addRoundedCornersToView:(UIView *)view {
|
||||
CGFloat radius = [self.presentable cornerRadius];
|
||||
|
||||
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:UIRectCornerTopRight | UIRectCornerTopLeft cornerRadii:CGSizeMake(radius, radius)];
|
||||
|
||||
CAShapeLayer *mask = [CAShapeLayer new];
|
||||
mask.path = bezierPath.CGPath;
|
||||
view.layer.mask = mask;
|
||||
|
||||
// 提高性能
|
||||
view.layer.shouldRasterize = YES;
|
||||
view.layer.rasterizationScale = [UIScreen mainScreen].scale;
|
||||
}
|
||||
|
||||
- (void)resetRoundedCornersToView:(UIView *)view {
|
||||
view.layer.mask = nil;
|
||||
view.layer.shouldRasterize = NO;
|
||||
}
|
||||
|
||||
- (void)snapToYPos:(CGFloat)yPos animated:(BOOL)animated {
|
||||
|
||||
if (animated) {
|
||||
[HWPanModalAnimator animate:^{
|
||||
self.isPresentedViewAnimating = YES;
|
||||
[self adjustToYPos:yPos];
|
||||
} config:self.presentable completion:^(BOOL completion) {
|
||||
self.isPresentedViewAnimating = NO;
|
||||
}];
|
||||
} else {
|
||||
[self adjustToYPos:yPos];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (void)adjustToYPos:(CGFloat)yPos {
|
||||
self.panContainerView.hw_top = MAX(yPos, self.handler.anchoredYPosition);
|
||||
|
||||
// change dim background starting from shortFormYPosition.
|
||||
if (self.panContainerView.frame.origin.y >= self.handler.shortFormYPosition) {
|
||||
|
||||
CGFloat yDistanceFromShortForm = self.panContainerView.frame.origin.y - self.handler.shortFormYPosition;
|
||||
CGFloat bottomHeight = self.hw_height - self.handler.shortFormYPosition;
|
||||
CGFloat percent = yDistanceFromShortForm / bottomHeight;
|
||||
self.backgroundView.dimState = DimStatePercent;
|
||||
self.backgroundView.percent = 1 - percent;
|
||||
|
||||
[self.presentable panModalGestureRecognizer:self.handler.panGestureRecognizer dismissPercent:MIN(percent, 1)];
|
||||
|
||||
} else {
|
||||
self.backgroundView.dimState = DimStateMax;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - HWPanModalPresentableHandlerDelegate
|
||||
|
||||
- (void)adjustPresentableYPos:(CGFloat)yPos {
|
||||
[self adjustToYPos:yPos];
|
||||
}
|
||||
|
||||
- (void)presentableTransitionToState:(PresentationState)state {
|
||||
[self transitionToState:state animated:YES];
|
||||
}
|
||||
|
||||
- (PresentationState)getCurrentPresentationState {
|
||||
return self.currentPresentationState;
|
||||
}
|
||||
|
||||
- (void)dismiss:(BOOL)isInteractive mode:(PanModalInteractiveMode)mode {
|
||||
self.handler.panGestureRecognizer.enabled = NO;
|
||||
self.isDismissing = YES;
|
||||
|
||||
[[self presentable] panModalWillDismiss];
|
||||
|
||||
[HWPanModalAnimator animate:^{
|
||||
self.panContainerView.hw_top = CGRectGetHeight(self.bounds);
|
||||
self.backgroundView.dimState = DimStateOff;
|
||||
self.dragIndicatorView.alpha = 0;
|
||||
} config:[self presentable] completion:^(BOOL completion) {
|
||||
[self removeFromSuperview];
|
||||
[[self presentable] panModalDidDismissed];
|
||||
self.animationBlock ? self.animationBlock() : nil;
|
||||
self.isDismissing = NO;
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - HWPanModalPresentableHandlerDataSource
|
||||
|
||||
- (CGSize)containerSize {
|
||||
return self.presentingView.bounds.size;
|
||||
}
|
||||
|
||||
- (BOOL)isBeingDismissed {
|
||||
return self.isDismissing;
|
||||
}
|
||||
|
||||
- (BOOL)isBeingPresented {
|
||||
return self.isPresenting;
|
||||
}
|
||||
|
||||
- (BOOL)isFormPositionAnimating {
|
||||
return self.isPresentedViewAnimating;
|
||||
}
|
||||
|
||||
- (BOOL)isPresentedViewAnchored {
|
||||
|
||||
if (![[self presentable] shouldRespondToPanModalGestureRecognizer:self.handler.panGestureRecognizer]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (!self.isPresentedViewAnimating && self.handler.extendsPanScrolling && (CGRectGetMinY(self.panContainerView.frame) <= self.handler.anchoredYPosition || HW_TWO_FLOAT_IS_EQUAL(CGRectGetMinY(self.panContainerView.frame), self.handler.anchoredYPosition))) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - event handle
|
||||
|
||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
|
||||
|
||||
if (!self.userInteractionEnabled || self.hidden || self.alpha < 0.01) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (![self pointInside:point withEvent:event]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
BOOL eventThrough = [[self presentable] allowsTouchEventsPassingThroughTransitionView];
|
||||
if (eventThrough) {
|
||||
CGPoint convertedPoint = [self.panContainerView convertPoint:point fromView:self];
|
||||
if (CGRectGetWidth(self.panContainerView.frame) >= convertedPoint.x &&
|
||||
convertedPoint.x > 0 &&
|
||||
CGRectGetHeight(self.panContainerView.frame) >= convertedPoint.y &&
|
||||
convertedPoint.y > 0) {
|
||||
return [super hitTest:point withEvent:event];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
} else {
|
||||
return [super hitTest:point withEvent:event];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)checkBackgroundViewEventPass {
|
||||
if ([[self presentable] allowsTouchEventsPassingThroughTransitionView]) {
|
||||
self.backgroundView.userInteractionEnabled = NO;
|
||||
self.backgroundView.tapBlock = nil;
|
||||
} else {
|
||||
self.backgroundView.userInteractionEnabled = YES;
|
||||
__weak typeof(self) wkSelf = self;
|
||||
self.backgroundView.tapBlock = ^(UITapGestureRecognizer *recognizer) {
|
||||
if ([[wkSelf presentable] allowsTapBackgroundToDismiss]) {
|
||||
[wkSelf dismiss:NO mode:PanModalInteractiveModeNone];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
- (void)checkPanGestureRecognizer {
|
||||
if ([[self presentable] allowsTouchEventsPassingThroughTransitionView]) {
|
||||
[self removeGestureRecognizer:self.handler.panGestureRecognizer];
|
||||
[self.panContainerView addGestureRecognizer:self.handler.panGestureRecognizer];
|
||||
} else {
|
||||
[self.panContainerView removeGestureRecognizer:self.handler.panGestureRecognizer];
|
||||
[self addGestureRecognizer:self.handler.panGestureRecognizer];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - getter
|
||||
|
||||
- (id<HWPanModalPresentable>)presentable {
|
||||
if ([self.contentView conformsToProtocol:@protocol(HWPanModalPresentable)]) {
|
||||
return self.contentView;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (HWDimmedView *)backgroundView {
|
||||
if (!_backgroundView) {
|
||||
if (self.presentable) {
|
||||
_backgroundView = [[HWDimmedView alloc] initWithBackgroundConfig:[self.presentable backgroundConfig]];
|
||||
} else {
|
||||
_backgroundView = [[HWDimmedView alloc] init];
|
||||
}
|
||||
|
||||
if ([[self presentable] allowsTouchEventsPassingThroughTransitionView]) {
|
||||
_backgroundView.userInteractionEnabled = NO;
|
||||
} else {
|
||||
__weak typeof(self) wkSelf = self;
|
||||
_backgroundView.tapBlock = ^(UITapGestureRecognizer *recognizer) {
|
||||
if ([[wkSelf presentable] allowsTapBackgroundToDismiss]) {
|
||||
[wkSelf dismiss:NO mode:PanModalInteractiveModeNone];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return _backgroundView;
|
||||
}
|
||||
|
||||
- (HWPanContainerView *)panContainerView {
|
||||
if (!_panContainerView) {
|
||||
_panContainerView = [[HWPanContainerView alloc] initWithPresentedView:self.contentView frame:self.bounds];
|
||||
}
|
||||
|
||||
return _panContainerView;
|
||||
}
|
||||
|
||||
- (UIView<HWPanModalIndicatorProtocol> *)dragIndicatorView {
|
||||
|
||||
if (!_dragIndicatorView) {
|
||||
if ([self presentable] &&
|
||||
[[self presentable] respondsToSelector:@selector(customIndicatorView)] &&
|
||||
[[self presentable] customIndicatorView] != nil) {
|
||||
_dragIndicatorView = [[self presentable] customIndicatorView];
|
||||
// set the indicator size first in case `setupSubviews` can Not get the right size.
|
||||
_dragIndicatorView.hw_size = [[[self presentable] customIndicatorView] indicatorSize];
|
||||
} else {
|
||||
_dragIndicatorView = [HWPanIndicatorView new];
|
||||
}
|
||||
}
|
||||
|
||||
return _dragIndicatorView;
|
||||
}
|
||||
|
||||
@end
|
||||
36
Pods/HWPanModal/Sources/View/PanModal/HWPanModalContentView.h
generated
Normal file
36
Pods/HWPanModal/Sources/View/PanModal/HWPanModalContentView.h
generated
Normal file
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// HWPanModalContentView.h
|
||||
// Pods
|
||||
//
|
||||
// Created by heath wang on 2019/10/17.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <HWPanModal/HWPanModalPresentable.h>
|
||||
#import <HWPanModal/HWPanModalPresentationUpdateProtocol.h>
|
||||
#import <HWPanModal/UIViewController+LayoutHelper.h>
|
||||
#import <HWPanModal/HWPanModalPanGestureDelegate.h>
|
||||
|
||||
@class HWPanModalContainerView;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// when use `HWPanModalContentView`, you should take care of the safe area by yourself.
|
||||
@interface HWPanModalContentView : UIView <HWPanModalPresentable, HWPanModalPanGestureDelegate, HWPanModalPresentationUpdateProtocol, HWPanModalPresentableLayoutProtocol>
|
||||
|
||||
/**
|
||||
* present in the target view
|
||||
* @param view The view which present to. If the view is nil, will use UIWindow's keyWindow.
|
||||
*/
|
||||
- (void)presentInView:(nullable UIView *)view;
|
||||
|
||||
/**
|
||||
* call this method to dismiss contentView directly.
|
||||
* @param flag should animate flag
|
||||
* @param completion dismiss completion block
|
||||
*/
|
||||
- (void)dismissAnimated:(BOOL)flag completion:(void (^)(void))completion;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
414
Pods/HWPanModal/Sources/View/PanModal/HWPanModalContentView.m
generated
Normal file
414
Pods/HWPanModal/Sources/View/PanModal/HWPanModalContentView.m
generated
Normal file
@@ -0,0 +1,414 @@
|
||||
//
|
||||
// HWPanModalContentView.m
|
||||
// Pods
|
||||
//
|
||||
// Created by heath wang on 2019/10/17.
|
||||
//
|
||||
|
||||
#import "HWPanModalContentView.h"
|
||||
#import "HWPanModalContainerView.h"
|
||||
|
||||
@interface HWPanModalContentView ()
|
||||
|
||||
@property (nonatomic, weak) HWPanModalContainerView *containerView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation HWPanModalContentView
|
||||
|
||||
#pragma mark - public method
|
||||
|
||||
- (void)presentInView:(UIView *)view {
|
||||
if (!view) {
|
||||
view = [self findKeyWindow];
|
||||
}
|
||||
HWPanModalContainerView *containerView = [[HWPanModalContainerView alloc] initWithPresentingView:view contentView:self];
|
||||
[containerView show];
|
||||
}
|
||||
|
||||
- (void)dismissAnimated:(BOOL)flag completion:(void (^)(void))completion {
|
||||
[self.containerView dismissAnimated:flag completion:completion];
|
||||
}
|
||||
|
||||
#pragma mark - HWPanModalPresentationUpdateProtocol
|
||||
|
||||
- (void)hw_panModalTransitionTo:(PresentationState)state {
|
||||
[self.containerView transitionToState:state animated:YES];
|
||||
}
|
||||
|
||||
- (void)hw_panModalSetContentOffset:(CGPoint)offset {
|
||||
[self.containerView setScrollableContentOffset:offset animated:YES];
|
||||
}
|
||||
|
||||
- (void)hw_panModalSetNeedsLayoutUpdate {
|
||||
[self.containerView setNeedsLayoutUpdate];
|
||||
}
|
||||
|
||||
- (void)hw_panModalUpdateUserHitBehavior {
|
||||
[self.containerView updateUserHitBehavior];
|
||||
}
|
||||
|
||||
- (void)hw_panModalTransitionTo:(PresentationState)state animated:(BOOL)animated {
|
||||
[self.containerView transitionToState:state animated:animated];
|
||||
}
|
||||
|
||||
- (void)hw_panModalSetContentOffset:(CGPoint)offset animated:(BOOL)animated {
|
||||
[self.containerView setScrollableContentOffset:offset animated:animated];
|
||||
}
|
||||
|
||||
- (void)hw_dismissAnimated:(BOOL)animated completion:(void (^)(void))completion {
|
||||
[self dismissAnimated:animated completion:completion];
|
||||
}
|
||||
|
||||
- (HWDimmedView *)hw_dimmedView {
|
||||
return self.containerView.backgroundView;
|
||||
}
|
||||
|
||||
- (UIView *)hw_rootContainerView {
|
||||
return self.containerView;
|
||||
}
|
||||
|
||||
- (UIView *)hw_contentView {
|
||||
return (UIView *)self.containerView.panContainerView;
|
||||
}
|
||||
|
||||
- (PresentationState)hw_presentationState {
|
||||
return self.containerView.currentPresentationState;
|
||||
}
|
||||
|
||||
#pragma mark - HWPanModalPresentable
|
||||
|
||||
- (UIScrollView *)panScrollable {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CGFloat)topOffset {
|
||||
return self.topLayoutOffset + 21.f;
|
||||
}
|
||||
|
||||
- (PanModalHeight)shortFormHeight {
|
||||
return [self longFormHeight];
|
||||
}
|
||||
|
||||
- (PanModalHeight)mediumFormHeight {
|
||||
return [self longFormHeight];
|
||||
}
|
||||
|
||||
- (PanModalHeight)longFormHeight {
|
||||
if ([self panScrollable]) {
|
||||
[[self panScrollable] layoutIfNeeded];
|
||||
return PanModalHeightMake(PanModalHeightTypeContent, MAX([self panScrollable].contentSize.height, [self panScrollable].bounds.size.height));
|
||||
} else {
|
||||
return PanModalHeightMake(PanModalHeightTypeMax, 0);
|
||||
}
|
||||
}
|
||||
|
||||
- (PresentationState)originPresentationState {
|
||||
return PresentationStateShort;
|
||||
}
|
||||
|
||||
- (CGFloat)springDamping {
|
||||
return 0.8;
|
||||
}
|
||||
|
||||
- (NSTimeInterval)transitionDuration {
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
- (NSTimeInterval)dismissalDuration {
|
||||
return [self transitionDuration];
|
||||
}
|
||||
|
||||
- (UIViewAnimationOptions)transitionAnimationOptions {
|
||||
return UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState;
|
||||
}
|
||||
|
||||
- (CGFloat)backgroundAlpha {
|
||||
return 0.7;
|
||||
}
|
||||
|
||||
- (CGFloat)backgroundBlurRadius {
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (nonnull UIColor *)backgroundBlurColor {
|
||||
return [UIColor whiteColor];
|
||||
}
|
||||
|
||||
- (HWBackgroundConfig *)backgroundConfig {
|
||||
return [HWBackgroundConfig configWithBehavior:HWBackgroundBehaviorDefault];
|
||||
}
|
||||
|
||||
- (UIEdgeInsets)scrollIndicatorInsets {
|
||||
CGFloat top = [self shouldRoundTopCorners] ? [self cornerRadius] : 0;
|
||||
return UIEdgeInsetsMake(top, 0, self.bottomLayoutOffset, 0);
|
||||
}
|
||||
|
||||
- (BOOL)showsScrollableVerticalScrollIndicator {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)shouldAutoSetPanScrollContentInset {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)anchorModalToLongForm {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)allowsExtendedPanScrolling {
|
||||
if ([self panScrollable]) {
|
||||
UIScrollView *scrollable = [self panScrollable];
|
||||
[scrollable layoutIfNeeded];
|
||||
return scrollable.contentSize.height > (scrollable.frame.size.height - self.bottomLayoutOffset);
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)allowsDragToDismiss {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (CGFloat)minVerticalVelocityToTriggerDismiss {
|
||||
return 300;
|
||||
}
|
||||
|
||||
- (BOOL)allowsTapBackgroundToDismiss {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)allowsPullDownWhenShortState {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)allowScreenEdgeInteractive {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (CGFloat)maxAllowedDistanceToLeftScreenEdgeForPanInteraction {
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (CGFloat)minHorizontalVelocityToTriggerScreenEdgeDismiss {
|
||||
return 500;
|
||||
}
|
||||
|
||||
- (PresentingViewControllerAnimationStyle)presentingVCAnimationStyle {
|
||||
return PresentingViewControllerAnimationStyleNone;
|
||||
}
|
||||
|
||||
- (BOOL)shouldAnimatePresentingVC {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (id <HWPresentingViewControllerAnimatedTransitioning>)customPresentingVCAnimation {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)isPanScrollEnabled {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)isUserInteractionEnabled {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)isHapticFeedbackEnabled {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)allowsTouchEventsPassingThroughTransitionView {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)shouldRoundTopCorners {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (CGFloat)cornerRadius {
|
||||
return 8;
|
||||
}
|
||||
|
||||
- (HWPanModalShadow *)contentShadow {
|
||||
return [HWPanModalShadow panModalShadowNil];
|
||||
}
|
||||
|
||||
- (BOOL)showDragIndicator {
|
||||
if ([self allowsTouchEventsPassingThroughTransitionView]) {
|
||||
return NO;
|
||||
}
|
||||
return [self shouldRoundTopCorners];
|
||||
}
|
||||
|
||||
- (nullable UIView <HWPanModalIndicatorProtocol> *)customIndicatorView {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)isAutoHandleKeyboardEnabled {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (CGFloat)keyboardOffsetFromInputView {
|
||||
return 5;
|
||||
}
|
||||
|
||||
- (BOOL)shouldRespondToPanModalGestureRecognizer:(UIPanGestureRecognizer *)panGestureRecognizer {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)willRespondToPanModalGestureRecognizer:(UIPanGestureRecognizer *)panGestureRecognizer {
|
||||
|
||||
}
|
||||
|
||||
- (void)didRespondToPanModalGestureRecognizer:(UIPanGestureRecognizer *)panGestureRecognizer {
|
||||
|
||||
}
|
||||
|
||||
- (void)didEndRespondToPanModalGestureRecognizer:(nonnull UIPanGestureRecognizer *)panGestureRecognizer {
|
||||
|
||||
}
|
||||
|
||||
- (BOOL)shouldPrioritizePanModalGestureRecognizer:(UIPanGestureRecognizer *)panGestureRecognizer {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)shouldTransitionToState:(PresentationState)state {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)willTransitionToState:(PresentationState)state {
|
||||
|
||||
}
|
||||
|
||||
- (void)didChangeTransitionToState:(PresentationState)state {
|
||||
|
||||
}
|
||||
|
||||
- (void)panModalGestureRecognizer:(UIPanGestureRecognizer *)panGestureRecognizer dismissPercent:(CGFloat)percent {
|
||||
|
||||
}
|
||||
|
||||
- (void)panModalWillDismiss {
|
||||
|
||||
}
|
||||
|
||||
- (void)panModalDidDismissed {
|
||||
|
||||
}
|
||||
|
||||
- (void)panModalTransitionWillBegin {
|
||||
|
||||
}
|
||||
|
||||
- (void)panModalTransitionDidFinish {
|
||||
|
||||
}
|
||||
|
||||
- (void)presentedViewDidMoveToSuperView {
|
||||
|
||||
}
|
||||
|
||||
- (BOOL)shouldEnableAppearanceTransition {
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - HWPanModalPresentableLayoutProtocol
|
||||
|
||||
- (CGFloat)topLayoutOffset {
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (CGFloat)bottomLayoutOffset {
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (CGFloat)shortFormYPos {
|
||||
CGFloat shortFormYPos = [self topMarginFromPanModalHeight:[self shortFormHeight]] + [self topOffset];
|
||||
return MAX(shortFormYPos, self.longFormYPos);
|
||||
}
|
||||
|
||||
- (CGFloat)mediumFormYPos {
|
||||
CGFloat mediumFormYPos = [self topMarginFromPanModalHeight:[self mediumFormHeight]] + [self topOffset];
|
||||
return MAX(mediumFormYPos, self.longFormYPos);
|
||||
}
|
||||
|
||||
- (CGFloat)longFormYPos {
|
||||
CGFloat longFrom = MAX([self topMarginFromPanModalHeight:[self longFormHeight]], [self topMarginFromPanModalHeight:PanModalHeightMake(PanModalHeightTypeMax, 0)]) + [self topOffset];
|
||||
return longFrom;
|
||||
}
|
||||
|
||||
- (CGFloat)bottomYPos {
|
||||
if (self.containerView) {
|
||||
return self.containerView.bounds.size.height - [self topOffset];
|
||||
}
|
||||
return self.bounds.size.height;
|
||||
}
|
||||
|
||||
- (CGFloat)topMarginFromPanModalHeight:(PanModalHeight)panModalHeight {
|
||||
switch (panModalHeight.heightType) {
|
||||
case PanModalHeightTypeMax:
|
||||
return 0.0f;
|
||||
case PanModalHeightTypeMaxTopInset:
|
||||
return panModalHeight.height;
|
||||
case PanModalHeightTypeContent:
|
||||
return self.bottomYPos - (panModalHeight.height + self.bottomLayoutOffset);
|
||||
case PanModalHeightTypeContentIgnoringSafeArea:
|
||||
return self.bottomYPos - panModalHeight.height;
|
||||
case PanModalHeightTypeIntrinsic: {
|
||||
[self layoutIfNeeded];
|
||||
|
||||
CGSize targetSize = CGSizeMake(self.containerView ? self.containerView.bounds.size.width : [UIScreen mainScreen].bounds.size.width, UILayoutFittingCompressedSize.height);
|
||||
CGFloat intrinsicHeight = [self systemLayoutSizeFittingSize:targetSize].height;
|
||||
return self.bottomYPos - (intrinsicHeight + self.bottomLayoutOffset);
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Getter
|
||||
|
||||
- (HWPanModalContainerView *)containerView {
|
||||
// we assume the container view will not change after we got it.
|
||||
if (!_containerView) {
|
||||
UIView *fatherView = self.superview;
|
||||
while (fatherView) {
|
||||
if ([fatherView isKindOfClass:HWPanModalContainerView.class]) {
|
||||
_containerView = (HWPanModalContainerView *) fatherView;
|
||||
break;
|
||||
}
|
||||
fatherView = fatherView.superview;
|
||||
}
|
||||
}
|
||||
|
||||
return _containerView;
|
||||
}
|
||||
|
||||
- (UIView *)findKeyWindow {
|
||||
|
||||
if (@available(iOS 13.0, *)) {
|
||||
NSSet<UIScene *> *connectedScenes = [UIApplication sharedApplication].connectedScenes;
|
||||
for (UIScene *scene in connectedScenes) {
|
||||
if ([scene isKindOfClass:UIWindowScene.class]) {
|
||||
UIWindowScene *windowScene = (UIWindowScene *)scene;
|
||||
for (UIWindow *tmpWindow in windowScene.windows) {
|
||||
if ([tmpWindow isKeyWindow]) {
|
||||
return tmpWindow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
NSArray *windows = [UIApplication sharedApplication].windows;
|
||||
for (UIWindow *window in windows) {
|
||||
if ([window isKeyWindow]) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user