Commit 31ea2f290e79e4663123e321285d629d93ff618a

Authored by 梁星国
1 parent 7b9f1a72

提交预览库放模块中

Showing 75 changed files with 6323 additions and 8 deletions
CNLiveOrganizationValidationModule.podspec
@@ -77,8 +77,8 @@ Pod::Spec.new do |s| @@ -77,8 +77,8 @@ Pod::Spec.new do |s|
77 ### 所有github三方管理库(非频繁更新) 77 ### 所有github三方管理库(非频繁更新)
78 ######################################## 78 ########################################
79 s.dependency 'CNLiveTripartiteManagement' 79 s.dependency 'CNLiveTripartiteManagement'
80 - #相册展示  
81 - s.dependency 'YBImageBrowser' 80 +# #相册展示
  81 +# s.dependency 'YBImageBrowser'
82 82
83 83
84 84
CNLiveOrganizationValidationModule/Assets/YBImageBrowser.bundle/ybib_loading@2x.png 0 → 100644

2.44 KB

CNLiveOrganizationValidationModule/Assets/YBImageBrowser.bundle/ybib_loading@3x.png 0 → 100644

3.97 KB

CNLiveOrganizationValidationModule/Assets/YBImageBrowser.bundle/ybib_more@2x.png 0 → 100644

844 Bytes

CNLiveOrganizationValidationModule/Assets/YBImageBrowser.bundle/ybib_more@3x.png 0 → 100644

1.13 KB

CNLiveOrganizationValidationModule/Assets/YBImageBrowser.bundle/ybib_save@2x.png 0 → 100644

862 Bytes

CNLiveOrganizationValidationModule/Assets/YBImageBrowser.bundle/ybib_save@3x.png 0 → 100644

960 Bytes

CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/Controller/CNLiveBOraganizationDetailController.m
@@ -20,8 +20,8 @@ @@ -20,8 +20,8 @@
20 #import <QMUIKit/QMUIKit.h> 20 #import <QMUIKit/QMUIKit.h>
21 #import <YYKit/YYKit.h> 21 #import <YYKit/YYKit.h>
22 #import <objc/runtime.h> 22 #import <objc/runtime.h>
23 -#import <YBImageBrowser/YBImageBrowser.h>  
24 -#import <YBImageBrowser/YBIBImageData.h> 23 +#import "YBImageBrowser.h"
  24 +#import "YBIBImageData.h"
25 #import "UIImage+OrganizationValidation.h" 25 #import "UIImage+OrganizationValidation.h"
26 26
27 27
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/AuxiliaryView/YBIBAuxiliaryViewHandler.h 0 → 100644
  1 +//
  2 +// YBIBAuxiliaryViewHandler.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/27.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@protocol YBIBAuxiliaryViewHandler <NSObject>
  14 +
  15 +@required
  16 +
  17 +/// 展示正确情况的提示
  18 +- (void)yb_showCorrectToastWithContainer:(UIView *)container text:(NSString *)text;
  19 +/// 展示错误情况的提示
  20 +- (void)yb_showIncorrectToastWithContainer:(UIView *)container text:(NSString *)text;
  21 +/// 隐藏所有提示
  22 +- (void)yb_hideToastWithContainer:(UIView *)container;
  23 +
  24 +/// 展示加载视图
  25 +- (void)yb_showLoadingWithContainer:(UIView *)container;
  26 +/// 展示带进度的加载视图
  27 +- (void)yb_showLoadingWithContainer:(UIView *)container progress:(CGFloat)progress;
  28 +/// 展示带文字的视图
  29 +- (void)yb_showLoadingWithContainer:(UIView *)container text:(NSString *)text;
  30 +/// 隐藏所有视图
  31 +- (void)yb_hideLoadingWithContainer:(UIView *)container;
  32 +
  33 +@end
  34 +
  35 +@interface YBIBAuxiliaryViewHandler : NSObject <YBIBAuxiliaryViewHandler>
  36 +
  37 +@end
  38 +
  39 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/AuxiliaryView/YBIBAuxiliaryViewHandler.m 0 → 100644
  1 +//
  2 +// YBIBAuxiliaryViewHandler.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/27.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBAuxiliaryViewHandler.h"
  10 +#import "YBIBToastView.h"
  11 +#import "YBIBLoadingView.h"
  12 +
  13 +@implementation YBIBAuxiliaryViewHandler
  14 +
  15 +#pragma mark - <YBIBAuxiliaryViewHandler>
  16 +
  17 +- (void)yb_showCorrectToastWithContainer:(UIView *)container text:(NSString *)text {
  18 + [container ybib_showHookToast:text];
  19 +}
  20 +
  21 +- (void)yb_showIncorrectToastWithContainer:(UIView *)container text:(NSString *)text {
  22 + [container ybib_showForkToast:text];
  23 +}
  24 +
  25 +- (void)yb_hideToastWithContainer:(UIView *)container {
  26 + [container ybib_hideToast];
  27 +}
  28 +
  29 +- (void)yb_showLoadingWithContainer:(UIView *)container {
  30 + [container ybib_showLoading];
  31 +}
  32 +
  33 +- (void)yb_showLoadingWithContainer:(UIView *)container progress:(CGFloat)progress {
  34 + [container ybib_showLoadingWithProgress:progress];
  35 +}
  36 +
  37 +- (void)yb_showLoadingWithContainer:(UIView *)container text:(NSString *)text {
  38 + [container ybib_showLoadingWithText:text click:nil];
  39 +}
  40 +
  41 +- (void)yb_hideLoadingWithContainer:(UIView *)container {
  42 + [container ybib_hideLoading];
  43 +}
  44 +
  45 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/AuxiliaryView/YBIBLoadingView.h 0 → 100644
  1 +//
  2 +// YBIBLoadingView.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2018/9/1.
  6 +// Copyright © 2018年 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@interface UIView (YBIBLoading)
  14 +
  15 +- (void)ybib_showLoading;
  16 +
  17 +- (void)ybib_showLoadingWithProgress:(CGFloat)progress;
  18 +
  19 +- (void)ybib_showLoadingWithText:(NSString *)text click:(nullable void(^)(void))click;
  20 +
  21 +- (void)ybib_hideLoading;
  22 +
  23 +@end
  24 +
  25 +
  26 +@interface YBIBLoadingView : UIView
  27 +
  28 +- (void)show;
  29 +
  30 +- (void)showProgress:(CGFloat)progress;
  31 +
  32 +- (void)showText:(NSString *)text click:(void(^)(void))click;
  33 +
  34 +@end
  35 +
  36 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/AuxiliaryView/YBIBLoadingView.m 0 → 100644
  1 +//
  2 +// YBIBLoadingView.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2018/9/1.
  6 +// Copyright © 2018年 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBLoadingView.h"
  10 +#import "YBIBIconManager.h"
  11 +#import <objc/runtime.h>
  12 +
  13 +@interface UIView ()
  14 +@property (nonatomic, strong, readonly) YBIBLoadingView *ybib_loading;
  15 +@end
  16 +
  17 +@implementation UIView (YBIBLoading)
  18 +
  19 +- (void)ybib_showLoadingWithProgress:(CGFloat)progress {
  20 + [self ybib_addLoadingView];
  21 + [self.ybib_loading showProgress:progress];
  22 +}
  23 +
  24 +- (void)ybib_showLoading {
  25 + [self ybib_addLoadingView];
  26 + [self.ybib_loading show];
  27 +}
  28 +
  29 +- (void)ybib_showLoadingWithText:(NSString *)text click:(void (^)(void))click {
  30 + [self ybib_addLoadingView];
  31 + [self.ybib_loading showText:text click:click];
  32 +}
  33 +
  34 +- (void)ybib_addLoadingView {
  35 + YBIBLoadingView *loading = self.ybib_loading;
  36 + if (!loading.superview) {
  37 + [self addSubview:loading];
  38 + loading.translatesAutoresizingMaskIntoConstraints = NO;
  39 + NSLayoutConstraint *layA = [NSLayoutConstraint constraintWithItem:loading attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1 constant:0];
  40 + NSLayoutConstraint *layB = [NSLayoutConstraint constraintWithItem:loading attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1 constant:0];
  41 + NSLayoutConstraint *layC = [NSLayoutConstraint constraintWithItem:loading attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
  42 + NSLayoutConstraint *layD = [NSLayoutConstraint constraintWithItem:loading attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1 constant:0];
  43 + [self addConstraints:@[layA, layB, layC, layD]];
  44 + }
  45 +}
  46 +
  47 +- (void)ybib_hideLoading {
  48 + YBIBLoadingView *loading = self.ybib_loading;
  49 + if (loading && loading.superview) {
  50 + [loading removeFromSuperview];
  51 + }
  52 +}
  53 +
  54 +static void *YBIBLoadingKey = &YBIBLoadingKey;
  55 +- (void)setYbib_loading:(YBIBLoadingView *)ybib_loading {
  56 + objc_setAssociatedObject(self, YBIBLoadingKey, ybib_loading, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  57 +}
  58 +- (YBIBLoadingView *)ybib_loading {
  59 + YBIBLoadingView *loading = objc_getAssociatedObject(self, YBIBLoadingKey);
  60 + if (!loading) {
  61 + loading = [YBIBLoadingView new];
  62 + self.ybib_loading = loading;
  63 + }
  64 + return loading;
  65 +}
  66 +
  67 +@end
  68 +
  69 +
  70 +@interface YBIBProgressDrawView : UIView
  71 +@property (nonatomic, assign) CGFloat progress;
  72 +@end
  73 +@implementation YBIBProgressDrawView
  74 +- (void)drawRect:(CGRect)rect {
  75 + if (self.isHidden) return;
  76 +
  77 + CGFloat progress = (isnan(_progress) || isinf(_progress) || _progress < 0) ? 0 : _progress;
  78 +
  79 + CGFloat radius = 17;
  80 + CGFloat strokeWidth = 3;
  81 + CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
  82 +
  83 + [[UIColor lightGrayColor] setStroke];
  84 + UIBezierPath *bottomPath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0 endAngle:M_PI * 2 clockwise:YES];
  85 + bottomPath.lineWidth = 4.0;
  86 + bottomPath.lineCapStyle = kCGLineCapRound;
  87 + bottomPath.lineJoinStyle = kCGLineCapRound;
  88 + [bottomPath stroke];
  89 +
  90 + [[UIColor whiteColor] setStroke];
  91 + UIBezierPath *activePath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:-M_PI / 2.0 endAngle:M_PI * 2 * progress - M_PI / 2.0 clockwise:true];
  92 + activePath.lineWidth = strokeWidth;
  93 + activePath.lineCapStyle = kCGLineCapRound;
  94 + activePath.lineJoinStyle = kCGLineCapRound;
  95 + [activePath stroke];
  96 +
  97 + NSShadow *shadow = [NSShadow new];
  98 + shadow.shadowBlurRadius = 4;
  99 + shadow.shadowOffset = CGSizeMake(0, 1);
  100 + shadow.shadowColor = UIColor.darkGrayColor;
  101 + NSString *string = [NSString stringWithFormat:@"%.0lf%@", progress * 100, @"%"];
  102 + NSMutableAttributedString *atts = [[NSMutableAttributedString alloc] initWithString:string attributes:@{NSFontAttributeName:[UIFont boldSystemFontOfSize:10], NSForegroundColorAttributeName:[UIColor whiteColor], NSShadowAttributeName:shadow}];
  103 + CGSize size = atts.size;
  104 + [atts drawAtPoint:CGPointMake(center.x - size.width / 2.0, center.y - size.height / 2.0)];
  105 +}
  106 +@end
  107 +
  108 +
  109 +typedef NS_ENUM(NSUInteger, YBImageBrowserProgressType) {
  110 + YBImageBrowserProgressTypeProgress,
  111 + YBImageBrowserProgressTypeLoad,
  112 + YBImageBrowserProgressTypeText
  113 +};
  114 +
  115 +@interface YBIBLoadingView () {
  116 + YBImageBrowserProgressType _type;
  117 +}
  118 +@property (nonatomic, strong) UILabel *textLabel;
  119 +@property (nonatomic, strong) UIImageView *imageView;
  120 +@property (nonatomic, strong) YBIBProgressDrawView *drawView;
  121 +@property (nonatomic, copy) void(^clickTextLabelBlock)(void);
  122 +@end
  123 +
  124 +@implementation YBIBLoadingView
  125 +
  126 +#pragma mark life cycle
  127 +
  128 +- (instancetype)initWithFrame:(CGRect)frame {
  129 + self = [super initWithFrame:frame];
  130 + if (self) {
  131 + self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0];
  132 + self.userInteractionEnabled = NO;
  133 +
  134 + [self addSubview:self.drawView];
  135 + [self addSubview:self.textLabel];
  136 + [self addSubview:self.imageView];
  137 + }
  138 + return self;
  139 +}
  140 +
  141 +- (void)updateConstraints {
  142 + self.textLabel.translatesAutoresizingMaskIntoConstraints = NO;
  143 + NSLayoutConstraint *layA = [NSLayoutConstraint constraintWithItem:self.textLabel attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1 constant:20];
  144 + NSLayoutConstraint *layB = [NSLayoutConstraint constraintWithItem:self.textLabel attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1 constant:-20];
  145 + NSLayoutConstraint *layC = [NSLayoutConstraint constraintWithItem:self.textLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
  146 +
  147 + self.imageView.translatesAutoresizingMaskIntoConstraints = NO;
  148 + NSLayoutConstraint *layE = [NSLayoutConstraint constraintWithItem:self.imageView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
  149 + NSLayoutConstraint *layF = [NSLayoutConstraint constraintWithItem:self.imageView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
  150 +
  151 + self.drawView.translatesAutoresizingMaskIntoConstraints = NO;
  152 + NSLayoutConstraint *layG = [NSLayoutConstraint constraintWithItem:self.drawView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
  153 + NSLayoutConstraint *layH = [NSLayoutConstraint constraintWithItem:self.drawView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
  154 + NSLayoutConstraint *layI = [NSLayoutConstraint constraintWithItem:self.drawView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:50];
  155 + NSLayoutConstraint *layJ = [NSLayoutConstraint constraintWithItem:self.drawView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:50];
  156 +
  157 + [self addConstraints:@[layA, layB, layC, layE, layF, layG, layH, layI, layJ]];
  158 + [super updateConstraints];
  159 +}
  160 +
  161 +#pragma mark public
  162 +
  163 +- (void)showProgress:(CGFloat)progress {
  164 + self.userInteractionEnabled = NO;
  165 + _type = YBImageBrowserProgressTypeProgress;
  166 + self.drawView.hidden = NO;
  167 + self.textLabel.hidden = YES;
  168 + self.imageView.hidden = YES;
  169 + [self stopImageViewAnimation];
  170 +
  171 + self.drawView.progress = progress;
  172 + [self.drawView setNeedsDisplay];
  173 +}
  174 +
  175 +- (void)show {
  176 + self.userInteractionEnabled = NO;
  177 + _type = YBImageBrowserProgressTypeLoad;
  178 + self.drawView.hidden = YES;
  179 + self.textLabel.hidden = YES;
  180 + self.imageView.hidden = NO;
  181 +
  182 + [self startImageViewAnimation];
  183 + [self.drawView setNeedsDisplay];
  184 +}
  185 +
  186 +- (void)startImageViewAnimation {
  187 + CABasicAnimation *ra = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
  188 + ra.toValue = [NSNumber numberWithFloat:M_PI * 2];
  189 + ra.duration = 1;
  190 + ra.cumulative = YES;
  191 + ra.repeatCount = HUGE_VALF;
  192 + ra.removedOnCompletion = NO;
  193 + ra.fillMode = kCAFillModeForwards;
  194 + [self.imageView.layer addAnimation:ra forKey:@"ra"];
  195 +}
  196 +
  197 +- (void)stopImageViewAnimation {
  198 + [self.imageView.layer removeAllAnimations];
  199 +}
  200 +
  201 +- (void)showText:(NSString *)text click:(void (^)(void))click {
  202 + self.userInteractionEnabled = click ? YES : NO;
  203 + _type = YBImageBrowserProgressTypeText;
  204 + self.drawView.hidden = YES;
  205 + self.textLabel.hidden = NO;
  206 + self.imageView.hidden = YES;
  207 + [self stopImageViewAnimation];
  208 +
  209 + self.textLabel.text = text;
  210 + self.clickTextLabelBlock = click;
  211 + [self.drawView setNeedsDisplay];
  212 +}
  213 +
  214 +#pragma mark - touch event
  215 +
  216 +- (void)respondsToTapTextlabel {
  217 + if (self.clickTextLabelBlock) {
  218 + self.clickTextLabelBlock();
  219 + }
  220 +}
  221 +
  222 +#pragma mark - getter
  223 +
  224 +- (YBIBProgressDrawView *)drawView {
  225 + if (!_drawView) {
  226 + _drawView = [YBIBProgressDrawView new];
  227 + _drawView.backgroundColor = [UIColor clearColor];
  228 + }
  229 + return _drawView;
  230 +}
  231 +
  232 +- (UILabel *)textLabel {
  233 + if (!_textLabel) {
  234 + _textLabel = [UILabel new];
  235 + _textLabel.textColor = [UIColor whiteColor];
  236 + _textLabel.numberOfLines = 0;
  237 + _textLabel.font = [UIFont systemFontOfSize:14];
  238 + _textLabel.textAlignment = NSTextAlignmentCenter;
  239 + UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToTapTextlabel)];
  240 + [_textLabel addGestureRecognizer:tapGesture];
  241 + _textLabel.userInteractionEnabled = YES;
  242 + }
  243 + return _textLabel;
  244 +}
  245 +
  246 +- (UIImageView *)imageView {
  247 + if (!_imageView) {
  248 + _imageView = [UIImageView new];
  249 + _imageView.image = [YBIBIconManager sharedManager].loadingImage();
  250 + _imageView.layer.shadowColor = UIColor.darkGrayColor.CGColor;
  251 + _imageView.layer.shadowOffset = CGSizeMake(0, 1);
  252 + _imageView.layer.shadowOpacity = 1;
  253 + _imageView.layer.shadowRadius = 4;
  254 + }
  255 + return _imageView;
  256 +}
  257 +
  258 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/AuxiliaryView/YBIBToastView.h 0 → 100644
  1 +//
  2 +// YBIBToastView.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/20.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@interface UIView (YBIBToast)
  14 +
  15 +- (void)ybib_showHookToast:(NSString *)text;
  16 +
  17 +- (void)ybib_showForkToast:(NSString *)text;
  18 +
  19 +- (void)ybib_hideToast;
  20 +
  21 +@end
  22 +
  23 +
  24 +typedef NS_ENUM(NSInteger, YBIBToastType) {
  25 + YBIBToastTypeNone,
  26 + YBIBToastTypeHook,
  27 + YBIBToastTypeFork
  28 +};
  29 +
  30 +@interface YBIBToastView : UIView
  31 +
  32 +- (void)showWithText:(NSString *)text type:(YBIBToastType)type;
  33 +
  34 +@end
  35 +
  36 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/AuxiliaryView/YBIBToastView.m 0 → 100644
  1 +//
  2 +// YBIBToastView.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/20.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBToastView.h"
  10 +#import <objc/runtime.h>
  11 +
  12 +@interface UIView ()
  13 +@property (nonatomic, strong, readonly) YBIBToastView *ybib_toast;
  14 +@end
  15 +
  16 +@implementation UIView (YBIBToast)
  17 +
  18 +- (void)ybib_showHookToast:(NSString *)text {
  19 + [self ybib_showToastWithText:text type:YBIBToastTypeHook hideAfterDelay:1.7];
  20 +}
  21 +
  22 +- (void)ybib_showForkToast:(NSString *)text {
  23 + [self ybib_showToastWithText:text type:YBIBToastTypeFork hideAfterDelay:1.7];
  24 +}
  25 +
  26 +- (void)ybib_showToastWithText:(NSString *)text type:(YBIBToastType)type hideAfterDelay:(NSTimeInterval)delay {
  27 + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(ybib_hideToast) object:nil];
  28 +
  29 + YBIBToastView *toast = self.ybib_toast;
  30 + if (!toast.superview) {
  31 + [self addSubview:toast];
  32 + toast.translatesAutoresizingMaskIntoConstraints = NO;
  33 + NSLayoutConstraint *layA = [NSLayoutConstraint constraintWithItem:toast attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
  34 + NSLayoutConstraint *layB = [NSLayoutConstraint constraintWithItem:toast attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
  35 + NSLayoutConstraint *layC = [NSLayoutConstraint constraintWithItem:toast attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1 constant:40];
  36 + NSLayoutConstraint *layD = [NSLayoutConstraint constraintWithItem:toast attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationLessThanOrEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1 constant:-40];
  37 + [self addConstraints:@[layA, layB, layC, layD]];
  38 + }
  39 +
  40 + [toast showWithText:text type:type];
  41 + [self performSelector:@selector(ybib_hideToast) withObject:nil afterDelay:delay];
  42 +}
  43 +
  44 +- (void)ybib_hideToast {
  45 + YBIBToastView *toast = self.ybib_toast;
  46 + if (toast && toast.superview) {
  47 + [UIView animateWithDuration:0.25 animations:^{
  48 + toast.alpha = 0;
  49 + } completion:^(BOOL finished) {
  50 + [toast removeFromSuperview];
  51 + toast.alpha = 1;
  52 + }];
  53 + }
  54 +}
  55 +
  56 +static void *YBIBToastKey = &YBIBToastKey;
  57 +- (void)setYbib_toast:(YBIBToastView *)ybib_toast {
  58 + objc_setAssociatedObject(self, YBIBToastKey, ybib_toast, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  59 +}
  60 +- (YBIBToastView *)ybib_toast {
  61 + YBIBToastView *toast = objc_getAssociatedObject(self, YBIBToastKey);
  62 + if (!toast) {
  63 + toast = [YBIBToastView new];
  64 + self.ybib_toast = toast;
  65 + }
  66 + return toast;
  67 +}
  68 +
  69 +@end
  70 +
  71 +
  72 +@interface YBIBToastView () {
  73 + YBIBToastType _type;
  74 + CAShapeLayer *_shapeLayer;
  75 +}
  76 +@property (nonatomic, strong) UILabel *textLabel;
  77 +@end
  78 +
  79 +@implementation YBIBToastView
  80 +
  81 +#pragma mark - life cycle
  82 +
  83 +- (instancetype)initWithFrame:(CGRect)frame {
  84 + self = [super initWithFrame:frame];
  85 + if (self) {
  86 + self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.8];
  87 + self.userInteractionEnabled = NO;
  88 + self.layer.cornerRadius = 7;
  89 +
  90 + [self addSubview:self.textLabel];
  91 + }
  92 + return self;
  93 +}
  94 +
  95 +- (void)updateConstraints {
  96 + self.textLabel.translatesAutoresizingMaskIntoConstraints = NO;
  97 + NSLayoutConstraint *layA = [NSLayoutConstraint constraintWithItem:self.textLabel attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1 constant:20];
  98 + NSLayoutConstraint *layB = [NSLayoutConstraint constraintWithItem:self.textLabel attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1 constant:-20];
  99 + NSLayoutConstraint *layC = [NSLayoutConstraint constraintWithItem:self.textLabel attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1 constant:-15];
  100 + NSLayoutConstraint *layD = [NSLayoutConstraint constraintWithItem:self.textLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1 constant:70];
  101 + NSLayoutConstraint *layE = [NSLayoutConstraint constraintWithItem:self.textLabel attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:60];
  102 + [self addConstraints:@[layA, layB, layC, layD, layE]];
  103 + [super updateConstraints];
  104 +}
  105 +
  106 +- (void)layoutSubviews {
  107 + [super layoutSubviews];
  108 + [self startAnimation];
  109 +}
  110 +
  111 +#pragma mark - animation
  112 +
  113 +- (void)showWithText:(NSString *)text type:(YBIBToastType)type {
  114 + self.textLabel.text = text;
  115 + _type = type;
  116 + [self setNeedsLayout];
  117 +}
  118 +
  119 +- (void)startAnimation {
  120 + if (_shapeLayer && _shapeLayer.superlayer) {
  121 + [_shapeLayer removeFromSuperlayer];
  122 + }
  123 + _shapeLayer = [CAShapeLayer layer];
  124 + _shapeLayer.strokeColor = [UIColor whiteColor].CGColor;
  125 + _shapeLayer.fillColor = [UIColor clearColor].CGColor;
  126 + _shapeLayer.lineWidth = 5.0;
  127 + _shapeLayer.lineCap = @"round";
  128 + _shapeLayer.lineJoin = @"round";
  129 + _shapeLayer.strokeStart = 0.0;
  130 + _shapeLayer.strokeEnd = 0.0;
  131 +
  132 + UIBezierPath *bezierPath = [UIBezierPath bezierPath];
  133 + CGFloat r = 13.0;
  134 + CGFloat x = self.bounds.size.width / 2.0;
  135 + CGFloat y = 38.0;
  136 + switch (_type) {
  137 + case YBIBToastTypeHook: {
  138 + [bezierPath moveToPoint:CGPointMake(x - r - r / 2, y)];
  139 + [bezierPath addLineToPoint:CGPointMake(x - r / 2, y + r)];
  140 + [bezierPath addLineToPoint:CGPointMake(x + r * 2 - r / 2, y - r)];
  141 + }
  142 + break;
  143 + case YBIBToastTypeFork: {
  144 + [bezierPath moveToPoint:CGPointMake(x - r, y - r)];
  145 + [bezierPath addLineToPoint:CGPointMake(x + r, y + r)];
  146 + [bezierPath moveToPoint:CGPointMake(x - r, y + r)];
  147 + [bezierPath addLineToPoint:CGPointMake(x + r, y - r)];
  148 + }
  149 + break;
  150 + default:break;
  151 + }
  152 +
  153 + CABasicAnimation *baseAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
  154 + [baseAnimation setFromValue:@0.0];
  155 + [baseAnimation setToValue:@1.0];
  156 + [baseAnimation setDuration:0.3];
  157 + baseAnimation.removedOnCompletion = NO;
  158 + baseAnimation.fillMode = kCAFillModeBoth;
  159 +
  160 + _shapeLayer.path = bezierPath.CGPath;
  161 + [self.layer addSublayer:_shapeLayer];
  162 + [_shapeLayer addAnimation:baseAnimation forKey:@"strokeEnd"];
  163 +}
  164 +
  165 +#pragma mark - getter
  166 +
  167 +- (UILabel *)textLabel {
  168 + if (!_textLabel) {
  169 + _textLabel = [UILabel new];
  170 + _textLabel.textColor = [UIColor whiteColor];
  171 + _textLabel.font = [UIFont systemFontOfSize:14];
  172 + _textLabel.textAlignment = NSTextAlignmentCenter;
  173 + _textLabel.numberOfLines = 0;
  174 + }
  175 + return _textLabel;
  176 +}
  177 +
  178 +@end
  179 +
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Base/NSObject+YBImageBrowser.h 0 → 100644
  1 +//
  2 +// NSObject+YBImageBrowser.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/9/26.
  6 +// Copyright © 2019 杨波. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@interface NSObject (YBImageBrowser)
  14 +
  15 +@property (nonatomic, assign) CGFloat ybib_originAlpha;
  16 +
  17 +@end
  18 +
  19 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Base/NSObject+YBImageBrowser.m 0 → 100644
  1 +//
  2 +// NSObject+YBImageBrowser.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/9/26.
  6 +// Copyright © 2019 杨波. All rights reserved.
  7 +//
  8 +
  9 +#import "NSObject+YBImageBrowser.h"
  10 +#import <objc/runtime.h>
  11 +
  12 +@implementation NSObject (YBImageBrowser)
  13 +
  14 +static void *YBIBOriginAlphaKey = &YBIBOriginAlphaKey;
  15 +- (void)setYbib_originAlpha:(CGFloat)ybib_originAlpha {
  16 + objc_setAssociatedObject(self, YBIBOriginAlphaKey, @(ybib_originAlpha), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  17 +}
  18 +- (CGFloat)ybib_originAlpha {
  19 + NSNumber *alpha = objc_getAssociatedObject(self, YBIBOriginAlphaKey);
  20 + return alpha ? alpha.floatValue : 1;
  21 +}
  22 +
  23 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Base/YBIBAnimatedTransition.h 0 → 100644
  1 +//
  2 +// YBIBAnimatedTransition.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/6.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@protocol YBIBAnimatedTransition <NSObject>
  14 +@required
  15 +
  16 +- (void)yb_showTransitioningWithContainer:(UIView *)container startView:(nullable __kindof UIView *)startView startImage:(nullable UIImage *)startImage endFrame:(CGRect)endFrame orientation:(UIDeviceOrientation)orientation completion:(void(^)(void))completion;
  17 +
  18 +- (void)yb_hideTransitioningWithContainer:(UIView *)container startView:(nullable __kindof UIView *)startView endView:(UIView *)endView orientation:(UIDeviceOrientation)orientation completion:(void(^)(void))completion;
  19 +
  20 +@end
  21 +
  22 +
  23 +typedef NS_ENUM(NSInteger, YBIBTransitionType) {
  24 + /// 无动效
  25 + YBIBTransitionTypeNone,
  26 + /// 渐隐
  27 + YBIBTransitionTypeFade,
  28 + /// 连贯移动
  29 + YBIBTransitionTypeCoherent
  30 +};
  31 +
  32 +@interface YBIBAnimatedTransition : NSObject <YBIBAnimatedTransition>
  33 +
  34 +/// 入场动效类型
  35 +@property (nonatomic, assign) YBIBTransitionType showType;
  36 +/// 出场动效类型
  37 +@property (nonatomic, assign) YBIBTransitionType hideType;
  38 +
  39 +/// 入场动效持续时间
  40 +@property (nonatomic, assign) NSTimeInterval showDuration;
  41 +/// 出场动效持续时间
  42 +@property (nonatomic, assign) NSTimeInterval hideDuration;
  43 +
  44 +@end
  45 +
  46 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Base/YBIBAnimatedTransition.m 0 → 100644
  1 +//
  2 +// YBIBAnimatedTransition.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/6.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBAnimatedTransition.h"
  10 +
  11 +extern CGFloat YBIBRotationAngle(UIDeviceOrientation startOrientation, UIDeviceOrientation endOrientation);
  12 +
  13 +@implementation YBIBAnimatedTransition
  14 +
  15 +#pragma mark - life cycle
  16 +
  17 +- (instancetype)init {
  18 + self = [super init];
  19 + if (self) {
  20 + _showType = _hideType = YBIBTransitionTypeCoherent;
  21 + _showDuration = _hideDuration = 0.25;
  22 + }
  23 + return self;
  24 +}
  25 +
  26 +#pragma mark - <YBIBAnimationHandler>
  27 +
  28 +- (void)yb_showTransitioningWithContainer:(UIView *)container startView:(__kindof UIView *)startView startImage:(UIImage *)startImage endFrame:(CGRect)endFrame orientation:(UIDeviceOrientation)orientation completion:(void (^)(void))completion {
  29 + YBIBTransitionType type = self.showType;
  30 + if (type == YBIBTransitionTypeCoherent) {
  31 + if (CGRectIsEmpty(endFrame) || !startView || orientation != (UIDeviceOrientation)UIApplication.sharedApplication.statusBarOrientation) {
  32 + type = YBIBTransitionTypeFade;
  33 + }
  34 + }
  35 +
  36 + switch (type) {
  37 + case YBIBTransitionTypeNone: {
  38 + completion();
  39 + }
  40 + break;
  41 + case YBIBTransitionTypeFade: {
  42 +
  43 + BOOL animateValid = !CGRectIsEmpty(endFrame) && startView;
  44 +
  45 + UIImageView *animateImageView;
  46 + if (animateValid) {
  47 + animateImageView = [self imageViewAssimilateToView:startView];
  48 + animateImageView.frame = endFrame;
  49 + animateImageView.image = startImage;
  50 + [container addSubview:animateImageView];
  51 + }
  52 +
  53 + CGFloat rawAlpha = container.alpha;
  54 + container.alpha = 0;
  55 +
  56 + if (!animateValid) completion();
  57 +
  58 + [UIView animateWithDuration:self.showDuration animations:^{
  59 + container.alpha = rawAlpha;
  60 + } completion:^(BOOL finished) {
  61 + if (animateValid) {
  62 + [animateImageView removeFromSuperview];
  63 + completion();
  64 + }
  65 + }];
  66 +
  67 + }
  68 + break;
  69 + case YBIBTransitionTypeCoherent: {
  70 +
  71 + UIImageView *animateImageView = [self imageViewAssimilateToView:startView];
  72 + animateImageView.frame = [startView convertRect:startView.bounds toView:container];
  73 + animateImageView.image = startImage;
  74 +
  75 + [container addSubview:animateImageView];
  76 +
  77 + UIColor *rawBackgroundColor = container.backgroundColor;
  78 + container.backgroundColor = [rawBackgroundColor colorWithAlphaComponent:0];
  79 +
  80 + [UIView animateWithDuration:self.showDuration animations:^{
  81 + animateImageView.frame = endFrame;
  82 + container.backgroundColor = rawBackgroundColor;
  83 + } completion:^(BOOL finished) {
  84 + completion();
  85 + // Disappear smoothly.
  86 + [UIView animateWithDuration:0.2 animations:^{
  87 + animateImageView.alpha = 0;
  88 + } completion:^(BOOL finished) {
  89 + [animateImageView removeFromSuperview];
  90 + }];
  91 + }];
  92 +
  93 + }
  94 + break;
  95 + }
  96 +}
  97 +
  98 +- (void)yb_hideTransitioningWithContainer:(UIView *)container startView:(__kindof UIView *)startView endView:(UIView *)endView orientation:(UIDeviceOrientation)orientation completion:(void (^)(void))completion {
  99 + YBIBTransitionType type = self.hideType;
  100 + if (type == YBIBTransitionTypeCoherent && (!startView || !endView)) {
  101 + type = YBIBTransitionTypeFade;
  102 + }
  103 +
  104 + switch (type) {
  105 + case YBIBTransitionTypeNone: {
  106 + completion();
  107 + }
  108 + break;
  109 + case YBIBTransitionTypeFade: {
  110 +
  111 + CGFloat rawAlpha = container.alpha;
  112 +
  113 + [UIView animateWithDuration:self.hideDuration animations:^{
  114 + container.alpha = 0;
  115 + } completion:^(BOOL finished) {
  116 + completion();
  117 + container.alpha = rawAlpha;
  118 + }];
  119 +
  120 + }
  121 + break;
  122 + case YBIBTransitionTypeCoherent: {
  123 +
  124 + CGRect startFrame = startView.frame;
  125 + CGRect endFrame = [endView convertRect:endView.bounds toView:startView.superview];
  126 +
  127 + UIColor *rawBackgroundColor = container.backgroundColor;
  128 +
  129 + [UIView animateWithDuration:self.hideDuration animations:^{
  130 +
  131 + container.backgroundColor = [rawBackgroundColor colorWithAlphaComponent:0];
  132 +
  133 + startView.contentMode = endView.contentMode;
  134 +
  135 + CGAffineTransform transform = startView.transform;
  136 + UIDeviceOrientation statusBarOrientation = (UIDeviceOrientation)UIApplication.sharedApplication.statusBarOrientation;
  137 + if (orientation != statusBarOrientation) {
  138 + transform = CGAffineTransformRotate(transform, YBIBRotationAngle(orientation, statusBarOrientation));
  139 + }
  140 +
  141 + if ([startView isKindOfClass:UIImageView.self]) {
  142 + startView.frame = endFrame;
  143 + startView.transform = transform;
  144 + } else {
  145 + CGFloat scale = MAX(endFrame.size.width / startFrame.size.width, endFrame.size.height / startFrame.size.height);
  146 + startView.center = CGPointMake(endFrame.size.width * startView.layer.anchorPoint.x + endFrame.origin.x, endFrame.size.height * startView.layer.anchorPoint.y + endFrame.origin.y);
  147 + startView.transform = CGAffineTransformScale(transform, scale, scale);
  148 + }
  149 +
  150 + } completion:^(BOOL finished) {
  151 + completion();
  152 + container.backgroundColor = rawBackgroundColor;
  153 + }];
  154 +
  155 + }
  156 + break;
  157 + }
  158 +}
  159 +
  160 +#pragma mark - private
  161 +
  162 +- (UIImageView *)imageViewAssimilateToView:(nullable __kindof UIView *)view {
  163 + UIImageView *animateImageView = [UIImageView new];
  164 + if ([view isKindOfClass:UIImageView.self]) {
  165 + animateImageView.contentMode = view.contentMode;
  166 + } else {
  167 + animateImageView.contentMode = UIViewContentModeScaleAspectFill;
  168 + }
  169 + animateImageView.layer.masksToBounds = view.layer.masksToBounds;
  170 + animateImageView.layer.cornerRadius = view.layer.cornerRadius;
  171 + animateImageView.layer.backgroundColor = view.layer.backgroundColor;
  172 + return animateImageView;
  173 +}
  174 +
  175 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Base/YBIBCollectionView.h 0 → 100644
  1 +//
  2 +// YBIBCollectionView.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/6.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBCollectionViewLayout.h"
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@interface YBIBCollectionView : UICollectionView
  14 +
  15 +@property (nonatomic, strong, readonly) YBIBCollectionViewLayout *layout;
  16 +
  17 +- (NSString *)reuseIdentifierForCellClass:(Class)cellClass;
  18 +
  19 +- (nullable UICollectionViewCell *)centerCell;
  20 +
  21 +- (void)scrollToPage:(NSInteger)page;
  22 +
  23 +@end
  24 +
  25 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Base/YBIBCollectionView.m 0 → 100644
  1 +//
  2 +// YBIBCollectionView.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/6.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBCollectionView.h"
  10 +
  11 +@implementation YBIBCollectionView {
  12 + NSMutableSet *_reuseSet;
  13 +}
  14 +
  15 +#pragma mark - life cycle
  16 +
  17 +- (instancetype)initWithFrame:(CGRect)frame {
  18 + _layout = [YBIBCollectionViewLayout new];
  19 + return [self initWithFrame:frame collectionViewLayout:_layout];
  20 +}
  21 +
  22 +- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(nonnull UICollectionViewLayout *)layout {
  23 + self = [super initWithFrame:frame collectionViewLayout:layout];
  24 + if (self) {
  25 + self.backgroundColor = [UIColor clearColor];
  26 + self.pagingEnabled = YES;
  27 + self.showsHorizontalScrollIndicator = NO;
  28 + self.showsVerticalScrollIndicator = NO;
  29 + self.alwaysBounceVertical = NO;
  30 + self.alwaysBounceHorizontal = NO;
  31 + self.decelerationRate = UIScrollViewDecelerationRateFast;
  32 + if (@available(iOS 11.0, *)) {
  33 + self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
  34 + }
  35 + _reuseSet = [NSMutableSet set];
  36 + }
  37 + return self;
  38 +}
  39 +
  40 +#pragma mark - public
  41 +
  42 +- (NSString *)reuseIdentifierForCellClass:(Class)cellClass {
  43 + NSString *identifier = NSStringFromClass(cellClass);
  44 + if (![_reuseSet containsObject:identifier]) {
  45 + NSString *path = [[NSBundle mainBundle] pathForResource:identifier ofType:@"nib"];
  46 + if (path) {
  47 + [self registerNib:[UINib nibWithNibName:identifier bundle:nil] forCellWithReuseIdentifier:identifier];
  48 + } else {
  49 + [self registerClass:cellClass forCellWithReuseIdentifier:identifier];
  50 + }
  51 + [_reuseSet addObject:identifier];
  52 + }
  53 + return identifier;
  54 +}
  55 +
  56 +- (UICollectionViewCell *)centerCell {
  57 + NSArray<UICollectionViewCell *> *cells = [self visibleCells];
  58 + if (cells.count == 0) return nil;
  59 +
  60 + UICollectionViewCell *res = cells[0];
  61 + CGFloat centerX = self.contentOffset.x + (self.bounds.size.width / 2.0);
  62 + for (int i = 1; i < cells.count; ++i) {
  63 + if (ABS(cells[i].center.x - centerX) < ABS(res.center.x - centerX)) {
  64 + res = cells[i];
  65 + }
  66 + }
  67 + return res;
  68 +}
  69 +
  70 +- (void)scrollToPage:(NSInteger)page {
  71 + [self setContentOffset:CGPointMake(self.bounds.size.width * page, 0)];
  72 +}
  73 +
  74 +#pragma mark - hit test
  75 +
  76 +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
  77 + UIView *view = [super hitTest:point withEvent:event];
  78 + // When the hit-test view is 'UISlider', set '_scrollEnabled' to 'NO', avoid gesture conflicts.
  79 + self.scrollEnabled = ![view isKindOfClass:UISlider.class];
  80 + return view;
  81 +}
  82 +
  83 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Base/YBIBCollectionViewLayout.h 0 → 100644
  1 +//
  2 +// YBIBCollectionViewLayout.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 杨少 on 2018/4/17.
  6 +// Copyright © 2018年 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +
  11 +@interface YBIBCollectionViewLayout : UICollectionViewFlowLayout
  12 +
  13 +@property (nonatomic, assign) CGFloat distanceBetweenPages;
  14 +
  15 +@end
  16 +
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Base/YBIBCollectionViewLayout.m 0 → 100644
  1 +//
  2 +// YBIBCollectionViewLayout.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 杨少 on 2018/4/17.
  6 +// Copyright © 2018年 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBCollectionViewLayout.h"
  10 +
  11 +@implementation YBIBCollectionViewLayout
  12 +
  13 +- (instancetype)init {
  14 + self = [super init];
  15 + if (self) {
  16 + self.minimumLineSpacing = 0;
  17 + self.minimumInteritemSpacing = 0;
  18 + self.sectionInset = UIEdgeInsetsZero;
  19 + self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
  20 + _distanceBetweenPages = 20;
  21 + }
  22 + return self;
  23 +}
  24 +
  25 +- (void)prepareLayout {
  26 + [super prepareLayout];
  27 + self.itemSize = self.collectionView.bounds.size;
  28 +}
  29 +
  30 +- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
  31 + NSArray<UICollectionViewLayoutAttributes *> *layoutAttsArray = [[NSArray alloc] initWithArray:[super layoutAttributesForElementsInRect:rect] copyItems:YES];
  32 + CGFloat halfWidth = self.collectionView.bounds.size.width / 2.0;
  33 + CGFloat centerX = self.collectionView.contentOffset.x + halfWidth;
  34 + [layoutAttsArray enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  35 + obj.center = CGPointMake(obj.center.x + (obj.center.x - centerX) / halfWidth * self.distanceBetweenPages / 2, obj.center.y);
  36 + }];
  37 + return layoutAttsArray;
  38 +}
  39 +
  40 +- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
  41 + return YES;
  42 +}
  43 +
  44 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Base/YBIBContainerView.h 0 → 100644
  1 +//
  2 +// YBIBContainerView.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/7/11.
  6 +// Copyright © 2019 杨波. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@interface YBIBContainerView : UIView
  14 +
  15 +@end
  16 +
  17 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Base/YBIBContainerView.m 0 → 100644
  1 +//
  2 +// YBIBContainerView.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/7/11.
  6 +// Copyright © 2019 杨波. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBContainerView.h"
  10 +
  11 +@implementation YBIBContainerView
  12 +
  13 +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
  14 + UIView *originView = [super hitTest:point withEvent:event];
  15 + if ([originView isKindOfClass:self.class]) {
  16 + // Continue hit-testing if the view is kind of 'self.class'.
  17 + return nil;
  18 + }
  19 + return originView;
  20 +}
  21 +
  22 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Base/YBIBDataMediator.h 0 → 100644
  1 +//
  2 +// YBIBDataMediator.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/6.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBImageBrowser.h"
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@interface YBIBDataMediator : NSObject
  14 +
  15 +- (instancetype)initWithBrowser:(YBImageBrowser *)browser;
  16 +
  17 +@property (nonatomic, assign) NSUInteger dataCacheCountLimit;
  18 +
  19 +- (NSInteger)numberOfCells;
  20 +
  21 +- (id<YBIBDataProtocol>)dataForCellAtIndex:(NSInteger)index;
  22 +
  23 +- (void)clear;
  24 +
  25 +@property (nonatomic, assign) NSUInteger preloadCount;
  26 +
  27 +- (void)preloadWithPage:(NSInteger)page;
  28 +
  29 +@end
  30 +
  31 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Base/YBIBDataMediator.m 0 → 100644
  1 +//
  2 +// YBIBDataMediator.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/6.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBDataMediator.h"
  10 +#import "YBImageBrowser+Internal.h"
  11 +
  12 +@implementation YBIBDataMediator {
  13 + __weak YBImageBrowser *_browser;
  14 + NSCache<NSNumber *, id<YBIBDataProtocol>> *_dataCache;
  15 +}
  16 +
  17 +#pragma mark - life cycle
  18 +
  19 +- (instancetype)initWithBrowser:(YBImageBrowser *)browser {
  20 + if (self = [super init]) {
  21 + _browser = browser;
  22 + _dataCache = [NSCache new];
  23 + }
  24 + return self;
  25 +}
  26 +
  27 +#pragma mark - public
  28 +
  29 +- (NSInteger)numberOfCells {
  30 + return _browser.dataSource ? [_browser.dataSource yb_numberOfCellsInImageBrowser:_browser] : _browser.dataSourceArray.count;
  31 +}
  32 +
  33 +- (id<YBIBDataProtocol>)dataForCellAtIndex:(NSInteger)index {
  34 + if (index < 0 || index > self.numberOfCells - 1) return nil;
  35 +
  36 + id<YBIBDataProtocol> data = [_dataCache objectForKey:@(index)];
  37 + if (!data) {
  38 + data = _browser.dataSource ? [_browser.dataSource yb_imageBrowser:_browser dataForCellAtIndex:index] : _browser.dataSourceArray[index];
  39 + [_dataCache setObject:data forKey:@(index)];
  40 + [_browser implementGetBaseInfoProtocol:data];
  41 + }
  42 + return data;
  43 +}
  44 +
  45 +- (void)clear {
  46 + [_dataCache removeAllObjects];
  47 +}
  48 +
  49 +- (void)preloadWithPage:(NSInteger)page {
  50 + if (_preloadCount == 0) return;
  51 +
  52 + NSInteger left = -(_preloadCount / 2), right = _preloadCount - ABS(left);
  53 + for (NSInteger i = left; i <= right; ++i) {
  54 + if (i == 0) continue;
  55 + id<YBIBDataProtocol> targetData = [self dataForCellAtIndex:page + i];
  56 + if ([targetData respondsToSelector:@selector(yb_preload)]) {
  57 + [targetData yb_preload];
  58 + }
  59 + }
  60 +}
  61 +
  62 +#pragma mark - getters & setters
  63 +
  64 +- (void)setDataCacheCountLimit:(NSUInteger)dataCacheCountLimit {
  65 + _dataCacheCountLimit = dataCacheCountLimit;
  66 + _dataCache.countLimit = dataCacheCountLimit;
  67 +}
  68 +
  69 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Base/YBIBScreenRotationHandler.h 0 → 100644
  1 +//
  2 +// YBIBScreenRotationHandler.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/8.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBImageBrowser.h"
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@interface YBIBScreenRotationHandler : NSObject
  14 +
  15 +- (instancetype)initWithBrowser:(YBImageBrowser *)browser;
  16 +
  17 +- (void)startObserveStatusBarOrientation;
  18 +
  19 +- (void)startObserveDeviceOrientation;
  20 +
  21 +- (void)clear;
  22 +
  23 +- (void)configContainerSize:(CGSize)size;
  24 +
  25 +- (CGSize)containerSizeWithOrientation:(UIDeviceOrientation)orientation;
  26 +
  27 +@property (nonatomic, assign, readonly, getter=isRotating) BOOL rotating;
  28 +
  29 +@property (nonatomic, assign, readonly) UIDeviceOrientation currentOrientation;
  30 +
  31 +@property (nonatomic, assign) UIInterfaceOrientationMask supportedOrientations;
  32 +
  33 +@property (nonatomic, assign) NSTimeInterval rotationDuration;
  34 +
  35 +@end
  36 +
  37 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Base/YBIBScreenRotationHandler.m 0 → 100644
  1 +//
  2 +// YBIBScreenRotationHandler.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/8.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBScreenRotationHandler.h"
  10 +#import "YBIBUtilities.h"
  11 +#import "YBIBCellProtocol.h"
  12 +#import "YBImageBrowser+Internal.h"
  13 +
  14 +BOOL YBIBValidDeviceOrientation(UIDeviceOrientation orientation) {
  15 + static NSSet *validSet;
  16 + static dispatch_once_t onceToken;
  17 + dispatch_once(&onceToken, ^{
  18 + validSet = [NSSet setWithObjects:@(UIDeviceOrientationPortrait), @(UIDeviceOrientationPortraitUpsideDown), @(UIDeviceOrientationLandscapeLeft), @(UIDeviceOrientationLandscapeRight), nil];
  19 + });
  20 + return [validSet containsObject:@(orientation)];
  21 +}
  22 +
  23 +CGFloat YBIBRotationAngle(UIDeviceOrientation startOrientation, UIDeviceOrientation endOrientation) {
  24 + static NSDictionary<NSNumber*, NSNumber*> *angleMap;
  25 + static dispatch_once_t onceToken;
  26 + dispatch_once(&onceToken, ^{
  27 + angleMap = @{@(UIDeviceOrientationPortrait):@(0), @(UIDeviceOrientationPortraitUpsideDown):@(M_PI), @(UIDeviceOrientationLandscapeLeft):@(M_PI_2), @(UIDeviceOrientationLandscapeRight): @(-M_PI_2)};
  28 + });
  29 + NSNumber *start = angleMap[@(startOrientation)], *end = angleMap[@(endOrientation)];
  30 + CGFloat res = CGFLOAT_IS_DOUBLE ? end.doubleValue - start.doubleValue : end.floatValue - start.floatValue;
  31 + if (ABS(res) > M_PI) {
  32 + return res > 0 ? res - M_PI * 2 : M_PI * 2 + res;
  33 + }
  34 + return res;
  35 +}
  36 +
  37 +
  38 +static NSUInteger const kMaskNull = 10000;
  39 +
  40 +@interface YBIBScreenRotationHandler ()
  41 +@property (nonatomic, assign) BOOL rotating;
  42 +@property (nonatomic, assign) UIDeviceOrientation currentOrientation;
  43 +@end
  44 +
  45 +@implementation YBIBScreenRotationHandler {
  46 + __weak YBImageBrowser *_browser;
  47 + CGSize _verticalContainerSize;
  48 + CGSize _horizontalContainerSize;
  49 + NSInteger _recordPage;
  50 +}
  51 +
  52 +#pragma mark - life cycle
  53 +
  54 +- (void)dealloc {
  55 + [self clear];
  56 +}
  57 +
  58 +- (instancetype)initWithBrowser:(YBImageBrowser *)browser {
  59 + if (self = [super init]) {
  60 + _browser = browser;
  61 + _rotating = NO;
  62 + _supportedOrientations = UIInterfaceOrientationMaskAllButUpsideDown;
  63 + _rotationDuration = 0.25;
  64 + }
  65 + return self;
  66 +}
  67 +
  68 +#pragma mark - public
  69 +
  70 +- (void)startObserveStatusBarOrientation {
  71 + self.currentOrientation = (UIDeviceOrientation)UIApplication.sharedApplication.statusBarOrientation;
  72 + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidChangedStatusBarOrientationNotification:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
  73 + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillChangeStatusBarOrientationNotification:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
  74 +}
  75 +
  76 +- (void)startObserveDeviceOrientation {
  77 + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChangeNotification:) name:UIDeviceOrientationDidChangeNotification object:nil];
  78 +}
  79 +
  80 +- (void)clear {
  81 + [[NSNotificationCenter defaultCenter] removeObserver:self];
  82 +}
  83 +
  84 +- (void)configContainerSize:(CGSize)size {
  85 + if (UIDeviceOrientationIsLandscape(self.currentOrientation)) {
  86 + // Now is horizontal.
  87 + _verticalContainerSize = CGSizeMake(size.height, size.width);
  88 + _horizontalContainerSize = size;
  89 + } else {
  90 + // Now is vertical.
  91 + _verticalContainerSize = size;
  92 + _horizontalContainerSize = CGSizeMake(size.height, size.width);
  93 + }
  94 +}
  95 +
  96 +- (CGSize)containerSizeWithOrientation:(UIDeviceOrientation)orientation {
  97 + return UIDeviceOrientationIsLandscape(orientation) ? _horizontalContainerSize : _verticalContainerSize;
  98 +}
  99 +
  100 +#pragma mark - private
  101 +
  102 +- (BOOL)supportedOfOrientation:(UIDeviceOrientation)orientation {
  103 + if (!YBIBValidDeviceOrientation(orientation)) return NO;
  104 + NSMutableSet *set = [NSMutableSet set];
  105 + if (_supportedOrientations & UIInterfaceOrientationMaskPortrait) [set addObject:@(UIDeviceOrientationPortrait)];
  106 + if (_supportedOrientations & UIInterfaceOrientationMaskPortraitUpsideDown) [set addObject:@(UIDeviceOrientationPortraitUpsideDown)];
  107 + if (_supportedOrientations & UIInterfaceOrientationMaskLandscapeRight) [set addObject:@(UIDeviceOrientationLandscapeLeft)];
  108 + if (_supportedOrientations & UIInterfaceOrientationMaskLandscapeLeft) [set addObject:@(UIDeviceOrientationLandscapeRight)];
  109 + return [set containsObject:@(orientation)];
  110 +}
  111 +
  112 +- (BOOL)supportedOnlyOneSystemOrientation {
  113 + UIInterfaceOrientationMask mask = [self supportSystemOrientationMask];
  114 + return mask == (mask & (-mask));
  115 +}
  116 +
  117 +- (void)orientationWillChangeWithExpectOrientation:(UIDeviceOrientation)orientation centerCell:(UICollectionViewCell<YBIBCellProtocol> *)centerCell {
  118 + if ([centerCell respondsToSelector:@selector(yb_orientationWillChangeWithExpectOrientation:)]) {
  119 + [centerCell yb_orientationWillChangeWithExpectOrientation:orientation];
  120 + }
  121 + for (id<YBIBToolViewHandler> handler in _browser.toolViewHandlers) {
  122 + if ([handler respondsToSelector:@selector(yb_orientationWillChangeWithExpectOrientation:)]) {
  123 + [handler yb_orientationWillChangeWithExpectOrientation:orientation];
  124 + }
  125 + }
  126 +}
  127 +
  128 +- (void)orientationChangeAnimationWithExpectOrientation:(UIDeviceOrientation)orientation centerCell:(UICollectionViewCell<YBIBCellProtocol> *)centerCell {
  129 + if ([centerCell respondsToSelector:@selector(yb_orientationChangeAnimationWithExpectOrientation:)]) {
  130 + [centerCell yb_orientationChangeAnimationWithExpectOrientation:orientation];
  131 + [centerCell layoutIfNeeded]; // Compatible with autolayout.
  132 + }
  133 + for (id<YBIBToolViewHandler> handler in _browser.toolViewHandlers) {
  134 + if ([handler respondsToSelector:@selector(yb_orientationChangeAnimationWithExpectOrientation:)]) {
  135 + [handler yb_orientationChangeAnimationWithExpectOrientation:orientation];
  136 + }
  137 + }
  138 +}
  139 +
  140 +- (void)orientationDidChangedWithOrientation:(UIDeviceOrientation)orientation centerCell:(UICollectionViewCell<YBIBCellProtocol> *)centerCell {
  141 + if ([centerCell respondsToSelector:@selector(yb_orientationDidChangedWithOrientation:)]) {
  142 + [centerCell yb_orientationDidChangedWithOrientation:orientation];
  143 + }
  144 + for (id<YBIBToolViewHandler> handler in _browser.toolViewHandlers) {
  145 + if ([handler respondsToSelector:@selector(yb_orientationDidChangedWithOrientation:)]) {
  146 + [handler yb_orientationDidChangedWithOrientation:orientation];
  147 + }
  148 + }
  149 +}
  150 +
  151 +#pragma mark - event
  152 +
  153 +- (void)deviceOrientationDidChangeNotification:(NSNotification *)note {
  154 + if (![self supportedOnlyOneSystemOrientation]) return;
  155 + if (_browser.isTransitioning || self.rotating) return;
  156 +
  157 + UIDeviceOrientation expectOrientation = [UIDevice currentDevice].orientation;
  158 + if (expectOrientation == self.currentOrientation || ![self supportedOfOrientation:expectOrientation]) return;
  159 +
  160 + self.rotating = YES;
  161 +
  162 + // Align.
  163 + [_browser.collectionView scrollToPage:_browser.currentPage];
  164 + // Record current page number before transforming.
  165 + NSInteger currentPage = _browser.currentPage;
  166 +
  167 + UIDeviceOrientation statusBarOrientation = (UIDeviceOrientation)UIApplication.sharedApplication.statusBarOrientation;
  168 + CGFloat angleStatusBarToExpect = YBIBRotationAngle(statusBarOrientation, expectOrientation);
  169 + CGFloat angleCurrentToExpect = YBIBRotationAngle(_currentOrientation, expectOrientation);
  170 + CGRect expectBounds = (CGRect){CGPointZero, [self containerSizeWithOrientation:expectOrientation]};
  171 + UICollectionViewCell<YBIBCellProtocol> *centerCell = (UICollectionViewCell<YBIBCellProtocol> *)self->_browser.collectionView.centerCell;
  172 + // Animate smoothly if bigger rotation angle.
  173 + NSTimeInterval duration = self.rotationDuration * (ABS(angleCurrentToExpect) > M_PI_2 ? 2 : 1);
  174 +
  175 + // 'collectionView' transformation.
  176 + self->_browser.collectionView.bounds = expectBounds;
  177 + self->_browser.collectionView.transform = CGAffineTransformMakeRotation(angleStatusBarToExpect);
  178 + centerCell.contentView.transform = CGAffineTransformMakeRotation(-angleCurrentToExpect);
  179 +
  180 + // Reset to prevent the page number change after transforming.
  181 + [self->_browser.collectionView scrollToPage:currentPage];
  182 +
  183 + [self orientationWillChangeWithExpectOrientation:expectOrientation centerCell:centerCell];
  184 +
  185 + [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
  186 +
  187 + // Maybe the internal UI need to transform.
  188 + [self orientationChangeAnimationWithExpectOrientation:expectOrientation centerCell:centerCell];
  189 +
  190 + centerCell.contentView.bounds = expectBounds;
  191 + centerCell.contentView.transform = CGAffineTransformIdentity;
  192 +
  193 + self->_browser.containerView.bounds = expectBounds;
  194 + self->_browser.containerView.transform = CGAffineTransformMakeRotation(angleStatusBarToExpect);
  195 +
  196 + } completion:^(BOOL finished) {
  197 + self.currentOrientation = expectOrientation;
  198 + self.rotating = NO;
  199 +
  200 + [self orientationDidChangedWithOrientation:expectOrientation centerCell:centerCell];
  201 + }];
  202 +}
  203 +
  204 +- (void)applicationWillChangeStatusBarOrientationNotification:(NSNotification *)noti {
  205 + if ([self supportedOnlyOneSystemOrientation]) return;
  206 +
  207 + self.rotating = YES;
  208 + // Record current page number before transforming.
  209 + _recordPage = _browser.currentPage;
  210 +
  211 + UICollectionViewCell<YBIBCellProtocol> *centerCell = (UICollectionViewCell<YBIBCellProtocol> *)self->_browser.collectionView.centerCell;
  212 + UIDeviceOrientation expectOrientation = ((NSNumber *)noti.userInfo[UIApplicationStatusBarOrientationUserInfoKey]).integerValue;
  213 + [self orientationWillChangeWithExpectOrientation:expectOrientation centerCell:centerCell];
  214 +}
  215 +
  216 +- (void)applicationDidChangedStatusBarOrientationNotification:(NSNotification *)noti {
  217 + if ([self supportedOnlyOneSystemOrientation]) return;
  218 +
  219 + UIDeviceOrientation expectOrientation = (UIDeviceOrientation)UIApplication.sharedApplication.statusBarOrientation;
  220 + UICollectionViewCell<YBIBCellProtocol> *centerCell = (UICollectionViewCell<YBIBCellProtocol> *)self->_browser.collectionView.centerCell;
  221 +
  222 + [self orientationChangeAnimationWithExpectOrientation:expectOrientation centerCell:centerCell];
  223 +
  224 + CGRect expectBounds = (CGRect){CGPointZero, [self containerSizeWithOrientation:expectOrientation]};
  225 + self->_browser.collectionView.layout.itemSize = expectBounds.size;
  226 +
  227 + // Reset to prevent the page number change after transforming.
  228 + [_browser.collectionView scrollToPage:_recordPage];
  229 +
  230 + self.currentOrientation = expectOrientation;
  231 + self.rotating = NO;
  232 +
  233 + [self orientationDidChangedWithOrientation:expectOrientation centerCell:centerCell];
  234 +}
  235 +
  236 +#pragma mark - getters & setters
  237 +
  238 +- (void)setRotating:(BOOL)rotating {
  239 + _rotating = rotating;
  240 + _browser.containerView.userInteractionEnabled = !rotating;
  241 + _browser.collectionView.userInteractionEnabled = !rotating;
  242 + _browser.collectionView.panGestureRecognizer.enabled = !rotating;
  243 +}
  244 +
  245 +#pragma mark - calculate supported orientation of system
  246 +
  247 +- (UIInterfaceOrientationMask)supportSystemOrientationMask {
  248 + UIInterfaceOrientationMask limitMask = 0;
  249 + // IphoneX series do not support UIInterfaceOrientationMaskPortraitUpsideDown, except selector '-application:supportedInterfaceOrientationsForWindow:' of '[UIApplication sharedApplication].delegate' return 0. Maybe it is BUG of Apple.
  250 + BOOL ignoreUpsideDownIfIphoneX = YES;
  251 + UIInterfaceOrientationMask delegateMask = [self maskOfApplicationDelegate];
  252 + if (delegateMask != kMaskNull) {
  253 + if (delegateMask == 0) {
  254 + // Apple do.
  255 + limitMask = UIInterfaceOrientationMaskAll;
  256 + ignoreUpsideDownIfIphoneX = NO;
  257 + } else {
  258 + limitMask = delegateMask;
  259 + }
  260 + } else {
  261 + // Lower priority.
  262 + limitMask = [self maskOfInfoPlist];
  263 + }
  264 +
  265 + UIInterfaceOrientationMask supportMask = limitMask & [self maskOfViewController];
  266 +
  267 + if (ignoreUpsideDownIfIphoneX && YBIBIsIphoneXSeries() && (supportMask & UIInterfaceOrientationMaskPortraitUpsideDown)) {
  268 + supportMask ^= UIInterfaceOrientationMaskPortraitUpsideDown;
  269 + }
  270 + return supportMask;
  271 +}
  272 +
  273 +- (UIInterfaceOrientationMask)maskOfInfoPlist {
  274 + // 'Info.plist' will not change in a process.
  275 + static UIInterfaceOrientationMask mask = 0;
  276 + static dispatch_once_t onceToken;
  277 + dispatch_once(&onceToken, ^{
  278 + NSString *path = [[NSBundle mainBundle] pathForResource:@"Info" ofType:@"plist"];
  279 + NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:path];
  280 + NSArray *array = dict[@"UISupportedInterfaceOrientations"];
  281 + NSSet *set = [NSSet setWithArray:array];
  282 + if ([set containsObject:@"UIInterfaceOrientationPortrait"]) mask |= UIInterfaceOrientationMaskPortrait;
  283 + if ([set containsObject:@"UIInterfaceOrientationLandscapeRight"]) mask |= UIInterfaceOrientationMaskLandscapeRight;
  284 + if ([set containsObject:@"UIInterfaceOrientationLandscapeLeft"]) mask |= UIInterfaceOrientationMaskLandscapeLeft;
  285 + if ([set containsObject:@"UIInterfaceOrientationPortraitUpsideDown"]) mask |= UIInterfaceOrientationMaskPortraitUpsideDown;
  286 + });
  287 + return mask == 0 ? kMaskNull : mask;
  288 +}
  289 +
  290 +- (UIInterfaceOrientationMask)maskOfApplicationDelegate {
  291 + UIInterfaceOrientationMask mask = kMaskNull;
  292 + id<UIApplicationDelegate> delegate = [UIApplication sharedApplication].delegate;
  293 + if ([delegate respondsToSelector:@selector(application:supportedInterfaceOrientationsForWindow:)]) {
  294 + mask = [delegate application:[UIApplication sharedApplication] supportedInterfaceOrientationsForWindow:_browser.window];
  295 + }
  296 + return mask;
  297 +}
  298 +
  299 +- (UIInterfaceOrientationMask)maskOfViewController {
  300 + UIInterfaceOrientationMask mask = kMaskNull;
  301 +
  302 + // Find the UIViewController whitch 'browser' followed.
  303 + UIViewController *target = nil;
  304 + id next = _browser;
  305 + while (next) {
  306 + if ([next isKindOfClass:UIViewController.self]) {
  307 + target = next;
  308 + break;
  309 + }
  310 + if ([next isKindOfClass:UIWindow.self]) {
  311 + target = YBIBTopControllerByWindow(next);
  312 + break;
  313 + }
  314 + next = [next nextResponder];
  315 + }
  316 +
  317 + // Cover directly.
  318 + if (target.tabBarController) {
  319 + mask = target.tabBarController.shouldAutorotate ? target.tabBarController.supportedInterfaceOrientations : 0;
  320 + } else if (target.navigationController) {
  321 + mask = target.navigationController.shouldAutorotate ? target.navigationController.supportedInterfaceOrientations : 0;
  322 + } else {
  323 + mask = target.shouldAutorotate ? target.supportedInterfaceOrientations : 0;
  324 + }
  325 + return mask;
  326 +}
  327 +
  328 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Base/YBImageBrowser+Internal.h 0 → 100644
  1 +//
  2 +// YBImageBrowser+Internal.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/7/1.
  6 +// Copyright © 2019 杨波. All rights reserved.
  7 +//
  8 +
  9 +#import "YBImageBrowser.h"
  10 +#import "YBIBContainerView.h"
  11 +
  12 +NS_ASSUME_NONNULL_BEGIN
  13 +
  14 +@interface YBImageBrowser ()
  15 +
  16 +@property (nonatomic, strong) YBIBContainerView *containerView;
  17 +
  18 +- (void)implementGetBaseInfoProtocol:(id<YBIBGetBaseInfoProtocol>)obj;
  19 +
  20 +@property (nonatomic, weak, nullable) NSObject *hiddenProjectiveView;
  21 +
  22 +@end
  23 +
  24 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Helper/YBIBCopywriter.h 0 → 100644
  1 +//
  2 +// YBIBCopywriter.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2018/9/13.
  6 +// Copyright © 2018年 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import <Foundation/Foundation.h>
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +typedef NS_ENUM(NSInteger, YBIBCopywriterType) {
  14 + /// 简体中文
  15 + YBIBCopywriterTypeSimplifiedChinese,
  16 + /// 英文
  17 + YBIBCopywriterTypeEnglish
  18 +};
  19 +
  20 +/**
  21 + 文案管理类
  22 + */
  23 +@interface YBIBCopywriter : NSObject
  24 +
  25 +/**
  26 + 唯一有效单例
  27 + */
  28 ++ (instancetype)sharedCopywriter;
  29 +
  30 +/// 语言类型
  31 +@property (nonatomic, assign) YBIBCopywriterType type;
  32 +
  33 +#pragma - 以下文案可更改
  34 +
  35 +@property (nonatomic, copy) NSString *videoIsInvalid;
  36 +
  37 +@property (nonatomic, copy) NSString *videoError;
  38 +
  39 +@property (nonatomic, copy) NSString *unableToSave;
  40 +
  41 +@property (nonatomic, copy) NSString *imageIsInvalid;
  42 +
  43 +@property (nonatomic, copy) NSString *downloadFailed;
  44 +
  45 +@property (nonatomic, copy) NSString *getPhotoAlbumAuthorizationFailed;
  46 +
  47 +@property (nonatomic, copy) NSString *saveToPhotoAlbumSuccess;
  48 +
  49 +@property (nonatomic, copy) NSString *saveToPhotoAlbumFailed;
  50 +
  51 +@property (nonatomic, copy) NSString *saveToPhotoAlbum;
  52 +
  53 +@property (nonatomic, copy) NSString *cancel;
  54 +
  55 +@end
  56 +
  57 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Helper/YBIBCopywriter.m 0 → 100644
  1 +//
  2 +// YBIBCopywriter.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2018/9/13.
  6 +// Copyright © 2018年 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBCopywriter.h"
  10 +
  11 +@implementation YBIBCopywriter
  12 +
  13 +#pragma mark - life cycle
  14 +
  15 ++ (instancetype)sharedCopywriter {
  16 + static YBIBCopywriter *copywriter = nil;
  17 + static dispatch_once_t onceToken;
  18 + dispatch_once(&onceToken, ^{
  19 + copywriter = [YBIBCopywriter new];
  20 + });
  21 + return copywriter;
  22 +}
  23 +
  24 +- (instancetype)init {
  25 + self = [super init];
  26 + if (self) {
  27 + _type = YBIBCopywriterTypeSimplifiedChinese;
  28 + NSArray *appleLanguages = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"];
  29 + if (appleLanguages && appleLanguages.count > 0) {
  30 + NSString *languages = appleLanguages[0];
  31 + if (![languages hasPrefix:@"zh-Hans"]) {
  32 + _type = YBIBCopywriterTypeEnglish;
  33 + }
  34 + }
  35 +
  36 + [self initCopy];
  37 + }
  38 + return self;
  39 +}
  40 +
  41 +#pragma mark - private
  42 +
  43 +- (void)initCopy {
  44 + BOOL en = self.type == YBIBCopywriterTypeEnglish;
  45 +
  46 + self.videoIsInvalid = en ? @"Video is invalid" : @"视频无效";
  47 + self.videoError = en ? @"Video error" : @"视频错误";
  48 + self.unableToSave = en ? @"Unable to save" : @"无法保存";
  49 + self.imageIsInvalid = en ? @"Image is invalid" : @"图片无效";
  50 + self.downloadFailed = en ? @"Download failed" : @"加载图片失败";
  51 + self.getPhotoAlbumAuthorizationFailed = en ? @"Failed to get album authorization" : @"获取相册权限失败";
  52 + self.saveToPhotoAlbumSuccess = en ? @"Save successful" : @"已保存到系统相册";
  53 + self.saveToPhotoAlbumFailed = en ? @"Save failed" : @"保存失败";
  54 + self.saveToPhotoAlbum = en ? @"Save" : @"保存到相册";
  55 + self.cancel = en ? @"Cancel" : @"取消";
  56 +}
  57 +
  58 +#pragma mark - public
  59 +
  60 +- (void)setType:(YBIBCopywriterType)type {
  61 + _type = type;
  62 + [self initCopy];
  63 +}
  64 +
  65 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Helper/YBIBIconManager.h 0 → 100644
  1 +//
  2 +// YBIBIconManager.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2018/8/29.
  6 +// Copyright © 2018年 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@interface UIImage (YBImageBrowser)
  14 +
  15 +/**
  16 + 获取图片便利构造方法
  17 +
  18 + @param name 图片名字
  19 + @param bundle 资源对象
  20 + @return 图片实例
  21 + */
  22 ++ (instancetype)ybib_imageNamed:(NSString *)name bundle:(NSBundle *)bundle;
  23 +
  24 +@end
  25 +
  26 +
  27 +/// 获取图片闭包
  28 +typedef UIImage * _Nullable (^YBIBIconBlock)(void);
  29 +
  30 +/**
  31 + 图标管理类
  32 + */
  33 +@interface YBIBIconManager : NSObject
  34 +
  35 +/**
  36 + 唯一有效单例
  37 + */
  38 ++ (instancetype)sharedManager;
  39 +
  40 +#pragma - 以下图片可更改
  41 +
  42 +/// 基本-加载
  43 +@property (nonatomic, copy) YBIBIconBlock loadingImage;
  44 +
  45 +/// 工具视图-保存
  46 +@property (nonatomic, copy) YBIBIconBlock toolSaveImage;
  47 +/// 工具视图-更多
  48 +@property (nonatomic, copy) YBIBIconBlock toolMoreImage;
  49 +
  50 +/// 视频-播放
  51 +@property (nonatomic, copy) YBIBIconBlock videoPlayImage;
  52 +/// 视频-暂停
  53 +@property (nonatomic, copy) YBIBIconBlock videoPauseImage;
  54 +/// 视频-取消
  55 +@property (nonatomic, copy) YBIBIconBlock videoCancelImage;
  56 +/// 视频-播放大图
  57 +@property (nonatomic, copy) YBIBIconBlock videoBigPlayImage;
  58 +/// 视频-拖动圆点
  59 +@property (nonatomic, copy) YBIBIconBlock videoDragCircleImage;
  60 +
  61 +@end
  62 +
  63 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Helper/YBIBIconManager.m 0 → 100644
  1 +//
  2 +// YBIBIconManager.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2018/8/29.
  6 +// Copyright © 2018年 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBIconManager.h"
  10 +
  11 +// The best order for path scale search.
  12 +static NSArray *_NSBundlePreferredScales() {
  13 + static NSArray *scales;
  14 + static dispatch_once_t onceToken;
  15 + dispatch_once(&onceToken, ^{
  16 + CGFloat screenScale = [UIScreen mainScreen].scale;
  17 + if (screenScale <= 1) {
  18 + scales = @[@1,@2,@3];
  19 + } else if (screenScale <= 2) {
  20 + scales = @[@2,@3,@1];
  21 + } else {
  22 + scales = @[@3,@2,@1];
  23 + }
  24 + });
  25 + return scales;
  26 +}
  27 +
  28 +// Add scale modifier to the file name (without path extension), from @"name" to @"name@2x".
  29 +static NSString *_NSStringByAppendingNameScale(NSString *string, CGFloat scale) {
  30 + if (!string) return nil;
  31 + if (fabs(scale - 1) <= __FLT_EPSILON__ || string.length == 0 || [string hasSuffix:@"/"]) return string.copy;
  32 + return [string stringByAppendingFormat:@"@%@x", @(scale)];
  33 +}
  34 +
  35 +@implementation UIImage (YBImageBrowser)
  36 +
  37 ++ (instancetype)ybib_imageNamed:(NSString *)name bundle:(NSBundle *)bundle {
  38 + if (name.length == 0) return nil;
  39 + if ([name hasSuffix:@"/"]) return nil;
  40 +
  41 + NSString *res = name.stringByDeletingPathExtension;
  42 + NSString *ext = name.pathExtension;
  43 + NSString *path = nil;
  44 + CGFloat scale = 1;
  45 +
  46 + // If no extension, guess by system supported (same as UIImage).
  47 + NSArray *exts = ext.length > 0 ? @[ext] : @[@"", @"png", @"jpeg", @"jpg", @"gif", @"webp", @"apng"];
  48 + NSArray *scales = _NSBundlePreferredScales();
  49 + for (int s = 0; s < scales.count; s++) {
  50 + scale = ((NSNumber *)scales[s]).floatValue;
  51 + NSString *scaledName = _NSStringByAppendingNameScale(res, scale);
  52 + for (NSString *e in exts) {
  53 + path = [bundle pathForResource:scaledName ofType:e];
  54 + if (path) break;
  55 + }
  56 + if (path) break;
  57 + }
  58 + if (path.length == 0) {
  59 + // Assets.xcassets supported.
  60 + return [self imageNamed:name];
  61 + }
  62 +
  63 + NSData *data = [NSData dataWithContentsOfFile:path];
  64 + if (data.length == 0) return nil;
  65 +
  66 + return [[self alloc] initWithData:data scale:scale];
  67 +}
  68 +
  69 +@end
  70 +
  71 +
  72 +NSBundle *YBIBDefaultBundle(void) {
  73 + static NSBundle *bundle = nil;
  74 + static dispatch_once_t onceToken;
  75 + dispatch_once(&onceToken, ^{
  76 + NSBundle *_bundle = [NSBundle bundleForClass:NSClassFromString(@"YBImageBrowser")];
  77 + NSString *path = [_bundle pathForResource:@"YBImageBrowser" ofType:@"bundle"];
  78 + bundle = [NSBundle bundleWithPath:path];
  79 + });
  80 + return bundle;
  81 +}
  82 +
  83 +NSBundle *YBIBVideoBundle(void) {
  84 + static NSBundle *bundle = nil;
  85 + static dispatch_once_t onceToken;
  86 + dispatch_once(&onceToken, ^{
  87 + NSBundle *_bundle = [NSBundle bundleForClass:NSClassFromString(@"YBImageBrowser")];
  88 + NSString *path = [_bundle pathForResource:@"YBImageBrowserVideo" ofType:@"bundle"];
  89 + bundle = [NSBundle bundleWithPath:path];
  90 + });
  91 + return bundle;
  92 +}
  93 +
  94 +@implementation YBIBIconManager
  95 +
  96 ++ (instancetype)sharedManager {
  97 + static YBIBIconManager *manager = nil;
  98 + static dispatch_once_t onceToken;
  99 + dispatch_once(&onceToken, ^{
  100 + manager = [YBIBIconManager new];
  101 + });
  102 + return manager;
  103 +}
  104 +
  105 +- (instancetype)init {
  106 + self = [super init];
  107 + if (self) {
  108 + _loadingImage = ^UIImage * _Nullable{
  109 + return [UIImage ybib_imageNamed:@"ybib_loading" bundle:YBIBDefaultBundle()];
  110 + };
  111 +
  112 + _toolSaveImage = ^UIImage * _Nullable{
  113 + return [UIImage ybib_imageNamed:@"ybib_save" bundle:YBIBDefaultBundle()];
  114 + };
  115 + _toolMoreImage = ^UIImage * _Nullable{
  116 + return [UIImage ybib_imageNamed:@"ybib_more" bundle:YBIBDefaultBundle()];
  117 + };
  118 +
  119 + _videoPlayImage = ^UIImage * _Nullable{
  120 + return [UIImage ybib_imageNamed:@"ybib_play" bundle:YBIBVideoBundle()];
  121 + };
  122 + _videoPauseImage = ^UIImage * _Nullable{
  123 + return [UIImage ybib_imageNamed:@"ybib_pause" bundle:YBIBVideoBundle()];
  124 + };
  125 + _videoCancelImage = ^UIImage * _Nullable{
  126 + return [UIImage ybib_imageNamed:@"ybib_cancel" bundle:YBIBVideoBundle()];
  127 + };
  128 + _videoBigPlayImage = ^UIImage * _Nullable{
  129 + return [UIImage ybib_imageNamed:@"ybib_bigPlay" bundle:YBIBVideoBundle()];
  130 + };
  131 + _videoDragCircleImage = ^UIImage * _Nullable{
  132 + return [UIImage ybib_imageNamed:@"ybib_circlePoint" bundle:YBIBVideoBundle()];
  133 + };
  134 + }
  135 + return self;
  136 +}
  137 +
  138 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Helper/YBIBPhotoAlbumManager.h 0 → 100644
  1 +//
  2 +// YBIBPhotoAlbumManager.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2018/8/28.
  6 +// Copyright © 2018年 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import <Photos/Photos.h>
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@interface YBIBPhotoAlbumManager : NSObject
  14 +
  15 +/**
  16 + Get photo album authorization.
  17 + */
  18 ++ (void)getPhotoAlbumAuthorizationSuccess:(void(^)(void))success failed:(void(^)(void))failed;
  19 +
  20 +/**
  21 + Get 'AVAsset' by 'PHAsset' asynchronously, callback is in child thread.
  22 + */
  23 ++ (void)getAVAssetWithPHAsset:(PHAsset *)phAsset completion:(void(^)(AVAsset * _Nullable asset))completion;
  24 +
  25 +/**
  26 + Get 'ImageData' by 'PHAsset' synchronously, callback is in child thread.
  27 + */
  28 ++ (void)getImageDataWithPHAsset:(PHAsset *)phAsset completion:(void(^)(NSData * _Nullable data))completion;
  29 +
  30 +@end
  31 +
  32 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Helper/YBIBPhotoAlbumManager.m 0 → 100644
  1 +//
  2 +// YBIBPhotoAlbumManager.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2018/8/28.
  6 +// Copyright © 2018年 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBPhotoAlbumManager.h"
  10 +#import "YBIBUtilities.h"
  11 +
  12 +@implementation YBIBPhotoAlbumManager
  13 +
  14 ++ (void)getAVAssetWithPHAsset:(PHAsset *)phAsset completion:(nonnull void (^)(AVAsset * _Nullable))completion {
  15 + PHVideoRequestOptions *options = [PHVideoRequestOptions new];
  16 + options.networkAccessAllowed = YES;
  17 + [[PHImageManager defaultManager] requestAVAssetForVideo:phAsset options:options resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
  18 + completion(asset);
  19 + }];
  20 +}
  21 +
  22 ++ (void)getImageDataWithPHAsset:(PHAsset *)phAsset completion:(nonnull void (^)(NSData * _Nullable))completion {
  23 + PHImageRequestOptions *options = [PHImageRequestOptions new];
  24 + options.networkAccessAllowed = YES;
  25 + options.resizeMode = PHImageRequestOptionsResizeModeNone;
  26 + // Only when this property is YES, the callback will in child thread.
  27 + options.synchronous = YES;
  28 + [[PHImageManager defaultManager] requestImageDataForAsset:phAsset options:options resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
  29 + BOOL complete = ![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey] && ![[info objectForKey:PHImageResultIsDegradedKey] boolValue];
  30 + if (complete && imageData) {
  31 + completion(imageData);
  32 + } else {
  33 + completion(nil);
  34 + }
  35 + }];
  36 +}
  37 +
  38 ++ (void)getPhotoAlbumAuthorizationSuccess:(void(^)(void))success failed:(void(^)(void))failed {
  39 + PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
  40 + switch (status) {
  41 + case PHAuthorizationStatusDenied:
  42 + if (failed) failed();
  43 + break;
  44 + case PHAuthorizationStatusRestricted:
  45 + if (failed) failed();
  46 + break;
  47 + case PHAuthorizationStatusNotDetermined: {
  48 + [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status){
  49 + YBIB_DISPATCH_ASYNC_MAIN(^{
  50 + if (status == PHAuthorizationStatusAuthorized) {
  51 + if (success) success();
  52 + } else {
  53 + if (failed) failed();
  54 + }
  55 + })
  56 + }];
  57 + }
  58 + break;
  59 + case PHAuthorizationStatusAuthorized:
  60 + if (success) success();
  61 + break;
  62 + }
  63 +}
  64 +
  65 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Helper/YBIBSentinel.h 0 → 100644
  1 +//
  2 +// YBIBSentinel.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/18.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import <Foundation/Foundation.h>
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +/**
  14 + Thread safe.
  15 + */
  16 +@interface YBIBSentinel : NSObject
  17 +
  18 +/// Returns the current value of the counter.
  19 +@property (readonly) int32_t value;
  20 +
  21 +/**
  22 + Increase the value atomically.
  23 + @return The new value.
  24 + */
  25 +- (int32_t)increase;
  26 +
  27 +@end
  28 +
  29 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Helper/YBIBSentinel.m 0 → 100644
  1 +//
  2 +// YBIBSentinel.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/18.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBSentinel.h"
  10 +#import <libkern/OSAtomic.h>
  11 +
  12 +@implementation YBIBSentinel {
  13 + int32_t _value;
  14 +}
  15 +
  16 +- (int32_t)value {
  17 + return _value;
  18 +}
  19 +
  20 +- (int32_t)increase {
  21 + return OSAtomicIncrement32(&_value);
  22 +}
  23 +
  24 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Helper/YBIBUtilities.h 0 → 100644
  1 +//
  2 +// YBIBUtilities.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 杨少 on 2018/4/11.
  6 +// Copyright © 2018年 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +
  14 +#define YBIB_DISPATCH_ASYNC(queue, block)\
  15 +if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(queue)) == 0) {\
  16 +block();\
  17 +} else {\
  18 +dispatch_async(queue, block);\
  19 +}
  20 +
  21 +#define YBIB_DISPATCH_ASYNC_MAIN(block) YBIB_DISPATCH_ASYNC(dispatch_get_main_queue(), block)
  22 +
  23 +
  24 +#define YBIB_CODE_EXEC_TIME(KEY, ...) \
  25 +CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent(); \
  26 +__VA_ARGS__ \
  27 +CFAbsoluteTime linkTime = (CFAbsoluteTimeGetCurrent() - startTime); \
  28 +NSLog(@"%@ Time-Consuming: %fms", KEY, linkTime * 1000.0);
  29 +
  30 +
  31 +UIWindow * _Nonnull YBIBNormalWindow(void);
  32 +
  33 +UIViewController * _Nullable YBIBTopController(void);
  34 +UIViewController * _Nullable YBIBTopControllerByWindow(UIWindow *);
  35 +
  36 +BOOL YBIBLowMemory(void);
  37 +
  38 +BOOL YBIBIsIphoneXSeries(void);
  39 +CGFloat YBIBStatusbarHeight(void);
  40 +CGFloat YBIBSafeAreaBottomHeight(void);
  41 +
  42 +UIImage *YBIBSnapshotView(UIView *);
  43 +
  44 +/// This is orientation of 'YBImageBrowser' not 'UIDevice'.
  45 +UIEdgeInsets YBIBPaddingByBrowserOrientation(UIDeviceOrientation);
  46 +
  47 +
  48 +@interface YBIBUtilities : NSObject
  49 +
  50 +@end
  51 +
  52 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Helper/YBIBUtilities.m 0 → 100644
  1 +//
  2 +// YBIBUtilities.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 杨少 on 2018/4/11.
  6 +// Copyright © 2018年 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBUtilities.h"
  10 +#import <sys/utsname.h>
  11 +
  12 +
  13 +UIWindow * _Nullable YBIBNormalWindow(void) {
  14 + UIWindow *window = [[UIApplication sharedApplication] keyWindow];
  15 + if (window.windowLevel != UIWindowLevelNormal) {
  16 + NSArray *windows = [[UIApplication sharedApplication] windows];
  17 + for(UIWindow *temp in windows) {
  18 + if (temp.windowLevel == UIWindowLevelNormal) {
  19 + return temp;
  20 + }
  21 + }
  22 + }
  23 + return window;
  24 +}
  25 +
  26 +UIViewController * _Nullable YBIBTopController(void) {
  27 + return YBIBTopControllerByWindow(YBIBNormalWindow());
  28 +}
  29 +
  30 +UIViewController * _Nullable YBIBTopControllerByWindow(UIWindow *window) {
  31 + if (!window) return nil;
  32 +
  33 + UIViewController *top = nil;
  34 + id nextResponder;
  35 + if (window.subviews.count > 0) {
  36 + UIView *frontView = [window.subviews objectAtIndex:0];
  37 + nextResponder = frontView.nextResponder;
  38 + }
  39 + if (nextResponder && [nextResponder isKindOfClass:UIViewController.class]) {
  40 + top = nextResponder;
  41 + } else {
  42 + top = window.rootViewController;
  43 + }
  44 +
  45 + while ([top isKindOfClass:UITabBarController.class] || [top isKindOfClass:UINavigationController.class] || top.presentedViewController) {
  46 + if ([top isKindOfClass:UITabBarController.class]) {
  47 + top = ((UITabBarController *)top).selectedViewController;
  48 + } else if ([top isKindOfClass:UINavigationController.class]) {
  49 + top = ((UINavigationController *)top).topViewController;
  50 + } else if (top.presentedViewController) {
  51 + top = top.presentedViewController;
  52 + }
  53 + }
  54 + return top;
  55 +}
  56 +
  57 +BOOL YBIBLowMemory(void) {
  58 + static BOOL lowMemory = NO;
  59 + static dispatch_once_t onceToken;
  60 + dispatch_once(&onceToken, ^{
  61 + unsigned long long physicalMemory = [[NSProcessInfo processInfo] physicalMemory];
  62 + lowMemory = physicalMemory > 0 && physicalMemory < 1024 * 1024 * 1500;
  63 + });
  64 + return lowMemory;
  65 +}
  66 +
  67 +BOOL YBIBIsIphoneXSeries(void) {
  68 + return YBIBStatusbarHeight() > 20;
  69 +}
  70 +
  71 +CGFloat YBIBStatusbarHeight(void) {
  72 + CGFloat height = 0;
  73 + if (@available(iOS 11.0, *)) {
  74 + height = UIApplication.sharedApplication.delegate.window.safeAreaInsets.top;
  75 + }
  76 + if (height <= 0) {
  77 + height = UIApplication.sharedApplication.statusBarFrame.size.height;
  78 + }
  79 + if (height <= 0) {
  80 + height = 20;
  81 + }
  82 + return height;
  83 +}
  84 +
  85 +CGFloat YBIBSafeAreaBottomHeight(void) {
  86 + CGFloat bottom = 0;
  87 + if (@available(iOS 11.0, *)) {
  88 + bottom = UIApplication.sharedApplication.delegate.window.safeAreaInsets.bottom;
  89 + }
  90 + return bottom;
  91 +}
  92 +
  93 +UIImage *YBIBSnapshotView(UIView *view) {
  94 + UIGraphicsBeginImageContextWithOptions(view.bounds.size, YES, [UIScreen mainScreen].scale);
  95 + [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:NO];
  96 + UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  97 + UIGraphicsEndImageContext();
  98 + return image;
  99 +}
  100 +
  101 +UIEdgeInsets YBIBPaddingByBrowserOrientation(UIDeviceOrientation orientation) {
  102 + UIEdgeInsets padding = UIEdgeInsetsZero;
  103 + if (!YBIBIsIphoneXSeries()) return padding;
  104 +
  105 + UIDeviceOrientation barOrientation = (UIDeviceOrientation)UIApplication.sharedApplication.statusBarOrientation;
  106 +
  107 + if (UIDeviceOrientationIsLandscape(orientation)) {
  108 + BOOL same = orientation == barOrientation;
  109 + BOOL reverse = !same && UIDeviceOrientationIsLandscape(barOrientation);
  110 + if (same) {
  111 + padding.bottom = YBIBSafeAreaBottomHeight();
  112 + padding.top = 0;
  113 + } else if (reverse) {
  114 + padding.top = YBIBSafeAreaBottomHeight();
  115 + padding.bottom = 0;
  116 + }
  117 + padding.left = padding.right = MAX(YBIBSafeAreaBottomHeight(), YBIBStatusbarHeight());
  118 + } else {
  119 + if (orientation == UIDeviceOrientationPortrait) {
  120 + padding.top = YBIBStatusbarHeight();
  121 + padding.bottom = barOrientation == UIDeviceOrientationPortrait ? YBIBSafeAreaBottomHeight() : 0;
  122 + } else {
  123 + padding.bottom = YBIBStatusbarHeight();
  124 + padding.top = barOrientation == UIDeviceOrientationPortrait ? YBIBSafeAreaBottomHeight() : 0;
  125 + }
  126 + padding.left = padding.right = UIDeviceOrientationIsLandscape(barOrientation) ? YBIBSafeAreaBottomHeight() : 0 ;
  127 + }
  128 + return padding;
  129 +}
  130 +
  131 +
  132 +@implementation YBIBUtilities
  133 +
  134 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Image/YBIBImageCache+Internal.h 0 → 100644
  1 +//
  2 +// YBIBImageCache+Internal.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/13.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBImageCache.h"
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +typedef NS_ENUM(NSInteger, YBIBImageCacheType) {
  14 + YBIBImageCacheTypeOrigin,
  15 + YBIBImageCacheTypeCompressed
  16 +};
  17 +
  18 +/**
  19 + Not thread safe.
  20 + */
  21 +@interface YBIBImageCache ()
  22 +
  23 +- (void)setImage:(UIImage *)image type:(YBIBImageCacheType)type forKey:(NSString *)key resident:(BOOL)resident;
  24 +
  25 +- (nullable UIImage *)imageForKey:(NSString *)key type:(YBIBImageCacheType)type;
  26 +
  27 +- (void)removeForKey:(NSString *)key;
  28 +
  29 +- (void)removeResidentForKey:(NSString *)key;
  30 +
  31 +@end
  32 +
  33 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Image/YBIBImageCache.h 0 → 100644
  1 +//
  2 +// YBIBImageCache.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/13.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@class YBIBImageCache;
  14 +
  15 +@interface NSObject (YBIBImageCache)
  16 +
  17 +/// 图片浏览器持有的图片缓存管理类
  18 +@property (nonatomic, strong, readonly) YBIBImageCache *ybib_imageCache;
  19 +
  20 +@end
  21 +
  22 +
  23 +@interface YBIBImageCache : NSObject
  24 +
  25 +/// 缓存数量限制(一个单位表示一个 YBIBImageData 产生的所有图片数据)
  26 +@property (nonatomic, assign) NSUInteger imageCacheCountLimit;
  27 +
  28 +@end
  29 +
  30 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Image/YBIBImageCache.m 0 → 100644
  1 +//
  2 +// YBIBImageCache.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/13.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBImageCache.h"
  10 +#import "YBIBImageCache+Internal.h"
  11 +#import "YBIBUtilities.h"
  12 +#import <objc/runtime.h>
  13 +
  14 +
  15 +@implementation NSObject (YBIBImageCache)
  16 +static void *YBIBImageCacheKey = &YBIBImageCacheKey;
  17 +- (void)setYbib_imageCache:(YBIBImageCache *)ybib_imageCache {
  18 + objc_setAssociatedObject(self, YBIBImageCacheKey, ybib_imageCache, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  19 +}
  20 +- (YBIBImageCache *)ybib_imageCache {
  21 + YBIBImageCache *cache = objc_getAssociatedObject(self, YBIBImageCacheKey);
  22 + if (!cache) {
  23 + cache = [YBIBImageCache new];
  24 + self.ybib_imageCache = cache;
  25 + }
  26 + return cache;
  27 +}
  28 +@end
  29 +
  30 +
  31 +@interface YBIBImageCachePack : NSObject
  32 +@property (nonatomic, strong) UIImage *originImage;
  33 +@property (nonatomic, strong) UIImage *compressedImage;
  34 +@end
  35 +@implementation YBIBImageCachePack
  36 +@end
  37 +
  38 +
  39 +@implementation YBIBImageCache {
  40 + NSCache<NSString *, YBIBImageCachePack *> *_imageCache;
  41 + NSMutableDictionary<NSString *, YBIBImageCachePack *> *_residentCache;
  42 +}
  43 +
  44 +#pragma mark - life cycle
  45 +
  46 +- (void)dealloc {
  47 + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
  48 +}
  49 +
  50 +- (instancetype)init {
  51 + self = [super init];
  52 + if (self) {
  53 + _imageCache = [NSCache new];
  54 + _imageCache.countLimit = _imageCacheCountLimit = YBIBLowMemory() ? 6 : 12;
  55 + _residentCache = [NSMutableDictionary dictionary];
  56 + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
  57 + }
  58 + return self;
  59 +}
  60 +
  61 +#pragma mark - event
  62 +
  63 +- (void)didReceiveMemoryWarning:(NSNotification *)notification {
  64 + [_imageCache removeAllObjects];
  65 + [_residentCache removeAllObjects];
  66 +}
  67 +
  68 +#pragma mark - public
  69 +
  70 +- (void)setImage:(UIImage *)image type:(YBIBImageCacheType)type forKey:(NSString *)key resident:(BOOL)resident {
  71 + YBIBImageCachePack *pack = [_imageCache objectForKey:key];
  72 + if (!pack) {
  73 + pack = [YBIBImageCachePack new];
  74 + [_imageCache setObject:pack forKey:key];
  75 + }
  76 + switch (type) {
  77 + case YBIBImageCacheTypeOrigin:
  78 + pack.originImage = image;
  79 + break;
  80 + case YBIBImageCacheTypeCompressed:
  81 + pack.compressedImage = image;
  82 + break;
  83 + }
  84 + [_residentCache setObject:pack forKey:key];
  85 +}
  86 +
  87 +- (UIImage *)imageForKey:(NSString *)key type:(YBIBImageCacheType)type {
  88 + YBIBImageCachePack *pack = [_imageCache objectForKey:key] ?: [_residentCache objectForKey:key];
  89 + switch (type) {
  90 + case YBIBImageCacheTypeOrigin: return pack.originImage;
  91 + case YBIBImageCacheTypeCompressed: return pack.compressedImage;
  92 + default: return nil;
  93 + }
  94 +}
  95 +
  96 +- (void)removeForKey:(NSString *)key {
  97 + [_imageCache removeObjectForKey:key];
  98 + [_residentCache removeObjectForKey:key];
  99 +}
  100 +
  101 +- (void)removeResidentForKey:(NSString *)key {
  102 + [_residentCache removeObjectForKey:key];
  103 +}
  104 +
  105 +#pragma mark - setter
  106 +
  107 +- (void)setImageCacheCountLimit:(NSUInteger)imageCacheCountLimit {
  108 + _imageCacheCountLimit = imageCacheCountLimit;
  109 + _imageCache.countLimit = imageCacheCountLimit;
  110 +}
  111 +
  112 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Image/YBIBImageCell+Internal.h 0 → 100644
  1 +//
  2 +// YBIBImageCell+Internal.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/12/23.
  6 +// Copyright © 2019 杨波. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBImageCell.h"
  10 +#import "YBIBImageScrollView.h"
  11 +
  12 +NS_ASSUME_NONNULL_BEGIN
  13 +
  14 +@interface YBIBImageCell ()
  15 +
  16 +@property (nonatomic, strong) YBIBImageScrollView *imageScrollView;
  17 +
  18 +@property (nonatomic, strong) UIImageView *tailoringImageView;
  19 +
  20 +@end
  21 +
  22 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Image/YBIBImageCell.h 0 → 100644
  1 +//
  2 +// YBIBImageCell.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/5.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBCellProtocol.h"
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@interface YBIBImageCell : UICollectionViewCell <YBIBCellProtocol>
  14 +
  15 +@end
  16 +
  17 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Image/YBIBImageCell.m 0 → 100644
  1 +//
  2 +// YBIBImageCell.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/5.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBImageCell.h"
  10 +#import "YBIBImageData.h"
  11 +#import "YBIBIconManager.h"
  12 +#import "YBIBImageCell+Internal.h"
  13 +#import "YBIBImageData+Internal.h"
  14 +#import "YBIBCopywriter.h"
  15 +#import "YBIBUtilities.h"
  16 +
  17 +@interface YBIBImageCell () <YBIBImageDataDelegate, UIScrollViewDelegate, UIGestureRecognizerDelegate>
  18 +@end
  19 +
  20 +@implementation YBIBImageCell {
  21 + CGPoint _interactStartPoint;
  22 + BOOL _interacting;
  23 +}
  24 +
  25 +#pragma mark - life cycle
  26 +
  27 +- (instancetype)initWithFrame:(CGRect)frame {
  28 + self = [super initWithFrame:frame];
  29 + if (self) {
  30 + [self initValue];
  31 + [self.contentView addSubview:self.imageScrollView];
  32 + [self addGesture];
  33 + }
  34 + return self;
  35 +}
  36 +
  37 +- (void)layoutSubviews {
  38 + [super layoutSubviews];
  39 + self.imageScrollView.frame = self.bounds;
  40 +}
  41 +
  42 +- (void)initValue {
  43 + _interactStartPoint = CGPointZero;
  44 + _interacting = NO;
  45 +}
  46 +
  47 +- (void)prepareForReuse {
  48 + ((YBIBImageData *)self.yb_cellData).delegate = nil;
  49 + [self.imageScrollView reset];
  50 + [self hideTailoringImageView];
  51 + [self hideAuxiliaryView];
  52 + [super prepareForReuse];
  53 +}
  54 +
  55 +#pragma mark - <YBIBCellProtocol>
  56 +
  57 +@synthesize yb_currentOrientation = _yb_currentOrientation;
  58 +@synthesize yb_containerSize = _yb_containerSize;
  59 +@synthesize yb_backView = _yb_backView;
  60 +@synthesize yb_collectionView = _yb_collectionView;
  61 +@synthesize yb_isTransitioning = _yb_isTransitioning;
  62 +@synthesize yb_isRotating = _yb_isRotating;
  63 +@synthesize yb_auxiliaryViewHandler = _yb_auxiliaryViewHandler;
  64 +@synthesize yb_hideStatusBar = _yb_hideStatusBar;
  65 +@synthesize yb_hideBrowser = _yb_hideBrowser;
  66 +@synthesize yb_hideToolViews = _yb_hideToolViews;
  67 +@synthesize yb_cellData = _yb_cellData;
  68 +@synthesize yb_cellIsInCenter = _yb_cellIsInCenter;
  69 +@synthesize yb_selfPage = _yb_selfPage;
  70 +@synthesize yb_currentPage = _yb_currentPage;
  71 +
  72 +- (void)setYb_cellData:(id<YBIBDataProtocol>)yb_cellData {
  73 + _yb_cellData = yb_cellData;
  74 + ((YBIBImageData *)yb_cellData).delegate = self;
  75 +}
  76 +
  77 +- (UIView *)yb_foregroundView {
  78 + return self.imageScrollView.imageView;
  79 +}
  80 +
  81 +- (void)yb_orientationWillChangeWithExpectOrientation:(UIDeviceOrientation)orientation {
  82 + [self hideTailoringImageView];
  83 +}
  84 +
  85 +- (void)yb_orientationChangeAnimationWithExpectOrientation:(UIDeviceOrientation)orientation {
  86 + [self updateImageLayoutWithOrientation:orientation previousImageSize:self.imageScrollView.imageView.image.size];
  87 +}
  88 +
  89 +#pragma mark - private
  90 +
  91 +- (CGSize)contentSizeWithContainerSize:(CGSize)containerSize imageViewFrame:(CGRect)imageViewFrame {
  92 + return CGSizeMake(MAX(containerSize.width, imageViewFrame.size.width), MAX(containerSize.height, imageViewFrame.size.height));
  93 +}
  94 +
  95 +- (void)updateImageLayoutWithOrientation:(UIDeviceOrientation)orientation previousImageSize:(CGSize)previousImageSize {
  96 + if (_interacting) [self restoreInteractionWithDuration:0];
  97 +
  98 + YBIBImageData *data = self.yb_cellData;
  99 +
  100 + CGSize imageSize;
  101 +
  102 + UIImage *image = self.imageScrollView.imageView.image;
  103 + YBIBScrollImageType imageType = self.imageScrollView.imageType;
  104 + if (imageType == YBIBScrollImageTypeCompressed) {
  105 + imageSize = data.originImage ? data.originImage.size : image.size;
  106 + } else {
  107 + imageSize = image.size;
  108 + }
  109 +
  110 + CGSize containerSize = self.yb_containerSize(orientation);
  111 + CGRect imageViewFrame = [data.layout yb_imageViewFrameWithContainerSize:containerSize imageSize:imageSize orientation:orientation];
  112 + CGSize contentSize = [self contentSizeWithContainerSize:containerSize imageViewFrame:imageViewFrame];
  113 + CGFloat maxZoomScale = imageType == YBIBScrollImageTypeThumb ? 1 : [data.layout yb_maximumZoomScaleWithContainerSize:containerSize imageSize:imageSize orientation:orientation];
  114 +
  115 + // 'zoomScale' must set before 'contentSize' and 'imageView.frame'.
  116 + self.imageScrollView.zoomScale = 1;
  117 + self.imageScrollView.contentSize = contentSize;
  118 + self.imageScrollView.minimumZoomScale = 1;
  119 + self.imageScrollView.maximumZoomScale = maxZoomScale;
  120 +
  121 + CGFloat scale;
  122 + if (previousImageSize.width > 0 && previousImageSize.height > 0) {
  123 + scale = imageSize.width / imageSize.height - previousImageSize.width / previousImageSize.height;
  124 + } else {
  125 + scale = 0;
  126 + }
  127 + // '0.001' is admissible error.
  128 + if (ABS(scale) <= 0.001) {
  129 + self.imageScrollView.imageView.frame = imageViewFrame;
  130 + } else {
  131 + [UIView animateWithDuration:0.25 animations:^{
  132 + self.imageScrollView.imageView.frame = imageViewFrame;
  133 + }];
  134 + }
  135 +}
  136 +
  137 +- (void)cuttingImage {
  138 + // This method has been delayed called, so 'browser' may be in transit now.
  139 + if (self.yb_isTransitioning()) return;
  140 + if (_interacting) return;
  141 +
  142 + YBIBImageData *data = self.yb_cellData;
  143 + if (!data.originImage) return;
  144 +
  145 + if (self.imageScrollView.zoomScale < data.cuttingZoomScale) return;
  146 +
  147 + if ([data shouldCompress]) {
  148 + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(cuttingImage_) object:nil];
  149 + [self performSelector:@selector(cuttingImage_) withObject:nil afterDelay:0.15];
  150 + }
  151 +}
  152 +- (void)cuttingImage_ {
  153 + YBIBImageData *data = self.yb_cellData;
  154 + if (!data.originImage) return;
  155 +
  156 + CGFloat scale = data.originImage.size.width / self.imageScrollView.contentSize.width;
  157 + CGFloat x = self.imageScrollView.contentOffset.x * scale,
  158 + y = self.imageScrollView.contentOffset.y * scale,
  159 + width = self.imageScrollView.bounds.size.width * scale,
  160 + height = self.imageScrollView.bounds.size.height * scale;
  161 +
  162 + __weak typeof(self) wSelf = self;
  163 + [data cuttingImageToRect:CGRectMake(x, y, width, height) complete:^(UIImage *image) {
  164 + if (!image) return;
  165 + YBIB_DISPATCH_ASYNC_MAIN(^{
  166 + __strong typeof(wSelf) self = wSelf;
  167 + if (!self) return;
  168 + if (data == self.yb_cellData && !self.imageScrollView.isDragging && !self->_interacting && !self.yb_isTransitioning()) {
  169 + [self showTailoringImageView:image];
  170 + }
  171 + })
  172 + }];
  173 +}
  174 +
  175 +- (void)showTailoringImageView:(UIImage *)image {
  176 + CGSize containerSize = self.yb_containerSize(self.yb_currentOrientation());
  177 + if (!self.tailoringImageView.superview) {
  178 + [self.contentView addSubview:self.tailoringImageView];
  179 + }
  180 + self.tailoringImageView.frame = CGRectMake(0, 0, containerSize.width, containerSize.height);
  181 + self.tailoringImageView.hidden = NO;
  182 + self.tailoringImageView.image = image;
  183 +}
  184 +
  185 +- (void)hideTailoringImageView {
  186 + // Don't use 'getter' method, because it's according to the need to load.
  187 + if (_tailoringImageView) {
  188 + self.tailoringImageView.hidden = YES;
  189 + }
  190 +}
  191 +
  192 +- (void)hideAuxiliaryView {
  193 + [self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
  194 + [self.yb_auxiliaryViewHandler() yb_hideToastWithContainer:self];
  195 +}
  196 +
  197 +- (void)hideBrowser {
  198 + ((YBIBImageData *)self.yb_cellData).delegate = nil;
  199 + [self hideTailoringImageView];
  200 + [self hideAuxiliaryView];
  201 + self.yb_hideBrowser();
  202 + _interacting = NO;
  203 +}
  204 +
  205 +#pragma mark - <YBIBImageDataDelegate>
  206 +
  207 +- (void)yb_imageData:(YBIBImageData *)data startLoadingWithStatus:(YBIBImageLoadingStatus)status {
  208 + switch (status) {
  209 + case YBIBImageLoadingStatusDecoding: {
  210 + if (!self.imageScrollView.imageView.image) {
  211 + [self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self];
  212 + }
  213 + }
  214 + break;
  215 + case YBIBImageLoadingStatusProcessing: {
  216 + if (!self.imageScrollView.imageView.image) {
  217 + [self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self];
  218 + }
  219 + }
  220 + break;
  221 + case YBIBImageLoadingStatusCompressing: {
  222 + if (!self.imageScrollView.imageView.image) {
  223 + [self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self];
  224 + }
  225 + }
  226 + break;
  227 + case YBIBImageLoadingStatusReadingPHAsset: {
  228 + if (!self.imageScrollView.imageView.image) {
  229 + [self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self];
  230 + }
  231 + }
  232 + break;
  233 + case YBIBImageLoadingStatusNone: {
  234 + [self hideAuxiliaryView];
  235 + }
  236 + break;
  237 + default:
  238 + break;
  239 + }
  240 +}
  241 +
  242 +- (void)yb_imageData:(YBIBImageData *)data readyForImage:(__kindof UIImage *)image {
  243 + [self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
  244 + if (self.imageScrollView.imageView.image == image) return;
  245 +
  246 + CGSize size = self.imageScrollView.imageView.image.size;
  247 + [self.imageScrollView setImage:image type:YBIBScrollImageTypeOriginal];
  248 + [self updateImageLayoutWithOrientation:self.yb_currentOrientation() previousImageSize:size];
  249 +}
  250 +
  251 +- (void)yb_imageData:(YBIBImageData *)data readyForCompressedImage:(__kindof UIImage *)image {
  252 + [self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
  253 + if (self.imageScrollView.imageView.image == image) return;
  254 +
  255 + CGSize size = self.imageScrollView.imageView.image.size;
  256 + [self.imageScrollView setImage:image type:YBIBScrollImageTypeCompressed];
  257 + [self updateImageLayoutWithOrientation:self.yb_currentOrientation() previousImageSize:size];
  258 +}
  259 +
  260 +- (void)yb_imageData:(YBIBImageData *)data readyForThumbImage:(__kindof UIImage *)image {
  261 + if (self.imageScrollView.imageView.image) return;
  262 +
  263 + [self.imageScrollView setImage:image type:YBIBScrollImageTypeThumb];
  264 + [self updateImageLayoutWithOrientation:self.yb_currentOrientation() previousImageSize:image.size];
  265 +}
  266 +
  267 +- (void)yb_imageIsInvalidForData:(YBIBImageData *)data {
  268 + [self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
  269 + NSString *imageIsInvalid = [YBIBCopywriter sharedCopywriter].imageIsInvalid;
  270 + if (self.imageScrollView.imageView.image) {
  271 + [self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self text:imageIsInvalid];
  272 + } else {
  273 + [self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self text:imageIsInvalid];
  274 + }
  275 +}
  276 +
  277 +- (void)yb_imageData:(YBIBImageData *)data downloadProgress:(CGFloat)progress {
  278 + [self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self progress:progress];
  279 +}
  280 +
  281 +- (void)yb_imageDownloadFailedForData:(YBIBImageData *)data {
  282 + if (self.imageScrollView.imageView.image) {
  283 + [self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
  284 + [self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self text:[YBIBCopywriter sharedCopywriter].downloadFailed];
  285 + } else {
  286 + [self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self text:[YBIBCopywriter sharedCopywriter].downloadFailed];
  287 + }
  288 +}
  289 +
  290 +#pragma mark - gesture
  291 +
  292 +- (void)addGesture {
  293 + UITapGestureRecognizer *tapSingle = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToTapSingle:)];
  294 + tapSingle.numberOfTapsRequired = 1;
  295 + UITapGestureRecognizer *tapDouble = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToTapDouble:)];
  296 + tapDouble.numberOfTapsRequired = 2;
  297 + UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToPan:)];
  298 + pan.maximumNumberOfTouches = 1;
  299 + pan.delegate = self;
  300 +
  301 + [tapSingle requireGestureRecognizerToFail:tapDouble];
  302 + [tapSingle requireGestureRecognizerToFail:pan];
  303 + [tapDouble requireGestureRecognizerToFail:pan];
  304 +
  305 + [self addGestureRecognizer:tapSingle];
  306 + [self addGestureRecognizer:tapDouble];
  307 + [self addGestureRecognizer:pan];
  308 +}
  309 +
  310 +- (void)respondsToTapSingle:(UITapGestureRecognizer *)tap {
  311 + if (self.yb_isRotating()) return;
  312 +
  313 + YBIBImageData *data = self.yb_cellData;
  314 + if (data.singleTouchBlock) {
  315 + data.singleTouchBlock(data);
  316 + } else {
  317 + [self hideTailoringImageView];
  318 + [self hideAuxiliaryView];
  319 + self.yb_hideBrowser();
  320 + }
  321 +}
  322 +
  323 +- (void)respondsToTapDouble:(UITapGestureRecognizer *)tap {
  324 + if (self.yb_isRotating()) return;
  325 +
  326 + [self hideTailoringImageView];
  327 +
  328 + UIScrollView *scrollView = self.imageScrollView;
  329 + UIView *zoomView = [self viewForZoomingInScrollView:scrollView];
  330 + CGPoint point = [tap locationInView:zoomView];
  331 + if (!CGRectContainsPoint(zoomView.bounds, point)) return;
  332 + if (scrollView.zoomScale == scrollView.maximumZoomScale) {
  333 + [scrollView setZoomScale:1 animated:YES];
  334 + } else {
  335 + [scrollView zoomToRect:CGRectMake(point.x, point.y, 1, 1) animated:YES];
  336 + }
  337 +}
  338 +
  339 +- (void)respondsToPan:(UIPanGestureRecognizer *)pan {
  340 + if (self.yb_isRotating()) return;
  341 +
  342 + YBIBInteractionProfile *profile = ((YBIBImageData *)self.yb_cellData).interactionProfile;
  343 + if (profile.disable) return;
  344 + if ((CGRectIsEmpty(self.imageScrollView.imageView.frame) || !self.imageScrollView.imageView.image)) return;
  345 +
  346 + CGPoint point = [pan locationInView:self];
  347 + CGSize containerSize = self.yb_containerSize(self.yb_currentOrientation());
  348 +
  349 + if (pan.state == UIGestureRecognizerStateBegan) {
  350 + _interactStartPoint = point;
  351 + } else if (pan.state == UIGestureRecognizerStateCancelled || pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateRecognized || pan.state == UIGestureRecognizerStateFailed) {
  352 +
  353 + // End.
  354 + if (_interacting) {
  355 + CGPoint velocity = [pan velocityInView:self.imageScrollView];
  356 +
  357 + BOOL velocityArrive = ABS(velocity.y) > profile.dismissVelocityY;
  358 + BOOL distanceArrive = ABS(point.y - _interactStartPoint.y) > containerSize.height * profile.dismissScale;
  359 +
  360 + BOOL shouldDismiss = distanceArrive || velocityArrive;
  361 + if (shouldDismiss) {
  362 + [self hideBrowser];
  363 + } else {
  364 + [self restoreInteractionWithDuration:profile.restoreDuration];
  365 + }
  366 + }
  367 +
  368 + } else if (pan.state == UIGestureRecognizerStateChanged) {
  369 + if (_interacting) {
  370 +
  371 + // Change.
  372 + self.imageScrollView.center = point;
  373 + CGFloat scale = 1 - ABS(point.y - _interactStartPoint.y) / (containerSize.height * 1.2);
  374 + if (scale > 1) scale = 1;
  375 + if (scale < 0.35) scale = 0.35;
  376 + self.imageScrollView.transform = CGAffineTransformMakeScale(scale, scale);
  377 +
  378 + CGFloat alpha = 1 - ABS(point.y - _interactStartPoint.y) / (containerSize.height * 0.7);
  379 + if (alpha > 1) alpha = 1;
  380 + if (alpha < 0) alpha = 0;
  381 + self.yb_backView.backgroundColor = [self.yb_backView.backgroundColor colorWithAlphaComponent:alpha];
  382 +
  383 + } else {
  384 +
  385 + // Start.
  386 + if (CGPointEqualToPoint(_interactStartPoint, CGPointZero) || self.yb_currentPage() != self.yb_selfPage() || !self.yb_cellIsInCenter() || self.imageScrollView.isZooming) return;
  387 +
  388 + CGPoint velocity = [pan velocityInView:self.imageScrollView];
  389 + CGFloat triggerDistance = profile.triggerDistance;
  390 + CGFloat offsetY = self.imageScrollView.contentOffset.y, height = self.imageScrollView.bounds.size.height;
  391 +
  392 + BOOL distanceArrive = ABS(point.x - _interactStartPoint.x) < triggerDistance && ABS(velocity.x) < 500;
  393 + BOOL upArrive = point.y - _interactStartPoint.y > triggerDistance && offsetY <= 1;
  394 + BOOL downArrive = point.y - _interactStartPoint.y < -triggerDistance && offsetY + height >= MAX(self.imageScrollView.contentSize.height, height) - 1;
  395 +
  396 + BOOL shouldStart = (upArrive || downArrive) && distanceArrive;
  397 + if (!shouldStart) return;
  398 +
  399 + _interactStartPoint = point;
  400 +
  401 + CGRect startFrame = self.imageScrollView.frame;
  402 + CGFloat anchorX = point.x / startFrame.size.width, anchorY = point.y / startFrame.size.height;
  403 + self.imageScrollView.layer.anchorPoint = CGPointMake(anchorX, anchorY);
  404 + self.imageScrollView.userInteractionEnabled = NO;
  405 + self.imageScrollView.scrollEnabled = NO;
  406 + self.imageScrollView.center = point;
  407 +
  408 + self.yb_hideToolViews(YES);
  409 + self.yb_hideStatusBar(NO);
  410 + self.yb_collectionView().scrollEnabled = NO;
  411 + [self hideTailoringImageView];
  412 +
  413 + _interacting = YES;
  414 + }
  415 + }
  416 +}
  417 +
  418 +- (void)restoreInteractionWithDuration:(NSTimeInterval)duration {
  419 + CGSize containerSize = self.yb_containerSize(self.yb_currentOrientation());
  420 +
  421 + void (^animations)(void) = ^{
  422 + self.yb_backView.backgroundColor = [self.yb_backView.backgroundColor colorWithAlphaComponent:1];
  423 +
  424 + CGPoint anchorPoint = self.imageScrollView.layer.anchorPoint;
  425 + self.imageScrollView.center = CGPointMake(containerSize.width * anchorPoint.x, containerSize.height * anchorPoint.y);
  426 + self.imageScrollView.transform = CGAffineTransformIdentity;
  427 + };
  428 + void (^completion)(BOOL finished) = ^(BOOL finished){
  429 + self.imageScrollView.layer.anchorPoint = CGPointMake(0.5, 0.5);
  430 + self.imageScrollView.center = CGPointMake(containerSize.width * 0.5, containerSize.height * 0.5);
  431 + self.imageScrollView.userInteractionEnabled = YES;
  432 + self.imageScrollView.scrollEnabled = YES;
  433 +
  434 + self.yb_hideToolViews(NO);
  435 + self.yb_hideStatusBar(YES);
  436 + self.yb_collectionView().scrollEnabled = YES;
  437 + [self cuttingImage];
  438 +
  439 + self->_interactStartPoint = CGPointZero;
  440 + self->_interacting = NO;
  441 + };
  442 +
  443 + if (duration <= 0) {
  444 + animations();
  445 + completion(NO);
  446 + } else {
  447 + [UIView animateWithDuration:duration animations:animations completion:completion];
  448 + }
  449 +}
  450 +
  451 +#pragma mark - <UIScrollViewDelegate>
  452 +
  453 +- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
  454 + YBIBImageData *data = self.yb_cellData;
  455 + if (data.imageDidZoomBlock) {
  456 + data.imageDidZoomBlock(data, scrollView);
  457 + }
  458 +
  459 + CGRect imageViewFrame = self.imageScrollView.imageView.frame;
  460 + CGFloat width = imageViewFrame.size.width,
  461 + height = imageViewFrame.size.height,
  462 + sHeight = scrollView.bounds.size.height,
  463 + sWidth = scrollView.bounds.size.width;
  464 + if (height > sHeight) {
  465 + imageViewFrame.origin.y = 0;
  466 + } else {
  467 + imageViewFrame.origin.y = (sHeight - height) / 2.0;
  468 + }
  469 + if (width > sWidth) {
  470 + imageViewFrame.origin.x = 0;
  471 + } else {
  472 + imageViewFrame.origin.x = (sWidth - width) / 2.0;
  473 + }
  474 + self.imageScrollView.imageView.frame = imageViewFrame;
  475 +}
  476 +
  477 +- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
  478 + return self.imageScrollView.imageView;
  479 +}
  480 +
  481 +- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
  482 + YBIBImageData *data = self.yb_cellData;
  483 + if (data.imageDidScrollBlock) {
  484 + data.imageDidScrollBlock(data, scrollView);
  485 + }
  486 +
  487 + [self cuttingImage];
  488 +}
  489 +
  490 +- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view {
  491 + [self hideTailoringImageView];
  492 +}
  493 +
  494 +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
  495 + [self hideTailoringImageView];
  496 +}
  497 +
  498 +#pragma mark - <UIGestureRecognizerDelegate>
  499 +
  500 +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
  501 + return YES;
  502 +}
  503 +
  504 +#pragma mark - getters
  505 +
  506 +- (YBIBImageScrollView *)imageScrollView {
  507 + if (!_imageScrollView) {
  508 + _imageScrollView = [YBIBImageScrollView new];
  509 + _imageScrollView.delegate = self;
  510 + }
  511 + return _imageScrollView;
  512 +}
  513 +
  514 +- (UIImageView *)tailoringImageView {
  515 + if (!_tailoringImageView) {
  516 + _tailoringImageView = [UIImageView new];
  517 + _tailoringImageView.contentMode = UIViewContentModeScaleAspectFit;
  518 + }
  519 + return _tailoringImageView;
  520 +}
  521 +
  522 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Image/YBIBImageData+Internal.h 0 → 100644
  1 +//
  2 +// YBIBImageData+Internal.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/12.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBImageData.h"
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +typedef NS_ENUM(NSInteger, YBIBImageLoadingStatus) {
  14 + YBIBImageLoadingStatusNone,
  15 + YBIBImageLoadingStatusCompressing,
  16 + YBIBImageLoadingStatusDecoding,
  17 + YBIBImageLoadingStatusQuerying,
  18 + YBIBImageLoadingStatusProcessing,
  19 + YBIBImageLoadingStatusDownloading,
  20 + YBIBImageLoadingStatusReadingPHAsset,
  21 +};
  22 +
  23 +@class YBIBImageData;
  24 +
  25 +@protocol YBIBImageDataDelegate <NSObject>
  26 +@required
  27 +
  28 +- (void)yb_imageData:(YBIBImageData *)data startLoadingWithStatus:(YBIBImageLoadingStatus)status;
  29 +
  30 +- (void)yb_imageIsInvalidForData:(YBIBImageData *)data;
  31 +
  32 +- (void)yb_imageData:(YBIBImageData *)data readyForImage:(__kindof UIImage *)image;
  33 +
  34 +- (void)yb_imageData:(YBIBImageData *)data readyForThumbImage:(__kindof UIImage *)image;
  35 +
  36 +- (void)yb_imageData:(YBIBImageData *)data readyForCompressedImage:(__kindof UIImage *)image;
  37 +
  38 +- (void)yb_imageData:(YBIBImageData *)data downloadProgress:(CGFloat)progress;
  39 +
  40 +- (void)yb_imageDownloadFailedForData:(YBIBImageData *)data;
  41 +
  42 +@end
  43 +
  44 +@interface YBIBImageData ()
  45 +
  46 +@property (nonatomic, weak) id<YBIBImageDataDelegate> delegate;
  47 +
  48 +/// The status of asynchronous tasks.
  49 +@property (nonatomic, assign) YBIBImageLoadingStatus loadingStatus;
  50 +
  51 +/// The origin image.
  52 +@property (nonatomic, strong) UIImage *originImage;
  53 +
  54 +/// The image compressed by 'originImage' if need.
  55 +@property (nonatomic, strong) UIImage *compressedImage;
  56 +
  57 +- (BOOL)shouldCompress;
  58 +
  59 +- (void)cuttingImageToRect:(CGRect)rect complete:(void(^)(UIImage * _Nullable image))complete;
  60 +
  61 +@end
  62 +
  63 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Image/YBIBImageData.h 0 → 100644
  1 +//
  2 +// YBIBImageData.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/5.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import <Photos/Photos.h>
  10 +#import "YBIBDataProtocol.h"
  11 +#import "YBIBImageLayout.h"
  12 +#import "YBIBImageCache.h"
  13 +#import "YBIBInteractionProfile.h"
  14 +
  15 +NS_ASSUME_NONNULL_BEGIN
  16 +
  17 +@class YBIBImageData;
  18 +
  19 +/// 获取 NSData 的闭包
  20 +typedef NSData * _Nullable (^YBIBImageDataBlock)(void);
  21 +
  22 +/// 获取 UIImage 的闭包
  23 +typedef UIImage * _Nullable (^YBIBImageBlock)(void);
  24 +
  25 +/// 修改 NSURLRequest 并返回的闭包
  26 +typedef NSURLRequest * _Nullable (^YBIBRequestModifierBlock)(YBIBImageData *imageData, NSURLRequest *request);
  27 +
  28 +/// 根据图片逻辑像素和 scale 判断是否需要预解码的闭包
  29 +typedef BOOL (^YBIBPreDecodeDecisionBlock)(YBIBImageData *imageData, CGSize imageSize, CGFloat scale);
  30 +
  31 +/// 修改 UIImage 并返回的闭包
  32 +typedef void (^YBIBImageModifierBlock)(YBIBImageData *imageData, UIImage *image, void(^completion)(UIImage *processedImage));
  33 +
  34 +/// 单击事件的处理闭包
  35 +typedef void (^YBIBImageSingleTouchBlock)(YBIBImageData *imageData);
  36 +
  37 +/// 内部图片滚动视图状态回调闭包
  38 +typedef void (^YBIBImageScrollViewStatusBlock)(YBIBImageData *imageData, UIScrollView *scrollView);
  39 +
  40 +
  41 +/**
  42 + 图片数据类,承担配置数据和处理数据的责任
  43 + */
  44 +@interface YBIBImageData : NSObject <YBIBDataProtocol>
  45 +
  46 +/// 本地图片名字
  47 +@property (nonatomic, copy, nullable) NSString *imageName;
  48 +
  49 +/// 本地图片路径
  50 +@property (nonatomic, copy, nullable) NSString *imagePath;
  51 +
  52 +/// 本地图片字节码,返回 NSData
  53 +@property (nonatomic, copy, nullable) YBIBImageDataBlock imageData;
  54 +
  55 +/// 本地图片,返回 UIImage 及其衍生类 (若不是遵循'YYAnimatedImage'协议的类型,将失去对动图和拓展格式的支持)
  56 +@property (nonatomic, copy, nullable) YBIBImageBlock image;
  57 +
  58 +/// 网络图片资源
  59 +@property (nonatomic, copy, nullable) NSURL *imageURL;
  60 +
  61 +/// 修改 NSURLRequest 并返回
  62 +@property (nonatomic, copy, nullable) YBIBRequestModifierBlock requestModifier;
  63 +
  64 +/// 相册图片资源
  65 +@property (nonatomic, strong, nullable) PHAsset *imagePHAsset;
  66 +
  67 +/// 投影视图,当前数据模型对应外界业务的 UIView (通常为 UIImageView),做转场动效用
  68 +@property (nonatomic, weak, nullable) __kindof UIView *projectiveView;
  69 +
  70 +/// 预览图/缩约图,注意若这个图片过大会导致内存压力(若 projectiveView 存在且是 UIImageView 类型将会自动获取缩约图)
  71 +@property (nonatomic, strong, nullable) UIImage *thumbImage;
  72 +
  73 +/// 预览图/缩约图 URL,缓存中未找到则忽略(若 projectiveView 存在且是 UIImageView 类型将会自动获取缩约图)
  74 +@property (nonatomic, copy, nullable) NSURL *thumbURL;
  75 +
  76 +/// 是否允许保存到相册
  77 +@property (nonatomic, assign) BOOL allowSaveToPhotoAlbum;
  78 +
  79 +/// 根据图片信息判断是否需要预解码
  80 +@property (nonatomic, copy, nullable) YBIBPreDecodeDecisionBlock preDecodeDecision;
  81 +
  82 +/// 是否异步预解码,默认为 YES
  83 +@property (nonatomic, assign) BOOL shouldPreDecodeAsync;
  84 +
  85 +/// 压缩物理像素界限大小,当图片超过这个值将会被压缩显示,默认为 4096*4096
  86 +@property (nonatomic, assign) CGFloat compressingSize;
  87 +
  88 +/// 触发裁剪的缩放比例,必须大于等于 1,默认情况内部会动态计算 (仅当图片需要压缩显示时有效)
  89 +@property (nonatomic, assign) CGFloat cuttingZoomScale;
  90 +
  91 +/**
  92 + 判断图片是否需要压缩显示
  93 + */
  94 +- (BOOL)shouldCompressWithImage:(UIImage *)image;
  95 +
  96 +/**
  97 + 修改原始图片并返回处理后的图片 (特别注意当 image 是大图的时候,避免 OOM)
  98 + Example:
  99 + [... setImageModifier:^(YBIBImageData *imageData, UIImage * _Nonnull image, void (^ _Nonnull completion)(UIImage * _Nonnull)) {
  100 + //step 1 : Add watermark, trademark, etc. (Sync or async).
  101 + ... processing code ...
  102 + //step 2 : Return the processed UIImage.
  103 + completion(image);
  104 + }];
  105 + */
  106 +@property (nonatomic, copy, nullable) YBIBImageModifierBlock originImageModifier;
  107 +
  108 +/// 修改压缩图片并返回处理后的图片 (仅在当前图片需要被压缩时有效)
  109 +@property (nonatomic, copy, nullable) YBIBImageModifierBlock compressedImageModifier;
  110 +
  111 +/// 修改裁剪图片并返回处理后的图片 (仅在当前图片需要被裁剪时有效)
  112 +@property (nonatomic, copy, nullable) YBIBImageModifierBlock cuttedImageModifier;
  113 +
  114 +/// 预留属性可随意使用
  115 +@property (nonatomic, strong, nullable) id extraData;
  116 +
  117 +/// 手势交互动效配置文件
  118 +@property (nonatomic, strong) YBIBInteractionProfile *interactionProfile;
  119 +
  120 +/// 单击的处理,默认是退出图片浏览器
  121 +@property (nonatomic, copy, nullable) YBIBImageSingleTouchBlock singleTouchBlock;
  122 +
  123 +/// 图片滚动的回调
  124 +@property (nonatomic, copy, nullable) YBIBImageScrollViewStatusBlock imageDidScrollBlock;
  125 +
  126 +/// 图片缩放的回调
  127 +@property (nonatomic, copy, nullable) YBIBImageScrollViewStatusBlock imageDidZoomBlock;
  128 +
  129 +/// 图片布局类 (赋值可自定义)
  130 +@property (nonatomic, strong) id<YBIBImageLayout> layout;
  131 +/// 默认图片布局类 (可配置其属性)
  132 +@property (nonatomic, weak, readonly) YBIBImageLayout *defaultLayout;
  133 +
  134 +/**
  135 + 终止处理数据程序
  136 + */
  137 +- (void)stopLoading;
  138 +
  139 +/**
  140 + 清除缓存的数据
  141 + */
  142 +- (void)clearCache;
  143 +
  144 +/**
  145 + 加载数据,一般不需要调用这个方法,当该数据对象做了数据更新时调用
  146 + */
  147 +- (void)loadData;
  148 +
  149 +/// 处理后的原始图片
  150 +@property (nonatomic, strong, readonly) UIImage *originImage;
  151 +
  152 +/// 处理后的压缩图片
  153 +@property (nonatomic, strong, readonly) UIImage *compressedImage;
  154 +
  155 +@end
  156 +
  157 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Image/YBIBImageData.m 0 → 100644
  1 +//
  2 +// YBIBImageData.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/5.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBImage.h"
  10 +#import "YBIBImageData.h"
  11 +#import "YBIBImageCell.h"
  12 +#import "YBIBPhotoAlbumManager.h"
  13 +#import "YBIBImageData+Internal.h"
  14 +#import "YBIBUtilities.h"
  15 +#import "YBIBImageCache+Internal.h"
  16 +#import "YBIBSentinel.h"
  17 +#import "YBIBCopywriter.h"
  18 +#import <AssetsLibrary/AssetsLibrary.h>
  19 +
  20 +extern CGImageRef YYCGImageCreateDecodedCopy(CGImageRef imageRef, BOOL decodeForDisplay);
  21 +
  22 +static dispatch_queue_t YBIBImageProcessingQueue(void) {
  23 + static dispatch_queue_t queue;
  24 + static dispatch_once_t onceToken;
  25 + dispatch_once(&onceToken, ^{
  26 + queue = dispatch_queue_create("com.yangbo.imagebrowser.imageprocessing", DISPATCH_QUEUE_CONCURRENT);
  27 + });
  28 + return queue;
  29 +}
  30 +
  31 +@implementation YBIBImageData {
  32 + __weak id _downloadToken;
  33 + YBIBSentinel *_cuttingSentinel;
  34 + /// Stop processing tasks when in freeze.
  35 + BOOL _freezing;
  36 +}
  37 +
  38 +#pragma mark - life cycle
  39 +
  40 +- (void)dealloc {
  41 + if (_downloadToken && [self.yb_webImageMediator() respondsToSelector:@selector(yb_cancelTaskWithDownloadToken:)]) {
  42 + [self.yb_webImageMediator() yb_cancelTaskWithDownloadToken:_downloadToken];
  43 + }
  44 + [self.imageCache removeForKey:self.cacheKey];
  45 +}
  46 +
  47 +- (instancetype)init {
  48 + self = [super init];
  49 + if (self) {
  50 + [self initValue];
  51 + }
  52 + return self;
  53 +}
  54 +
  55 +- (void)initValue {
  56 + _defaultLayout = _layout = [YBIBImageLayout new];
  57 + _loadingStatus = YBIBImageLoadingStatusNone;
  58 + _compressingSize = 4096 * 4096;
  59 + _shouldPreDecodeAsync = YES;
  60 + _freezing = NO;
  61 + _cuttingSentinel = [YBIBSentinel new];
  62 + _interactionProfile = [YBIBInteractionProfile new];
  63 + _allowSaveToPhotoAlbum = YES;
  64 +}
  65 +
  66 +#pragma mark - load data
  67 +
  68 +- (void)loadData {
  69 + _freezing = NO;
  70 +
  71 + // Avoid handling asynchronous tasks repeatedly.
  72 + if (self.loadingStatus != YBIBImageLoadingStatusNone) {
  73 + [self loadThumbImage];
  74 + self.loadingStatus = self.loadingStatus;
  75 + return;
  76 + }
  77 +
  78 + if (self.originImage) {
  79 + [self loadOriginImage];
  80 + } else if (self.imageName || self.imagePath || self.imageData) {
  81 + [self loadYBImage];
  82 + } else if (self.image) {
  83 + [self loadImageBlock];
  84 + } else if (self.imageURL) {
  85 + [self loadThumbImage];
  86 + [self loadURL];
  87 + } else if (self.imagePHAsset) {
  88 + [self loadThumbImage];
  89 + [self loadPHAsset];
  90 + } else {
  91 + [self.delegate yb_imageIsInvalidForData:self];
  92 + }
  93 +}
  94 +
  95 +- (void)loadOriginImage {
  96 + if (_freezing) return;
  97 + if (!self.originImage) return;
  98 +
  99 + if ([self shouldCompress]) {
  100 + if (self.compressedImage) {
  101 + [self.delegate yb_imageData:self readyForCompressedImage:self.compressedImage];
  102 + } else {
  103 + [self loadThumbImage];
  104 + [self loadOriginImage_compress];
  105 + }
  106 + } else {
  107 + [self.delegate yb_imageData:self readyForImage:self.originImage];
  108 + }
  109 +}
  110 +- (void)loadOriginImage_compress {
  111 + if (_freezing) return;
  112 + if (!self.originImage) return;
  113 +
  114 + self.loadingStatus = YBIBImageLoadingStatusCompressing;
  115 + __weak typeof(self) wSelf = self;
  116 + CGSize size = [self bestSizeOfCompressing];
  117 +
  118 + YBIB_DISPATCH_ASYNC(YBIBImageProcessingQueue(), ^{
  119 + if (self->_freezing) {
  120 + self.loadingStatus = YBIBImageLoadingStatusNone;
  121 + return;
  122 + }
  123 + // Ensure the best display effect.
  124 + UIGraphicsBeginImageContextWithOptions(size, NO, UIScreen.mainScreen.scale);
  125 + [self.originImage drawInRect:CGRectMake(0, 0, size.width, size.height)];
  126 + if (self->_freezing) {
  127 + UIGraphicsEndImageContext();
  128 + self.loadingStatus = YBIBImageLoadingStatusNone;
  129 + return;
  130 + }
  131 + UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
  132 + UIGraphicsEndImageContext();
  133 +
  134 + YBIB_DISPATCH_ASYNC_MAIN(^{
  135 + __strong typeof(wSelf) self = wSelf;
  136 + if (!self) return;
  137 +
  138 + self.loadingStatus = YBIBImageLoadingStatusNone;
  139 +
  140 + [self modifyImageWithModifier:self.compressedImageModifier image:resultImage completion:^(UIImage *processedImage) {
  141 + __strong typeof(wSelf) self = wSelf;
  142 + if (!self) return;
  143 + self.compressedImage = processedImage ?: self.originImage;
  144 + [self.delegate yb_imageData:self readyForCompressedImage:self.compressedImage];
  145 + }];
  146 + })
  147 + })
  148 +}
  149 +
  150 +- (void)loadYBImage {
  151 + if (_freezing) return;
  152 + NSString *name = self.imageName.copy;
  153 + NSString *path = self.imagePath.copy;
  154 + NSData *data = self.imageData ? self.imageData().copy : nil;
  155 + if (name.length == 0 && path.length == 0 && data.length == 0) return;
  156 +
  157 + YBImageDecodeDecision decision = [self defaultDecodeDecision];
  158 +
  159 + __block YBImage *image;
  160 + __weak typeof(self) wSelf = self;
  161 + void(^dealBlock)(void) = ^{
  162 + if (name.length > 0) {
  163 + image = [YBImage imageNamed:name decodeDecision:decision];
  164 + } else if (path.length > 0) {
  165 + image = [YBImage imageWithContentsOfFile:path decodeDecision:decision];
  166 + } else if (data.length > 0) {
  167 + image = [YBImage imageWithData:data scale:UIScreen.mainScreen.scale decodeDecision:decision];
  168 + }
  169 + YBIB_DISPATCH_ASYNC_MAIN(^{
  170 + __strong typeof(wSelf) self = wSelf;
  171 + if (!self) return;
  172 + self.loadingStatus = YBIBImageLoadingStatusNone;
  173 + if (image) {
  174 + [self setOriginImageAndLoadWithImage:image];
  175 + } else {
  176 + [self.delegate yb_imageIsInvalidForData:self];
  177 + }
  178 + })
  179 + };
  180 +
  181 + if (self.shouldPreDecodeAsync) {
  182 + [self loadThumbImage];
  183 + self.loadingStatus = YBIBImageLoadingStatusDecoding;
  184 + YBIB_DISPATCH_ASYNC(YBIBImageProcessingQueue(), dealBlock)
  185 + } else {
  186 + self.loadingStatus = YBIBImageLoadingStatusDecoding;
  187 + dealBlock();
  188 + }
  189 +}
  190 +
  191 +- (void)loadImageBlock {
  192 + if (_freezing) return;
  193 + __block UIImage *image = self.image ? self.image() : nil;
  194 + if (!image) return;
  195 +
  196 + BOOL shouldPreDecode = self.preDecodeDecision ? self.preDecodeDecision(self, image.size, image.scale) : ![self shouldCompressWithImage:image];
  197 +
  198 + __weak typeof(self) wSelf = self;
  199 + void(^dealBlock)(void) = ^{
  200 + // Do not need to decode If 'image' conformed 'YYAnimatedImage'. (Not entirely accurate.)
  201 + if (![image conformsToProtocol:@protocol(YYAnimatedImage)]) {
  202 + CGImageRef cgImage = YYCGImageCreateDecodedCopy(image.CGImage, shouldPreDecode);
  203 + image = [UIImage imageWithCGImage:cgImage scale:image.scale orientation:image.imageOrientation];
  204 + if (cgImage) CGImageRelease(cgImage);
  205 + }
  206 + YBIB_DISPATCH_ASYNC_MAIN(^{
  207 + __strong typeof(wSelf) self = wSelf;
  208 + if (!self) return;
  209 + self.loadingStatus = YBIBImageLoadingStatusNone;
  210 + [self setOriginImageAndLoadWithImage:image];
  211 + })
  212 + };
  213 +
  214 + if (self.shouldPreDecodeAsync) {
  215 + [self loadThumbImage];
  216 + self.loadingStatus = YBIBImageLoadingStatusDecoding;
  217 + YBIB_DISPATCH_ASYNC(YBIBImageProcessingQueue(), dealBlock)
  218 + } else {
  219 + self.loadingStatus = YBIBImageLoadingStatusDecoding;
  220 + dealBlock();
  221 + }
  222 +}
  223 +
  224 +- (void)loadURL {
  225 + if (!self.imageURL || self.imageURL.absoluteString.length == 0) return;
  226 + [self loadURL_queryCache];
  227 +}
  228 +- (void)loadURL_queryCache {
  229 + if (_freezing) return;
  230 + if (!self.imageURL || self.imageURL.absoluteString.length == 0) return;
  231 +
  232 + YBImageDecodeDecision decision = [self defaultDecodeDecision];
  233 +
  234 + self.loadingStatus = YBIBImageLoadingStatusQuerying;
  235 + [self.yb_webImageMediator() yb_queryCacheOperationForKey:self.imageURL completed:^(UIImage * _Nullable image, NSData * _Nullable imageData) {
  236 + if (!imageData || imageData.length == 0) {
  237 + YBIB_DISPATCH_ASYNC_MAIN(^{
  238 + self.loadingStatus = YBIBImageLoadingStatusNone;
  239 + [self loadURL_download];
  240 + })
  241 + return;
  242 + }
  243 +
  244 + YBIB_DISPATCH_ASYNC(YBIBImageProcessingQueue(), ^{
  245 + if (self->_freezing) {
  246 + self.loadingStatus = YBIBImageLoadingStatusNone;
  247 + return;
  248 + }
  249 + YBImage *image = [YBImage imageWithData:imageData scale:UIScreen.mainScreen.scale decodeDecision:decision];
  250 + __weak typeof(self) wSelf = self;
  251 + YBIB_DISPATCH_ASYNC_MAIN(^{
  252 + __strong typeof(wSelf) self = wSelf;
  253 + if (!self) return;
  254 + self.loadingStatus = YBIBImageLoadingStatusNone;
  255 + if (image) { // Maybe the image data is invalid.
  256 + [self setOriginImageAndLoadWithImage:image];
  257 + } else {
  258 + [self loadURL_download];
  259 + }
  260 + })
  261 + })
  262 + }];
  263 +}
  264 +- (void)loadURL_download {
  265 + if (_freezing) return;
  266 + if (!self.imageURL || self.imageURL.absoluteString.length == 0) return;
  267 +
  268 + YBImageDecodeDecision decision = [self defaultDecodeDecision];
  269 +
  270 + self.loadingStatus = YBIBImageLoadingStatusDownloading;
  271 + __weak typeof(self) wSelf = self;
  272 + _downloadToken = [self.yb_webImageMediator() yb_downloadImageWithURL:self.imageURL requestModifier:^NSURLRequest * _Nullable(NSURLRequest * _Nonnull request) {
  273 + return self.requestModifier ? self.requestModifier(self, request) : request;
  274 + } progress:^(NSInteger receivedSize, NSInteger expectedSize) {
  275 + CGFloat progress = receivedSize * 1.0 / expectedSize ?: 0;
  276 + YBIB_DISPATCH_ASYNC_MAIN(^{
  277 + __strong typeof(wSelf) self = wSelf;
  278 + if (!self) return;
  279 + [self.delegate yb_imageData:self downloadProgress:progress];
  280 + })
  281 + } success:^(NSData * _Nullable imageData, BOOL finished) {
  282 + if (!finished) return;
  283 +
  284 + YBIB_DISPATCH_ASYNC(YBIBImageProcessingQueue(), ^{
  285 + if (self->_freezing) {
  286 + self.loadingStatus = YBIBImageLoadingStatusNone;
  287 + return;
  288 + }
  289 + YBImage *image = [YBImage imageWithData:imageData scale:UIScreen.mainScreen.scale decodeDecision:decision];
  290 + YBIB_DISPATCH_ASYNC_MAIN(^{
  291 + __strong typeof(wSelf) self = wSelf;
  292 + if (!self) return;
  293 + [self.yb_webImageMediator() yb_storeToDiskWithImageData:imageData forKey:self.imageURL];
  294 + self.loadingStatus = YBIBImageLoadingStatusNone;
  295 + if (image) {
  296 + [self setOriginImageAndLoadWithImage:image];
  297 + } else {
  298 + [self.delegate yb_imageIsInvalidForData:self];
  299 + }
  300 + })
  301 + })
  302 + } failed:^(NSError * _Nullable error, BOOL finished) {
  303 + if (!finished) return;
  304 + __strong typeof(wSelf) self = wSelf;
  305 + if (!self) return;
  306 + self.loadingStatus = YBIBImageLoadingStatusNone;
  307 + [self.delegate yb_imageDownloadFailedForData:self];
  308 + }];
  309 +}
  310 +
  311 +- (void)loadPHAsset {
  312 + if (_freezing) return;
  313 + if (!self.imagePHAsset) return;
  314 +
  315 + YBImageDecodeDecision decision = [self defaultDecodeDecision];
  316 +
  317 + self.loadingStatus = YBIBImageLoadingStatusReadingPHAsset;
  318 + YBIB_DISPATCH_ASYNC(YBIBImageProcessingQueue(), ^{
  319 + [YBIBPhotoAlbumManager getImageDataWithPHAsset:self.imagePHAsset completion:^(NSData * _Nullable data) {
  320 + if (self->_freezing) {
  321 + self.loadingStatus = YBIBImageLoadingStatusNone;
  322 + return;
  323 + }
  324 + YBImage *image = [YBImage imageWithData:data scale:UIScreen.mainScreen.scale decodeDecision:decision];
  325 + __weak typeof(self) wSelf = self;
  326 + YBIB_DISPATCH_ASYNC_MAIN(^{
  327 + __strong typeof(wSelf) self = wSelf;
  328 + if (!self) return;
  329 +
  330 + self.loadingStatus = YBIBImageLoadingStatusNone;
  331 + if (image) {
  332 + [self setOriginImageAndLoadWithImage:image];
  333 + } else {
  334 + [self.delegate yb_imageIsInvalidForData:self];
  335 + }
  336 + })
  337 + }];
  338 + })
  339 +}
  340 +
  341 +- (void)loadThumbImage {
  342 + if (_freezing) return;
  343 + if (self.thumbImage) {
  344 + [self.delegate yb_imageData:self readyForThumbImage:self.thumbImage];
  345 + } else if (self.thumbURL) {
  346 + __weak typeof(self) wSelf = self;
  347 + [self.yb_webImageMediator() yb_queryCacheOperationForKey:self.thumbURL completed:^(UIImage * _Nullable image, NSData * _Nullable imageData) {
  348 + __strong typeof(wSelf) self = wSelf;
  349 + if (!self) return;
  350 +
  351 + UIImage *thumbImage;
  352 + if (image) {
  353 + thumbImage = image;
  354 + } else if (imageData) {
  355 + thumbImage = [UIImage imageWithData:imageData];
  356 + }
  357 + // If the target image is ready, ignore the thumb image.
  358 + BOOL shouldIgnore = [self shouldCompress] ? (self.compressedImage != nil) : (self.originImage != nil);
  359 + if (!shouldIgnore && thumbImage) {
  360 + [self.delegate yb_imageData:self readyForThumbImage:thumbImage];
  361 + }
  362 + }];
  363 + } else if (self.projectiveView && [self.projectiveView isKindOfClass:UIImageView.self] && ((UIImageView *)self.projectiveView).image) {
  364 + UIImage *thumbImage = ((UIImageView *)self.projectiveView).image;
  365 + [self.delegate yb_imageData:self readyForThumbImage:thumbImage];
  366 + }
  367 +}
  368 +
  369 +#pragma mark - internal
  370 +
  371 +- (void)cuttingImageToRect:(CGRect)rect complete:(void (^)(UIImage * _Nullable))complete {
  372 + if (_freezing) return;
  373 + if (!self.originImage) return;
  374 +
  375 + int32_t value = [_cuttingSentinel increase];
  376 + BOOL (^isCancelled)(void) = ^BOOL(void) {
  377 + if (self->_freezing) return YES;
  378 + return value != self->_cuttingSentinel.value;
  379 + };
  380 +
  381 + YBIB_DISPATCH_ASYNC(YBIBImageProcessingQueue(), ^{
  382 + if (isCancelled()) {
  383 + complete(nil);
  384 + return;
  385 + }
  386 + CGFloat scale = self.originImage.scale;
  387 + CGFloat width = self.originImage.size.width;
  388 + CGFloat height = self.originImage.size.height;
  389 +
  390 + BOOL reverseWidthHeight = NO;
  391 + CGAffineTransform transform = CGAffineTransformIdentity;
  392 + switch (self.originImage.imageOrientation) {
  393 + case UIImageOrientationDown:
  394 + case UIImageOrientationDownMirrored:
  395 + CGAffineTransformTranslate(CGAffineTransformMakeRotation(-M_PI), -width, -height);
  396 + break;
  397 + case UIImageOrientationLeft:
  398 + case UIImageOrientationLeftMirrored:
  399 + transform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(M_PI_2), 0, -height);
  400 + reverseWidthHeight = YES;
  401 + break;
  402 + case UIImageOrientationRight:
  403 + case UIImageOrientationRightMirrored:
  404 + transform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(-M_PI_2), -width, 0);
  405 + reverseWidthHeight = YES;
  406 + break;
  407 + default: break;
  408 + }
  409 + transform = CGAffineTransformScale(transform, scale, scale);
  410 + CGRect correctRect = CGRectApplyAffineTransform(rect, transform);
  411 + CGImageRef cgImage = CGImageCreateWithImageInRect(self.originImage.CGImage, correctRect);
  412 +
  413 + if (isCancelled()) {
  414 + complete(nil);
  415 + if (cgImage) CGImageRelease(cgImage);
  416 + return;
  417 + }
  418 + CGFloat cgWidth = reverseWidthHeight ? CGImageGetHeight(cgImage) : CGImageGetWidth(cgImage);
  419 + CGFloat cgHeight = reverseWidthHeight ? CGImageGetWidth(cgImage) : CGImageGetHeight(cgImage);
  420 + CGSize size = [self bestSizeOfCuttingWithOriginSize:CGSizeMake(cgWidth / scale, cgHeight / scale)];
  421 + UIImage *tmpImage = [UIImage imageWithCGImage:cgImage scale:self.originImage.scale orientation:self.originImage.imageOrientation];
  422 + if (isCancelled()) {
  423 + complete(nil);
  424 + if (cgImage) CGImageRelease(cgImage);
  425 + return;
  426 + }
  427 +
  428 + // Ensure the best display effect.
  429 + UIGraphicsBeginImageContextWithOptions(size, NO, UIScreen.mainScreen.scale);
  430 + [tmpImage drawInRect:CGRectMake(0, 0, size.width, size.height)];
  431 + if (isCancelled()) {
  432 + complete(nil);
  433 + UIGraphicsEndImageContext();
  434 + if (cgImage) CGImageRelease(cgImage);
  435 + return;
  436 + }
  437 + UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
  438 + UIGraphicsEndImageContext();
  439 + if (cgImage) CGImageRelease(cgImage);
  440 +
  441 + __weak typeof(self) wSelf = self;
  442 + YBIB_DISPATCH_ASYNC_MAIN(^{
  443 + __strong typeof(wSelf) self = wSelf;
  444 + if (!self) return;
  445 +
  446 + [self modifyImageWithModifier:self.cuttedImageModifier image:resultImage completion:^(UIImage *image) {
  447 + __strong typeof(wSelf) self = wSelf;
  448 + if (!self) return;
  449 + complete(image);
  450 + }];
  451 + })
  452 + })
  453 +}
  454 +
  455 +- (BOOL)shouldCompress {
  456 + return [self shouldCompressWithImage:self.originImage];
  457 +}
  458 +
  459 +#pragma mark - public
  460 +
  461 +- (BOOL)shouldCompressWithImage:(UIImage *)image {
  462 + if (!image) return NO;
  463 + return [self shouldCompressWithImageSize:image.size scale:image.scale];
  464 +}
  465 +
  466 +- (void)stopLoading {
  467 + _freezing = YES;
  468 + self.loadingStatus = YBIBImageLoadingStatusNone;
  469 +}
  470 +
  471 +- (void)clearCache {
  472 + [self.imageCache removeForKey:self.cacheKey];
  473 +}
  474 +
  475 +#pragma mark - private
  476 +
  477 +/// 'size': logic pixel.
  478 +- (BOOL)shouldCompressWithImageSize:(CGSize)size scale:(CGFloat)scale {
  479 + return size.width * scale * size.height * scale > self.compressingSize;
  480 +}
  481 +
  482 +/// Logic pixel.
  483 +- (CGSize)bestSizeOfCompressing {
  484 + if (!self.originImage) return CGSizeZero;
  485 + UIDeviceOrientation orientation = self.yb_currentOrientation();
  486 + CGRect imageViewFrame = [self.layout yb_imageViewFrameWithContainerSize:self.yb_containerSize(orientation) imageSize:self.originImage.size orientation:orientation];
  487 + return imageViewFrame.size;
  488 +}
  489 +
  490 +/// Logic pixel.
  491 +- (CGSize)bestSizeOfCuttingWithOriginSize:(CGSize)originSize {
  492 + CGSize containerSize = self.yb_containerSize(self.yb_currentOrientation());
  493 + CGFloat maxWidth = containerSize.width, maxHeight = containerSize.height;
  494 + CGFloat oWidth = originSize.width, oHeight = originSize.height;
  495 + if (maxWidth <= 0 || maxHeight <= 0 || oWidth <= 0 || oHeight <= 0) return CGSizeZero;
  496 +
  497 + if (oWidth <= maxWidth && oHeight <= maxHeight) {
  498 + return originSize;
  499 + }
  500 + CGFloat rWidth = 0, rHeight = 0;
  501 + if (oWidth / maxWidth < oHeight / maxHeight) {
  502 + rHeight = maxHeight;
  503 + rWidth = oWidth / oHeight * rHeight;
  504 + } else {
  505 + rWidth = maxWidth;
  506 + rHeight = oHeight / oWidth * rWidth;
  507 + }
  508 + return CGSizeMake(rWidth, rHeight);
  509 +}
  510 +
  511 +- (YBImageDecodeDecision)defaultDecodeDecision {
  512 + __weak typeof(self) wSelf = self;
  513 + return ^BOOL(CGSize imageSize, CGFloat scale) {
  514 + __strong typeof(wSelf) self = wSelf;
  515 + if (!self) return NO;
  516 + CGSize logicSize = CGSizeMake(imageSize.width / scale, imageSize.height / scale);
  517 + if (self.preDecodeDecision) return self.preDecodeDecision(self, logicSize, scale);
  518 + if ([self shouldCompressWithImageSize:logicSize scale:scale]) return NO;
  519 + return YES;
  520 + };
  521 +}
  522 +
  523 +- (void)modifyImageWithModifier:(YBIBImageModifierBlock)modifier image:(UIImage *)image completion:(void(^)(UIImage *processedImage))completion {
  524 + if (modifier) {
  525 + self.loadingStatus = YBIBImageLoadingStatusProcessing;
  526 + __weak typeof(self) wSelf = self;
  527 + modifier(self, image, ^(UIImage *processedImage){
  528 + // This step is necessary, maybe 'self' is already 'dealloc' if processing code takes too much time.
  529 + __strong typeof(wSelf) self = wSelf;
  530 + if (!self) return;
  531 + self.loadingStatus = YBIBImageLoadingStatusNone;
  532 + completion(processedImage);
  533 + });
  534 + } else {
  535 + completion(image);
  536 + }
  537 +}
  538 +
  539 +- (void)setOriginImageAndLoadWithImage:(UIImage *)image {
  540 + __weak typeof(self) wSelf = self;
  541 + [self modifyImageWithModifier:self.originImageModifier image:image completion:^(UIImage *processedImage) {
  542 + __strong typeof(wSelf) self = wSelf;
  543 + if (!self) return;
  544 + self.originImage = processedImage;
  545 + [self loadOriginImage];
  546 + }];
  547 +}
  548 +
  549 +- (void)saveToPhotoAlbumCompleteWithError:(nullable NSError *)error {
  550 + if (error) {
  551 + [self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].saveToPhotoAlbumFailed];
  552 + } else {
  553 + [self.yb_auxiliaryViewHandler() yb_showCorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].saveToPhotoAlbumSuccess];
  554 + }
  555 +}
  556 +
  557 +- (void)UIImageWriteToSavedPhotosAlbum_completedWithImage:(UIImage *)image error:(NSError *)error context:(void *)context {
  558 + [self saveToPhotoAlbumCompleteWithError:error];
  559 +}
  560 +
  561 +- (YBIBImageCache *)imageCache {
  562 + return self.yb_backView.ybib_imageCache;
  563 +}
  564 +
  565 +#pragma mark - <YBIBDataProtocol>
  566 +
  567 +@synthesize yb_isHideTransitioning = _yb_isHideTransitioning;
  568 +@synthesize yb_currentOrientation = _yb_currentOrientation;
  569 +@synthesize yb_containerSize = _yb_containerSize;
  570 +@synthesize yb_containerView = _yb_containerView;
  571 +@synthesize yb_auxiliaryViewHandler = _yb_auxiliaryViewHandler;
  572 +@synthesize yb_webImageMediator = _yb_webImageMediator;
  573 +@synthesize yb_backView = _yb_backView;
  574 +
  575 +- (nonnull Class)yb_classOfCell {
  576 + return YBIBImageCell.self;
  577 +}
  578 +
  579 +- (UIView *)yb_projectiveView {
  580 + return self.projectiveView;
  581 +}
  582 +
  583 +- (CGRect)yb_imageViewFrameWithContainerSize:(CGSize)containerSize imageSize:(CGSize)imageSize orientation:(UIDeviceOrientation)orientation {
  584 + return [self.layout yb_imageViewFrameWithContainerSize:containerSize imageSize:imageSize orientation:orientation];
  585 +}
  586 +
  587 +- (void)yb_preload {
  588 + if (!self.delegate) {
  589 + [self loadData];
  590 + }
  591 +}
  592 +
  593 +- (BOOL)yb_allowSaveToPhotoAlbum {
  594 + return self.allowSaveToPhotoAlbum;
  595 +}
  596 +
  597 +- (void)yb_saveToPhotoAlbum {
  598 + void(^saveData)(NSData *) = ^(NSData * _Nonnull data){
  599 + [[ALAssetsLibrary new] writeImageDataToSavedPhotosAlbum:data metadata:nil completionBlock:^(NSURL *assetURL, NSError *error) {
  600 + [self saveToPhotoAlbumCompleteWithError:error];
  601 + }];
  602 + };
  603 + void(^saveImage)(UIImage *) = ^(UIImage * _Nonnull image){
  604 + UIImageWriteToSavedPhotosAlbum(image, self, @selector(UIImageWriteToSavedPhotosAlbum_completedWithImage:error:context:), NULL);
  605 + };
  606 + void(^unableToSave)(void) = ^(){
  607 + [self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].unableToSave];
  608 + };
  609 +
  610 + [YBIBPhotoAlbumManager getPhotoAlbumAuthorizationSuccess:^{
  611 + if ([self.originImage conformsToProtocol:@protocol(YYAnimatedImage)] && [self.originImage respondsToSelector:@selector(animatedImageData)] && [self.originImage performSelector:@selector(animatedImageData)]) {
  612 + NSData *data = [self.originImage performSelector:@selector(animatedImageData)];
  613 + data ? saveData(data) : unableToSave();
  614 + } else if (self.originImage) {
  615 + saveImage(self.originImage);
  616 + } else if (self.imageURL) {
  617 + [self.yb_webImageMediator() yb_queryCacheOperationForKey:self.imageURL completed:^(UIImage * _Nullable image, NSData * _Nullable data) {
  618 + if (data) {
  619 + saveData(data);
  620 + } else if (image) {
  621 + saveImage(image);
  622 + } else {
  623 + unableToSave();
  624 + }
  625 + }];
  626 + } else {
  627 + unableToSave();
  628 + }
  629 + } failed:^{
  630 + [self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].getPhotoAlbumAuthorizationFailed];
  631 + }];
  632 +}
  633 +
  634 +#pragma mark - getters & setters
  635 +
  636 +@synthesize delegate = _delegate;
  637 +- (void)setDelegate:(id<YBIBImageDataDelegate>)delegate {
  638 + _delegate = delegate;
  639 + if (delegate) {
  640 + [self loadData];
  641 + } else {
  642 + _freezing = YES;
  643 + // Remove the resident cache if '_delegate' is nil.
  644 + [self.imageCache removeResidentForKey:self.cacheKey];
  645 + }
  646 +}
  647 +- (id<YBIBImageDataDelegate>)delegate {
  648 + // Stop sending data to the '_delegate' if it is transiting.
  649 + return self.yb_isHideTransitioning() ? nil : _delegate;
  650 +}
  651 +
  652 +- (void)setImageURL:(NSURL *)imageURL {
  653 + _imageURL = [imageURL isKindOfClass:NSString.class] ? [NSURL URLWithString:(NSString *)imageURL] : imageURL;
  654 +}
  655 +
  656 +- (NSString *)cacheKey {
  657 + return [NSString stringWithFormat:@"%p", self];
  658 +}
  659 +
  660 +- (void)setOriginImage:(__kindof UIImage *)originImage {
  661 + // 'image' should be resident if '_delegate' exists.
  662 + [self.imageCache setImage:originImage type:YBIBImageCacheTypeOrigin forKey:self.cacheKey resident:self->_delegate != nil];
  663 +}
  664 +- (UIImage *)originImage {
  665 + return [self.imageCache imageForKey:self.cacheKey type:YBIBImageCacheTypeOrigin];
  666 +}
  667 +
  668 +- (void)setCompressedImage:(UIImage *)compressedImage {
  669 + // 'image' should be resident if '_delegate' exists.
  670 + [self.imageCache setImage:compressedImage type:YBIBImageCacheTypeCompressed forKey:self.cacheKey resident:_delegate != nil];
  671 +}
  672 +- (UIImage *)compressedImage {
  673 + return [self.imageCache imageForKey:self.cacheKey type:YBIBImageCacheTypeCompressed];
  674 +}
  675 +
  676 +- (void)setLoadingStatus:(YBIBImageLoadingStatus)loadingStatus {
  677 + // Ensure thread safety.
  678 + YBIB_DISPATCH_ASYNC_MAIN(^{
  679 + self->_loadingStatus = loadingStatus;
  680 + [self.delegate yb_imageData:self startLoadingWithStatus:loadingStatus];
  681 + });
  682 +}
  683 +
  684 +- (CGFloat)cuttingZoomScale {
  685 + if (_cuttingZoomScale >= 1) return _cuttingZoomScale;
  686 + _cuttingZoomScale = 1.1;
  687 + if (!self.originImage) return _cuttingZoomScale;
  688 + CGFloat imagePixel = self.originImage.size.width * self.originImage.size.height * self.originImage.scale * self.originImage.scale;
  689 + // The larger the image size, the larger the '_cuttingZoomScale', in order to reduce the burden of CPU and memory.
  690 + CGFloat scale = YBIBLowMemory() ? 0.28 : 0.19;
  691 + _cuttingZoomScale += (imagePixel / self.compressingSize * scale);
  692 + return _cuttingZoomScale;
  693 +}
  694 +
  695 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Image/YBIBImageLayout.h 0 → 100644
  1 +//
  2 +// YBIBImageLayout.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/12.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@protocol YBIBImageLayout <NSObject>
  14 +@required
  15 +
  16 +/**
  17 + 计算图片展示的位置
  18 +
  19 + @param containerSize 容器大小
  20 + @param imageSize 图片大小 (逻辑像素)
  21 + @param orientation 图片浏览器的方向
  22 + @return 图片展示的位置 (frame)
  23 + */
  24 +- (CGRect)yb_imageViewFrameWithContainerSize:(CGSize)containerSize imageSize:(CGSize)imageSize orientation:(UIDeviceOrientation)orientation;
  25 +
  26 +/**
  27 + 计算最大缩放比例
  28 +
  29 + @param containerSize 容器大小
  30 + @param imageSize 图片大小 (逻辑像素)
  31 + @param orientation 图片浏览器的方向
  32 + @return 最大缩放比例
  33 + */
  34 +- (CGFloat)yb_maximumZoomScaleWithContainerSize:(CGSize)containerSize imageSize:(CGSize)imageSize orientation:(UIDeviceOrientation)orientation;
  35 +
  36 +@end
  37 +
  38 +typedef NS_ENUM(NSUInteger, YBIBImageFillType) {
  39 + /// 宽度优先填充满
  40 + YBIBImageFillTypeFullWidth,
  41 + /// 完整显示
  42 + YBIBImageFillTypeCompletely
  43 +};
  44 +
  45 +@interface YBIBImageLayout : NSObject <YBIBImageLayout>
  46 +
  47 +/// 纵向的填充方式,默认 YBIBImageFillTypeCompletely
  48 +@property (nonatomic, assign) YBIBImageFillType verticalFillType;
  49 +
  50 +/// 横向的填充方式,默认 YBIBImageFillTypeFullWidth
  51 +@property (nonatomic, assign) YBIBImageFillType horizontalFillType;
  52 +
  53 +/// 最大缩放比例 (必须大于 1 才有效,若不指定内部会自动计算)
  54 +@property (nonatomic, assign) CGFloat maxZoomScale;
  55 +
  56 +/// 自动计算严格缩放比例后,再乘以这个值作为最终缩放比例,默认 1.5
  57 +@property (nonatomic, assign) CGFloat zoomScaleSurplus;
  58 +
  59 +@end
  60 +
  61 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Image/YBIBImageLayout.m 0 → 100644
  1 +//
  2 +// YBIBImageLayout.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/12.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBImageLayout.h"
  10 +
  11 +@implementation YBIBImageLayout
  12 +
  13 +#pragma mark - life cycle
  14 +
  15 +- (instancetype)init {
  16 + self = [super init];
  17 + if (self) {
  18 + _verticalFillType = YBIBImageFillTypeCompletely;
  19 + _horizontalFillType = YBIBImageFillTypeFullWidth;
  20 + _zoomScaleSurplus = 1.5;
  21 + }
  22 + return self;
  23 +}
  24 +
  25 +#pragma mark - private
  26 +
  27 +- (YBIBImageFillType)fillTypeByOrientation:(UIDeviceOrientation)orientation {
  28 + return UIDeviceOrientationIsLandscape(orientation) ? self.horizontalFillType : self.verticalFillType;
  29 +}
  30 +
  31 +#pragma mark - <YBIBImageLayout>
  32 +
  33 +- (CGRect)yb_imageViewFrameWithContainerSize:(CGSize)containerSize imageSize:(CGSize)imageSize orientation:(UIDeviceOrientation)orientation {
  34 + if (containerSize.width <= 0 || containerSize.height <= 0 || imageSize.width <= 0 || imageSize.height <= 0) return CGRectZero;
  35 +
  36 + CGFloat x = 0, y = 0, width = 0, height = 0;
  37 + switch ([self fillTypeByOrientation:orientation]) {
  38 + case YBIBImageFillTypeFullWidth: {
  39 + x = 0;
  40 + width = containerSize.width;
  41 + height = containerSize.width * (imageSize.height / imageSize.width);
  42 + if (imageSize.width / imageSize.height >= containerSize.width / containerSize.height)
  43 + y = (containerSize.height - height) / 2.0;
  44 + else
  45 + y = 0;
  46 + }
  47 + break;
  48 + case YBIBImageFillTypeCompletely: {
  49 + if (imageSize.width / imageSize.height >= containerSize.width / containerSize.height) {
  50 + width = containerSize.width;
  51 + height = containerSize.width * (imageSize.height / imageSize.width);
  52 + x = 0;
  53 + y = (containerSize.height - height) / 2.0;
  54 + } else {
  55 + height = containerSize.height;
  56 + width = containerSize.height * (imageSize.width / imageSize.height);
  57 + x = (containerSize.width - width) / 2.0;
  58 + y = 0;
  59 + }
  60 + }
  61 + break;
  62 + default: return CGRectZero;
  63 + }
  64 + return CGRectMake(x, y, width, height);
  65 +}
  66 +
  67 +- (CGFloat)yb_maximumZoomScaleWithContainerSize:(CGSize)containerSize imageSize:(CGSize)imageSize orientation:(UIDeviceOrientation)orientation {
  68 + if (self.maxZoomScale >= 1) return self.maxZoomScale;
  69 +
  70 + if (containerSize.width <= 0 || containerSize.height <= 0) return 0;
  71 +
  72 + CGFloat scale = [UIScreen mainScreen].scale;
  73 + if (scale <= 0) return 0;
  74 +
  75 + CGFloat widthScale = imageSize.width / scale / containerSize.width,
  76 + heightScale = imageSize.height / scale / containerSize.height,
  77 + maxScale = 1;
  78 + switch ([self fillTypeByOrientation:orientation]) {
  79 + case YBIBImageFillTypeFullWidth:
  80 + maxScale = widthScale;
  81 + break;
  82 + case YBIBImageFillTypeCompletely:
  83 + maxScale = MAX(widthScale, heightScale);
  84 + break;
  85 + default: return 0;
  86 + }
  87 + return MAX(maxScale, 1) * self.zoomScaleSurplus;
  88 +}
  89 +
  90 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Image/YBIBImageScrollView.h 0 → 100644
  1 +//
  2 +// YBIBImageScrollView.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/10.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBImage.h"
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +typedef NS_ENUM(NSInteger, YBIBScrollImageType) {
  14 + YBIBScrollImageTypeNone,
  15 + YBIBScrollImageTypeOriginal,
  16 + YBIBScrollImageTypeCompressed,
  17 + YBIBScrollImageTypeThumb
  18 +};
  19 +
  20 +@interface YBIBImageScrollView : UIScrollView
  21 +
  22 +- (void)setImage:(__kindof UIImage *)image type:(YBIBScrollImageType)type;
  23 +
  24 +@property (nonatomic, strong, readonly) YYAnimatedImageView *imageView;
  25 +
  26 +@property (nonatomic, assign) YBIBScrollImageType imageType;
  27 +
  28 +- (void)reset;
  29 +
  30 +@end
  31 +
  32 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Image/YBIBImageScrollView.m 0 → 100644
  1 +//
  2 +// YBIBImageScrollView.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/10.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBImageScrollView.h"
  10 +
  11 +@interface YBIBImageScrollView ()
  12 +@property (nonatomic, strong) YYAnimatedImageView *imageView;
  13 +@end
  14 +
  15 +@implementation YBIBImageScrollView
  16 +
  17 +#pragma mark - life cycle
  18 +
  19 +- (instancetype)initWithFrame:(CGRect)frame {
  20 + self = [super initWithFrame:frame];
  21 + if (self) {
  22 + self.showsHorizontalScrollIndicator = NO;
  23 + self.showsVerticalScrollIndicator = NO;
  24 + self.decelerationRate = UIScrollViewDecelerationRateFast;
  25 + self.maximumZoomScale = 1;
  26 + self.minimumZoomScale = 1;
  27 + self.alwaysBounceHorizontal = NO;
  28 + self.alwaysBounceVertical = NO;
  29 + self.layer.masksToBounds = NO;
  30 + if (@available(iOS 11.0, *)) {
  31 + self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
  32 + }
  33 +
  34 + [self addSubview:self.imageView];
  35 + }
  36 + return self;
  37 +}
  38 +
  39 +#pragma mark - public
  40 +
  41 +- (void)setImage:(__kindof UIImage *)image type:(YBIBScrollImageType)type {
  42 + self.imageView.image = image;
  43 + self.imageType = type;
  44 +}
  45 +
  46 +- (void)reset {
  47 + self.zoomScale = 1;
  48 + self.imageView.image = nil;
  49 + self.imageView.frame = CGRectZero;
  50 + self.imageType = YBIBScrollImageTypeNone;
  51 +}
  52 +
  53 +#pragma mark - getters
  54 +
  55 +- (YYAnimatedImageView *)imageView {
  56 + if (!_imageView) {
  57 + _imageView = [YYAnimatedImageView new];
  58 + _imageView.contentMode = UIViewContentModeScaleAspectFill;
  59 + _imageView.layer.masksToBounds = YES;
  60 + }
  61 + return _imageView;
  62 +}
  63 +
  64 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Image/YBIBInteractionProfile.h 0 → 100644
  1 +//
  2 +// YBIBInteractionProfile.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/30.
  6 +// Copyright © 2019 杨波. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@interface YBIBInteractionProfile : NSObject
  14 +
  15 +/// 是否取消手势交互动效
  16 +@property (nonatomic, assign) BOOL disable;
  17 +
  18 +/// 拖动的距离与最大高度的比例,达到这个比例就会出场
  19 +@property (nonatomic, assign) CGFloat dismissScale;
  20 +
  21 +/// 拖动的速度,达到这个值就会出场
  22 +@property (nonatomic, assign) CGFloat dismissVelocityY;
  23 +
  24 +/// 拖动动效复位时的时长
  25 +@property (nonatomic, assign) CGFloat restoreDuration;
  26 +
  27 +/// 拖动触发手势交互动效的起始距离
  28 +@property (nonatomic, assign) CGFloat triggerDistance;
  29 +
  30 +@end
  31 +
  32 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Image/YBIBInteractionProfile.m 0 → 100644
  1 +//
  2 +// YBIBInteractionProfile.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/30.
  6 +// Copyright © 2019 杨波. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBInteractionProfile.h"
  10 +
  11 +@implementation YBIBInteractionProfile
  12 +
  13 +- (instancetype)init {
  14 + self = [super init];
  15 + if (self) {
  16 + _disable = NO;
  17 + _dismissScale = 0.22;
  18 + _dismissVelocityY = 800;
  19 + _restoreDuration = 0.15;
  20 + _triggerDistance = 3;
  21 + }
  22 + return self;
  23 +}
  24 +
  25 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Image/YBImage.h 0 → 100644
  1 +//
  2 +// YBImage.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2018/8/31.
  6 +// Copyright © 2018年 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#if __has_include(<YYImage/YYImage.h>)
  10 +#import <YYImage/YYFrameImage.h>
  11 +#import <YYImage/YYSpriteSheetImage.h>
  12 +#import <YYImage/YYImageCoder.h>
  13 +#import <YYImage/YYAnimatedImageView.h>
  14 +#elif __has_include(<YYWebImage/YYImage.h>)
  15 +#import <YYWebImage/YYFrameImage.h>
  16 +#import <YYWebImage/YYSpriteSheetImage.h>
  17 +#import <YYWebImage/YYImageCoder.h>
  18 +#import <YYWebImage/YYAnimatedImageView.h>
  19 +#else
  20 +#import "YYFrameImage.h"
  21 +#import "YYSpriteSheetImage.h"
  22 +#import "YYImageCoder.h"
  23 +#import "YYAnimatedImageView.h"
  24 +#endif
  25 +
  26 +NS_ASSUME_NONNULL_BEGIN
  27 +
  28 +/// 🙄波儿菜:Decide whether should to decode by image size. ('imageSize': physical pixel)
  29 +typedef BOOL(^YBImageDecodeDecision)(CGSize imageSize, CGFloat scale);
  30 +
  31 +/**
  32 + It is a fully compatible `UIImage` subclass. It extends the UIImage
  33 + to support animated WebP, APNG and GIF format image data decoding. It also
  34 + support NSCoding protocol to archive and unarchive multi-frame image data.
  35 +
  36 + If the image is created from multi-frame image data, and you want to play the
  37 + animation, try replace UIImageView with `YYAnimatedImageView`.
  38 +
  39 + 🙄波儿菜:Copied from 'YYImage' and made some extensions.
  40 + */
  41 +
  42 +@interface YBImage : UIImage <YYAnimatedImage>
  43 +
  44 ++ (nullable __kindof UIImage *)imageNamed:(NSString *)name; // no cache!
  45 ++ (nullable YBImage *)imageWithContentsOfFile:(NSString *)path;
  46 ++ (nullable YBImage *)imageWithData:(NSData *)data;
  47 ++ (nullable YBImage *)imageWithData:(NSData *)data scale:(CGFloat)scale;
  48 +
  49 +/// 🙄波儿菜:Expand methodes.
  50 +/// Start ->
  51 ++ (nullable __kindof UIImage *)imageNamed:(NSString *)name decodeDecision:(nullable YBImageDecodeDecision)decodeDecision;
  52 ++ (nullable YBImage *)imageWithContentsOfFile:(NSString *)path decodeDecision:(nullable YBImageDecodeDecision)decodeDecision;
  53 ++ (nullable YBImage *)imageWithData:(NSData *)data decodeDecision:(nullable YBImageDecodeDecision)decodeDecision;
  54 ++ (nullable YBImage *)imageWithData:(NSData *)data scale:(CGFloat)scale decodeDecision:(nullable YBImageDecodeDecision)decodeDecision;
  55 +/// <- End
  56 +
  57 +/**
  58 + If the image is created from data or file, then the value indicates the data type.
  59 + */
  60 +@property (nonatomic, readonly) YYImageType animatedImageType;
  61 +
  62 +/**
  63 + If the image is created from animated image data (multi-frame GIF/APNG/WebP),
  64 + this property stores the original image data.
  65 + */
  66 +@property (nullable, nonatomic, readonly) NSData *animatedImageData;
  67 +
  68 +/**
  69 + The total memory usage (in bytes) if all frame images was loaded into memory.
  70 + The value is 0 if the image is not created from a multi-frame image data.
  71 + */
  72 +@property (nonatomic, readonly) NSUInteger animatedImageMemorySize;
  73 +
  74 +/**
  75 + Preload all frame image to memory.
  76 +
  77 + @discussion Set this property to `YES` will block the calling thread to decode
  78 + all animation frame image to memory, set to `NO` will release the preloaded frames.
  79 + If the image is shared by lots of image views (such as emoticon), preload all
  80 + frames will reduce the CPU cost.
  81 +
  82 + See `animatedImageMemorySize` for memory cost.
  83 + */
  84 +@property (nonatomic) BOOL preloadAllAnimatedImageFrames;
  85 +
  86 +
  87 +@end
  88 +
  89 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Image/YBImage.m 0 → 100644
  1 +//
  2 +// YBImage.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2018/8/31.
  6 +// Copyright © 2018年 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBImage.h"
  10 +#import "YBIBImageData.h"
  11 +#import "YBIBUtilities.h"
  12 +
  13 +/**
  14 + An array of NSNumber objects, shows the best order for path scale search.
  15 + e.g. iPhone3GS:@[@1,@2,@3] iPhone5:@[@2,@3,@1] iPhone6 Plus:@[@3,@2,@1]
  16 + */
  17 +static NSArray *_NSBundlePreferredScales() {
  18 + static NSArray *scales;
  19 + static dispatch_once_t onceToken;
  20 + dispatch_once(&onceToken, ^{
  21 + CGFloat screenScale = [UIScreen mainScreen].scale;
  22 + if (screenScale <= 1) {
  23 + scales = @[@1,@2,@3];
  24 + } else if (screenScale <= 2) {
  25 + scales = @[@2,@3,@1];
  26 + } else {
  27 + scales = @[@3,@2,@1];
  28 + }
  29 + });
  30 + return scales;
  31 +}
  32 +
  33 +/**
  34 + Add scale modifier to the file name (without path extension),
  35 + From @"name" to @"name@2x".
  36 +
  37 + e.g.
  38 + <table>
  39 + <tr><th>Before </th><th>After(scale:2)</th></tr>
  40 + <tr><td>"icon" </td><td>"icon@2x" </td></tr>
  41 + <tr><td>"icon " </td><td>"icon @2x" </td></tr>
  42 + <tr><td>"icon.top" </td><td>"icon.top@2x" </td></tr>
  43 + <tr><td>"/p/name" </td><td>"/p/name@2x" </td></tr>
  44 + <tr><td>"/path/" </td><td>"/path/" </td></tr>
  45 + </table>
  46 +
  47 + @param scale Resource scale.
  48 + @return String by add scale modifier, or just return if it's not end with file name.
  49 + */
  50 +static NSString *_NSStringByAppendingNameScale(NSString *string, CGFloat scale) {
  51 + if (!string) return nil;
  52 + if (fabs(scale - 1) <= __FLT_EPSILON__ || string.length == 0 || [string hasSuffix:@"/"]) return string.copy;
  53 + return [string stringByAppendingFormat:@"@%@x", @(scale)];
  54 +}
  55 +
  56 +/**
  57 + Return the path scale.
  58 +
  59 + e.g.
  60 + <table>
  61 + <tr><th>Path </th><th>Scale </th></tr>
  62 + <tr><td>"icon.png" </td><td>1 </td></tr>
  63 + <tr><td>"icon@2x.png" </td><td>2 </td></tr>
  64 + <tr><td>"icon@2.5x.png" </td><td>2.5 </td></tr>
  65 + <tr><td>"icon@2x" </td><td>1 </td></tr>
  66 + <tr><td>"icon@2x..png" </td><td>1 </td></tr>
  67 + <tr><td>"icon@2x.png/" </td><td>1 </td></tr>
  68 + </table>
  69 + */
  70 +static CGFloat _NSStringPathScale(NSString *string) {
  71 + if (string.length == 0 || [string hasSuffix:@"/"]) return 1;
  72 + NSString *name = string.stringByDeletingPathExtension;
  73 + __block CGFloat scale = 1;
  74 +
  75 + NSRegularExpression *pattern = [NSRegularExpression regularExpressionWithPattern:@"@[0-9]+\\.?[0-9]*x$" options:NSRegularExpressionAnchorsMatchLines error:nil];
  76 + [pattern enumerateMatchesInString:name options:kNilOptions range:NSMakeRange(0, name.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
  77 + if (result.range.location >= 3) {
  78 + scale = [string substringWithRange:NSMakeRange(result.range.location + 1, result.range.length - 2)].doubleValue;
  79 + }
  80 + }];
  81 +
  82 + return scale;
  83 +}
  84 +
  85 +
  86 +@implementation YBImage {
  87 + YYImageDecoder *_decoder;
  88 + NSArray *_preloadedFrames;
  89 + dispatch_semaphore_t _preloadedLock;
  90 + NSUInteger _bytesPerFrame;
  91 +}
  92 +
  93 ++ (__kindof UIImage *)imageNamed:(NSString *)name {
  94 + return [self imageNamed:name decodeDecision:nil];
  95 +}
  96 ++ (__kindof UIImage *)imageNamed:(NSString *)name decodeDecision:(nullable YBImageDecodeDecision)decodeDecision {
  97 + if (name.length == 0) return nil;
  98 + if ([name hasSuffix:@"/"]) return nil;
  99 +
  100 + NSString *res = name.stringByDeletingPathExtension;
  101 + NSString *ext = name.pathExtension;
  102 + NSString *path = nil;
  103 + CGFloat scale = 1;
  104 +
  105 + // If no extension, guess by system supported (same as UIImage).
  106 + NSArray *exts = ext.length > 0 ? @[ext] : @[@"", @"png", @"jpeg", @"jpg", @"gif", @"webp", @"apng"];
  107 + NSArray *scales = _NSBundlePreferredScales();
  108 + for (int s = 0; s < scales.count; s++) {
  109 + scale = ((NSNumber *)scales[s]).floatValue;
  110 + NSString *scaledName = _NSStringByAppendingNameScale(res, scale);
  111 + for (NSString *e in exts) {
  112 + path = [[NSBundle mainBundle] pathForResource:scaledName ofType:e];
  113 + if (path) break;
  114 + }
  115 + if (path) break;
  116 + }
  117 + if (path.length == 0) {
  118 + // 🙄波儿菜:Assets.xcassets supported.
  119 + return [super imageNamed:name];
  120 + }
  121 +
  122 + NSData *data = [NSData dataWithContentsOfFile:path];
  123 + if (data.length == 0) return nil;
  124 +
  125 + return [[self alloc] initWithData:data scale:scale decodeDecision:decodeDecision];
  126 +}
  127 +
  128 ++ (YBImage *)imageWithContentsOfFile:(NSString *)path {
  129 + return [self imageWithContentsOfFile:path decodeDecision:nil];
  130 +}
  131 ++ (YBImage *)imageWithContentsOfFile:(NSString *)path decodeDecision:(nullable YBImageDecodeDecision)decodeDecision {
  132 + return [[self alloc] initWithContentsOfFile:path decodeDecision:decodeDecision];
  133 +}
  134 +
  135 ++ (YBImage *)imageWithData:(NSData *)data {
  136 + return [self imageWithData:data decodeDecision:nil];
  137 +}
  138 ++ (YBImage *)imageWithData:(NSData *)data decodeDecision:(nullable YBImageDecodeDecision)decodeDecision {
  139 + return [[self alloc] initWithData:data decodeDecision:decodeDecision];
  140 +}
  141 +
  142 ++ (YBImage *)imageWithData:(NSData *)data scale:(CGFloat)scale {
  143 + return [self imageWithData:data scale:scale decodeDecision:nil];
  144 +}
  145 ++ (YBImage *)imageWithData:(NSData *)data scale:(CGFloat)scale decodeDecision:(nullable YBImageDecodeDecision)decodeDecision {
  146 + return [[self alloc] initWithData:data scale:scale decodeDecision:decodeDecision];
  147 +}
  148 +
  149 +- (instancetype)initWithContentsOfFile:(NSString *)path {
  150 + return [self initWithContentsOfFile:path decodeDecision:nil];
  151 +}
  152 +- (instancetype)initWithContentsOfFile:(NSString *)path decodeDecision:(nullable YBImageDecodeDecision)decodeDecision {
  153 + NSData *data = [NSData dataWithContentsOfFile:path];
  154 + return [self initWithData:data scale:_NSStringPathScale(path) decodeDecision:decodeDecision];
  155 +}
  156 +
  157 +- (instancetype)initWithData:(NSData *)data {
  158 + return [self initWithData:data decodeDecision:nil];
  159 +}
  160 +- (instancetype)initWithData:(NSData *)data decodeDecision:(nullable YBImageDecodeDecision)decodeDecision {
  161 + return [self initWithData:data scale:1 decodeDecision:decodeDecision];
  162 +}
  163 +
  164 +- (instancetype)initWithData:(NSData *)data scale:(CGFloat)scale {
  165 + return [self initWithData:data scale:scale decodeDecision:nil];
  166 +}
  167 +- (instancetype)initWithData:(NSData *)data scale:(CGFloat)scale decodeDecision:(nullable YBImageDecodeDecision)decodeDecision {
  168 + if (data.length == 0) return nil;
  169 + if (scale <= 0) scale = [UIScreen mainScreen].scale;
  170 + _preloadedLock = dispatch_semaphore_create(1);
  171 + @autoreleasepool {
  172 + YYImageDecoder *decoder = [YYImageDecoder decoderWithData:data scale:scale];
  173 +
  174 + // 🙄波儿菜:Determine whether should to decode.
  175 + BOOL decodeForDisplay = YES;
  176 + if (decodeDecision) {
  177 + decodeForDisplay = decodeDecision(CGSizeMake(decoder.width, decoder.height), decoder.scale ?: 1);
  178 + }
  179 +
  180 + YYImageFrame *frame = [decoder frameAtIndex:0 decodeForDisplay:decodeForDisplay];
  181 + UIImage *image = frame.image;
  182 + if (!image) return nil;
  183 + self = [self initWithCGImage:image.CGImage scale:decoder.scale orientation:image.imageOrientation];
  184 + if (!self) return nil;
  185 + _animatedImageType = decoder.type;
  186 + if (decoder.frameCount > 1) {
  187 + _decoder = decoder;
  188 + _bytesPerFrame = CGImageGetBytesPerRow(image.CGImage) * CGImageGetHeight(image.CGImage);
  189 + _animatedImageMemorySize = _bytesPerFrame * decoder.frameCount;
  190 + }
  191 + self.isDecodedForDisplay = YES;
  192 + }
  193 + return self;
  194 +}
  195 +
  196 +- (NSData *)animatedImageData {
  197 + return _decoder.data;
  198 +}
  199 +
  200 +- (void)setPreloadAllAnimatedImageFrames:(BOOL)preloadAllAnimatedImageFrames {
  201 + if (_preloadAllAnimatedImageFrames != preloadAllAnimatedImageFrames) {
  202 + if (preloadAllAnimatedImageFrames && _decoder.frameCount > 0) {
  203 + NSMutableArray *frames = [NSMutableArray new];
  204 + for (NSUInteger i = 0, max = _decoder.frameCount; i < max; i++) {
  205 + UIImage *img = [self animatedImageFrameAtIndex:i];
  206 + if (img) {
  207 + [frames addObject:img];
  208 + } else {
  209 + [frames addObject:[NSNull null]];
  210 + }
  211 + }
  212 + dispatch_semaphore_wait(_preloadedLock, DISPATCH_TIME_FOREVER);
  213 + _preloadedFrames = frames;
  214 + dispatch_semaphore_signal(_preloadedLock);
  215 + } else {
  216 + dispatch_semaphore_wait(_preloadedLock, DISPATCH_TIME_FOREVER);
  217 + _preloadedFrames = nil;
  218 + dispatch_semaphore_signal(_preloadedLock);
  219 + }
  220 + }
  221 +}
  222 +
  223 +#pragma mark - protocol NSCoding
  224 +
  225 +- (instancetype)initWithCoder:(NSCoder *)aDecoder {
  226 + NSNumber *scale = [aDecoder decodeObjectForKey:@"YYImageScale"];
  227 + NSData *data = [aDecoder decodeObjectForKey:@"YYImageData"];
  228 + if (data.length) {
  229 + self = [self initWithData:data scale:scale.doubleValue];
  230 + } else {
  231 + self = [super initWithCoder:aDecoder];
  232 + }
  233 + return self;
  234 +}
  235 +
  236 +- (void)encodeWithCoder:(NSCoder *)aCoder {
  237 + if (_decoder.data.length) {
  238 + [aCoder encodeObject:@(self.scale) forKey:@"YYImageScale"];
  239 + [aCoder encodeObject:_decoder.data forKey:@"YYImageData"];
  240 + } else {
  241 + [super encodeWithCoder:aCoder]; // Apple use UIImagePNGRepresentation() to encode UIImage.
  242 + }
  243 +}
  244 +
  245 +#pragma mark - protocol YYAnimatedImage
  246 +
  247 +- (NSUInteger)animatedImageFrameCount {
  248 + return _decoder.frameCount;
  249 +}
  250 +
  251 +- (NSUInteger)animatedImageLoopCount {
  252 + return _decoder.loopCount;
  253 +}
  254 +
  255 +- (NSUInteger)animatedImageBytesPerFrame {
  256 + return _bytesPerFrame;
  257 +}
  258 +
  259 +- (UIImage *)animatedImageFrameAtIndex:(NSUInteger)index {
  260 + if (index >= _decoder.frameCount) return nil;
  261 + dispatch_semaphore_wait(_preloadedLock, DISPATCH_TIME_FOREVER);
  262 + UIImage *image = _preloadedFrames[index];
  263 + dispatch_semaphore_signal(_preloadedLock);
  264 + if (image) return image == (id)[NSNull null] ? nil : image;
  265 + return [_decoder frameAtIndex:index decodeForDisplay:YES].image;
  266 +}
  267 +
  268 +- (NSTimeInterval)animatedImageDurationAtIndex:(NSUInteger)index {
  269 + NSTimeInterval duration = [_decoder frameDurationAtIndex:index];
  270 +
  271 + /*
  272 + http://opensource.apple.com/source/WebCore/WebCore-7600.1.25/platform/graphics/cg/ImageSourceCG.cpp
  273 + Many annoying ads specify a 0 duration to make an image flash as quickly as
  274 + possible. We follow Safari and Firefox's behavior and use a duration of 100 ms
  275 + for any frames that specify a duration of <= 10 ms.
  276 + See <rdar://problem/7689300> and <http://webkit.org/b/36082> for more information.
  277 +
  278 + See also: http://nullsleep.tumblr.com/post/16524517190/animated-gif-minimum-frame-delay-browser.
  279 + */
  280 + if (duration < 0.011f) return 0.100f;
  281 + return duration;
  282 +}
  283 +
  284 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Protocol/YBIBCellProtocol.h 0 → 100644
  1 +//
  2 +// YBIBCellProtocol.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/5.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBGetBaseInfoProtocol.h"
  10 +#import "YBIBOrientationReceiveProtocol.h"
  11 +#import "YBIBOperateBrowserProtocol.h"
  12 +
  13 +@protocol YBIBDataProtocol;
  14 +
  15 +NS_ASSUME_NONNULL_BEGIN
  16 +
  17 +@protocol YBIBCellProtocol <YBIBGetBaseInfoProtocol, YBIBOperateBrowserProtocol, YBIBOrientationReceiveProtocol>
  18 +
  19 +@required
  20 +
  21 +/// Cell 对应的 Data
  22 +@property (nonatomic, strong) id<YBIBDataProtocol> yb_cellData;
  23 +
  24 +@optional
  25 +
  26 +/**
  27 + 获取前景视图,出入场时需要用这个返回值做动效
  28 +
  29 + @return 前景视图
  30 + */
  31 +- (__kindof UIView *)yb_foregroundView;
  32 +
  33 +/**
  34 + 页码变化了
  35 + */
  36 +- (void)yb_pageChanged;
  37 +
  38 +/// 当前 Cell 的页码
  39 +@property (nonatomic, copy) NSInteger(^yb_selfPage)(void);
  40 +
  41 +@end
  42 +
  43 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Protocol/YBIBDataProtocol.h 0 → 100644
  1 +//
  2 +// YBIBDataProtocol.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/5.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBGetBaseInfoProtocol.h"
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@protocol YBIBDataProtocol <YBIBGetBaseInfoProtocol>
  14 +
  15 +@required
  16 +
  17 +/**
  18 + 当前 Data 对应 Cell 的类类型
  19 +
  20 + @return Class 类型
  21 + */
  22 +- (Class)yb_classOfCell;
  23 +
  24 +@optional
  25 +
  26 +/**
  27 + 获取投影视图,当前数据模型对应外界业务的 UIView (通常为 UIImageView),做转场动效用
  28 +
  29 + 这个方法会在做出入场动效时调用,若未实现时将无法进行平滑的入场
  30 +
  31 + @return 投影视图
  32 + */
  33 +- (__kindof UIView *)yb_projectiveView;
  34 +
  35 +/**
  36 + 通过一系列数据,计算并返回图片视图在容器中的 frame
  37 +
  38 + 这个方法会在做入场动效时调用,若未实现时将无法进行平滑的入场
  39 +
  40 + @param containerSize 容器大小
  41 + @param imageSize 图片大小 (逻辑像素)
  42 + @param orientation 图片浏览器的方向
  43 + @return 计算好的 frame
  44 + */
  45 +- (CGRect)yb_imageViewFrameWithContainerSize:(CGSize)containerSize imageSize:(CGSize)imageSize orientation:(UIDeviceOrientation)orientation;
  46 +
  47 +/**
  48 + 预加载数据,有效的预加载能提高性能,请注意管理内存
  49 + */
  50 +- (void)yb_preload;
  51 +
  52 +/**
  53 + 保存到相册
  54 + */
  55 +- (void)yb_saveToPhotoAlbum;
  56 +
  57 +/**
  58 + 是否允许保存到相册
  59 + */
  60 +- (BOOL)yb_allowSaveToPhotoAlbum;
  61 +
  62 +@end
  63 +
  64 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Protocol/YBIBGetBaseInfoProtocol.h 0 → 100644
  1 +//
  2 +// YBIBGetBaseInfoProtocol.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/23.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +#import "YBIBAuxiliaryViewHandler.h"
  11 +#import "YBIBWebImageMediator.h"
  12 +
  13 +NS_ASSUME_NONNULL_BEGIN
  14 +
  15 +@protocol YBIBGetBaseInfoProtocol <NSObject>
  16 +
  17 +@optional
  18 +
  19 +/// 当前的方向
  20 +@property (nonatomic, copy) UIDeviceOrientation(^yb_currentOrientation)(void);
  21 +
  22 +/// 根据方向获取容器大小
  23 +@property (nonatomic, copy) CGSize(^yb_containerSize)(UIDeviceOrientation orientation);
  24 +
  25 +/// 容器视图 (可在上面添加子视图)
  26 +@property (nonatomic, weak) UIView *yb_containerView;
  27 +
  28 +/// 辅助视图处理器
  29 +@property (nonatomic, copy) id<YBIBAuxiliaryViewHandler>(^yb_auxiliaryViewHandler)(void);
  30 +
  31 +/// 图片下载缓存相关中介者
  32 +@property (nonatomic, copy) id<YBIBWebImageMediator>(^yb_webImageMediator)(void);
  33 +
  34 +/// 当前页码
  35 +@property (nonatomic, copy) NSInteger(^yb_currentPage)(void);
  36 +
  37 +/// 总页码数量
  38 +@property (nonatomic, copy) NSInteger(^yb_totalPage)(void);
  39 +
  40 +/// 判断当前展示的 cell 是否恰好在屏幕中间
  41 +@property (nonatomic, copy) BOOL(^yb_cellIsInCenter)(void);
  42 +
  43 +/// 是否正在转场
  44 +@property (nonatomic, copy) BOOL(^yb_isTransitioning)(void);
  45 +/// 是否正在展示过程转场
  46 +@property (nonatomic, copy) BOOL(^yb_isShowTransitioning)(void);
  47 +/// 是否正在隐藏过程转场
  48 +@property (nonatomic, copy) BOOL(^yb_isHideTransitioning)(void);
  49 +
  50 +/// 是否正在旋转
  51 +@property (nonatomic, copy) BOOL(^yb_isRotating)(void);
  52 +
  53 +/// 背景视图 (也就是 YBImageBrower 对象,不可在上面添加子视图。作用:一是可以更改透明度和颜色,入场和出场动效有用;二是可以用来比较内存指针,在做不同实例差异化功能时可能有用,虽然不提倡这么做)
  54 +@property (nonatomic, weak) __kindof UIView *yb_backView;
  55 +
  56 +/// 集合视图
  57 +@property (nonatomic, copy) __kindof UICollectionView *(^yb_collectionView)(void);
  58 +
  59 +@end
  60 +
  61 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Protocol/YBIBOperateBrowserProtocol.h 0 → 100644
  1 +//
  2 +// YBIBOperateBrowserProtocol.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/7/8.
  6 +// Copyright © 2019 杨波. All rights reserved.
  7 +//
  8 +
  9 +#import <Foundation/Foundation.h>
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@protocol YBIBOperateBrowserProtocol <NSObject>
  14 +
  15 +@optional
  16 +
  17 +/// 隐藏图片浏览器
  18 +@property (nonatomic, copy) void(^yb_hideBrowser)(void);
  19 +
  20 +/// 是否隐藏状态栏
  21 +@property (nonatomic, copy) void(^yb_hideStatusBar)(BOOL);
  22 +
  23 +/// 是否隐藏工具视图
  24 +@property (nonatomic, copy) void(^yb_hideToolViews)(BOOL);
  25 +
  26 +@end
  27 +
  28 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Protocol/YBIBOrientationReceiveProtocol.h 0 → 100644
  1 +//
  2 +// YBIBOrientationReceiveProtocol.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/7/8.
  6 +// Copyright © 2019 杨波. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@protocol YBIBOrientationReceiveProtocol <NSObject>
  14 +
  15 +@optional
  16 +
  17 +/**
  18 + 图片浏览器的方向将要变化
  19 +
  20 + @param orientation 期望的方向
  21 + */
  22 +- (void)yb_orientationWillChangeWithExpectOrientation:(UIDeviceOrientation)orientation;
  23 +
  24 +/**
  25 + 图片浏览器的方向变化动效调用,实现的变化会自动转换为动画
  26 +
  27 + @param orientation 期望的方向
  28 + */
  29 +- (void)yb_orientationChangeAnimationWithExpectOrientation:(UIDeviceOrientation)orientation;
  30 +
  31 +/**
  32 + 图片浏览器的方向已经变化
  33 +
  34 + @param orientation 当前的方向
  35 + */
  36 +- (void)yb_orientationDidChangedWithOrientation:(UIDeviceOrientation)orientation;
  37 +
  38 +@end
  39 +
  40 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Protocol/YBImageBrowserDataSource.h 0 → 100644
  1 +//
  2 +// YBImageBrowserDataSource.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2018/8/25.
  6 +// Copyright © 2018年 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBDataProtocol.h"
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@class YBImageBrowser;
  14 +
  15 +@protocol YBImageBrowserDataSource <NSObject>
  16 +
  17 +@required
  18 +
  19 +/**
  20 + 返回数据源数量
  21 +
  22 + @param imageBrowser 图片浏览器
  23 + @return 数量
  24 + */
  25 +- (NSInteger)yb_numberOfCellsInImageBrowser:(YBImageBrowser *)imageBrowser;
  26 +
  27 +/**
  28 + 返回当前下标对应的数据
  29 +
  30 + @param imageBrowser 图片浏览器
  31 + @param index 当前下标
  32 + @return 数据
  33 + */
  34 +- (id<YBIBDataProtocol>)yb_imageBrowser:(YBImageBrowser *)imageBrowser dataForCellAtIndex:(NSInteger)index;
  35 +
  36 +@end
  37 +
  38 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/Protocol/YBImageBrowserDelegate.h 0 → 100644
  1 +//
  2 +// YBImageBrowserDelegate.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/9.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBDataProtocol.h"
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@class YBImageBrowser;
  14 +
  15 +@protocol YBImageBrowserDelegate <NSObject>
  16 +
  17 +@optional
  18 +
  19 +/**
  20 + 页码变化
  21 +
  22 + @param imageBrowser 图片浏览器
  23 + @param page 当前页码
  24 + @param data 数据
  25 + */
  26 +- (void)yb_imageBrowser:(YBImageBrowser *)imageBrowser pageChanged:(NSInteger)page data:(id<YBIBDataProtocol>)data;
  27 +
  28 +/**
  29 + 响应长按手势(若实现该方法将阻止其它地方捕获到长按事件)
  30 +
  31 + @param imageBrowser 图片浏览器
  32 + @param data 数据
  33 + */
  34 +- (void)yb_imageBrowser:(YBImageBrowser *)imageBrowser respondsToLongPressWithData:(id<YBIBDataProtocol>)data;
  35 +
  36 +/**
  37 + 开始转场
  38 +
  39 + @param imageBrowser 图片浏览器
  40 + @param isShow YES 表示入场,NO 表示出场
  41 + */
  42 +- (void)yb_imageBrowser:(YBImageBrowser *)imageBrowser beginTransitioningWithIsShow:(BOOL)isShow;
  43 +
  44 +/**
  45 + 结束转场
  46 +
  47 + @param imageBrowser 图片浏览器
  48 + @param isShow YES 表示入场,NO 表示出场
  49 + */
  50 +- (void)yb_imageBrowser:(YBImageBrowser *)imageBrowser endTransitioningWithIsShow:(BOOL)isShow;
  51 +
  52 +@end
  53 +
  54 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/ToolView/YBIBSheetView.h 0 → 100644
  1 +//
  2 +// YBIBSheetView.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/7/6.
  6 +// Copyright © 2019 杨波. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@protocol YBIBDataProtocol;
  14 +
  15 +typedef void(^YBIBSheetActionBlock)(id<YBIBDataProtocol> data);
  16 +
  17 +@interface YBIBSheetAction : NSObject
  18 +
  19 +/// 显示的名字
  20 +@property (nonatomic, copy) NSString *name;
  21 +
  22 +/// 点击回调闭包
  23 +@property (nonatomic, copy, nullable) YBIBSheetActionBlock action;
  24 +
  25 ++ (instancetype)actionWithName:(NSString *)name action:(_Nullable YBIBSheetActionBlock)action;
  26 +
  27 +@end
  28 +
  29 +
  30 +@interface YBIBSheetView : UIView
  31 +
  32 +/// 数据源 (可自定义添加)
  33 +@property (nonatomic, strong) NSMutableArray<YBIBSheetAction *> *actions;
  34 +
  35 +/// 列表 Cell 的高度
  36 +@property (nonatomic, assign) CGFloat cellHeight;
  37 +
  38 +/// 列表最大高度与容器高度的比例
  39 +@property (nonatomic, assign) CGFloat maxHeightScale;
  40 +
  41 +/// 取消的文本
  42 +@property (nonatomic, copy) NSString *cancelText;
  43 +
  44 +/// 显示动画持续时间
  45 +@property (nonatomic, assign) NSTimeInterval showDuration;
  46 +
  47 +/// 隐藏动画持续时间
  48 +@property (nonatomic, assign) NSTimeInterval hideDuration;
  49 +
  50 +/// 背景透明度
  51 +@property (nonatomic, assign) CGFloat backAlpha;
  52 +
  53 +/**
  54 + 展示
  55 +
  56 + @param view 指定父视图
  57 + @param orientation 当前方向
  58 + */
  59 +- (void)showToView:(UIView *)view orientation:(UIDeviceOrientation)orientation;
  60 +
  61 +/**
  62 + 隐藏
  63 +
  64 + @param animation 是否带动画
  65 + */
  66 +- (void)hideWithAnimation:(BOOL)animation;
  67 +
  68 +/// 获取当前数据的闭包
  69 +@property (nonatomic, copy) id<YBIBDataProtocol>(^currentdata)(void);
  70 +
  71 +@end
  72 +
  73 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/ToolView/YBIBSheetView.m 0 → 100644
  1 +//
  2 +// YBIBSheetView.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/7/6.
  6 +// Copyright © 2019 杨波. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBSheetView.h"
  10 +#import "YBIBUtilities.h"
  11 +#import "YBIBCopywriter.h"
  12 +
  13 +
  14 +@implementation YBIBSheetAction
  15 ++ (instancetype)actionWithName:(NSString *)name action:(YBIBSheetActionBlock)action {
  16 + YBIBSheetAction *sheetAction = [YBIBSheetAction new];
  17 + sheetAction.name = name;
  18 + sheetAction.action = action;
  19 + return sheetAction;
  20 +}
  21 +@end
  22 +
  23 +
  24 +@interface YBIBSheetCell : UITableViewCell
  25 +@property (nonatomic, strong) UILabel *titleLabel;
  26 +@property (nonatomic, strong) CALayer *line;
  27 +@end
  28 +@implementation YBIBSheetCell
  29 +- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
  30 + if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
  31 + self.selectionStyle = UITableViewCellSelectionStyleNone;
  32 + _titleLabel = [UILabel new];
  33 + _titleLabel.textColor = UIColor.darkTextColor;
  34 + _titleLabel.font = [UIFont fontWithName:@"Avenir-Medium" size:16];
  35 + _titleLabel.textAlignment = NSTextAlignmentCenter;
  36 + _line = [CALayer new];
  37 + _line.backgroundColor = UIColor.groupTableViewBackgroundColor.CGColor;
  38 + [self.contentView addSubview:_titleLabel];
  39 + [self.contentView.layer addSublayer:_line];
  40 + }
  41 + return self;
  42 +}
  43 +- (void)layoutSubviews {
  44 + [super layoutSubviews];
  45 + CGFloat width = self.contentView.bounds.size.width, height = self.contentView.bounds.size.height;
  46 + CGFloat lineHeight = 0.5;
  47 + _line.frame = CGRectMake(0, height - lineHeight, width, lineHeight);
  48 + CGFloat offset = 15;
  49 + _titleLabel.frame = CGRectMake(offset, 0, width - offset * 2, height);
  50 +}
  51 +@end
  52 +
  53 +
  54 +static CGFloat kOffsetSpace = 5;
  55 +
  56 +@interface YBIBSheetView () <UITableViewDelegate, UITableViewDataSource>
  57 +@property (nonatomic, strong) UITableView *tableView;
  58 +@end
  59 +
  60 +@implementation YBIBSheetView {
  61 + CGRect _tableShowFrame;
  62 + CGRect _tableHideFrame;
  63 +}
  64 +
  65 +#pragma mark - life cycle
  66 +
  67 +- (instancetype)initWithFrame:(CGRect)frame {
  68 + self = [super initWithFrame:frame];
  69 + if (self) {
  70 + _cancelText = [YBIBCopywriter sharedCopywriter].cancel;
  71 + _maxHeightScale = 0.7;
  72 + _showDuration = 0.2;
  73 + _hideDuration = 0.1;
  74 + _cellHeight = 50;
  75 + _backAlpha = 0.3;
  76 + _actions = [NSMutableArray array];
  77 +
  78 + [self addSubview:self.tableView];
  79 + }
  80 + return self;
  81 +}
  82 +
  83 +#pragma mark - public
  84 +
  85 +- (void)showToView:(UIView *)view orientation:(UIDeviceOrientation)orientation {
  86 + if (self.actions.count == 0) return;
  87 +
  88 + [view addSubview:self];
  89 + self.frame = view.bounds;
  90 +
  91 + UIEdgeInsets padding = YBIBPaddingByBrowserOrientation(orientation);
  92 +
  93 + CGFloat footerHeight = padding.bottom;
  94 + CGFloat tableHeight = self.cellHeight * (self.actions.count + 1) + kOffsetSpace + footerHeight;
  95 +
  96 + _tableShowFrame = self.frame;
  97 + _tableShowFrame.size.height = MIN(self.maxHeightScale * self.bounds.size.height, tableHeight);
  98 + _tableShowFrame.origin.y = self.bounds.size.height - _tableShowFrame.size.height;
  99 +
  100 + _tableHideFrame = _tableShowFrame;
  101 + _tableHideFrame.origin.y = self.bounds.size.height;
  102 +
  103 + self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0];
  104 + self.tableView.frame = _tableHideFrame;
  105 + self.tableView.tableFooterView.bounds = CGRectMake(0, 0, self.tableView.frame.size.width, footerHeight);
  106 + [UIView animateWithDuration:self.showDuration animations:^{
  107 + self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:self->_backAlpha];
  108 + self.tableView.frame = self->_tableShowFrame;
  109 + }];
  110 +}
  111 +
  112 +- (void)hideWithAnimation:(BOOL)animation {
  113 + if (!self.superview) return;
  114 +
  115 + void(^animationsBlock)(void) = ^{
  116 + self.tableView.frame = self->_tableHideFrame;
  117 + self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0];
  118 + };
  119 + void(^completionBlock)(BOOL n) = ^(BOOL n){
  120 + [self removeFromSuperview];
  121 + };
  122 + if (animation) {
  123 + [UIView animateWithDuration:self.hideDuration animations:animationsBlock completion:completionBlock];
  124 + } else {
  125 + animationsBlock();
  126 + completionBlock(NO);
  127 + }
  128 +}
  129 +
  130 +#pragma mark - touch
  131 +
  132 +- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  133 + CGPoint point = [touches.anyObject locationInView:self];
  134 + if (!CGRectContainsPoint(self.tableView.frame, point)) {
  135 + [self hideWithAnimation:YES];
  136 + }
  137 +}
  138 +
  139 +#pragma mark - <UITableViewDataSource>
  140 +
  141 +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
  142 + return 2;
  143 +}
  144 +
  145 +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  146 + return section == 0 ? self.actions.count : 1;
  147 +}
  148 +
  149 +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
  150 + return self.cellHeight;
  151 +}
  152 +
  153 +- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
  154 + return section == 0 ? CGFLOAT_MIN : kOffsetSpace;
  155 +}
  156 +
  157 +- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
  158 + return CGFLOAT_MIN;
  159 +}
  160 +
  161 +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  162 + YBIBSheetCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass(YBIBSheetCell.self)];
  163 + if (indexPath.section == 0) {
  164 + cell.line.hidden = NO;
  165 + YBIBSheetAction *action = self.actions[indexPath.row];
  166 + cell.titleLabel.text = action.name;
  167 + } else {
  168 + cell.line.hidden = YES;
  169 + cell.titleLabel.text = self.cancelText;
  170 + }
  171 + return cell;
  172 +}
  173 +
  174 +#pragma mark - <UITableViewDelegate>
  175 +
  176 +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  177 + if (indexPath.section == 0) {
  178 + YBIBSheetAction *action = self.actions[indexPath.row];
  179 + if (action.action) action.action(self.currentdata());
  180 + } else {
  181 + [self hideWithAnimation:YES];
  182 + }
  183 +}
  184 +
  185 +#pragma mark - getters
  186 +
  187 +- (UITableView *)tableView {
  188 + if (!_tableView) {
  189 + _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
  190 + _tableView.delegate = self;
  191 + _tableView.dataSource = self;
  192 + _tableView.estimatedRowHeight = 44;
  193 + _tableView.estimatedSectionFooterHeight = 0;
  194 + _tableView.estimatedSectionHeaderHeight = 0;
  195 + _tableView.backgroundColor = [UIColor clearColor];
  196 + _tableView.alwaysBounceVertical = NO;
  197 + _tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  198 + if (@available(iOS 11.0, *)) {
  199 + _tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
  200 + }
  201 + UIView *footer = [UIView new];
  202 + footer.backgroundColor = UIColor.whiteColor;
  203 + _tableView.tableFooterView = footer;
  204 + [_tableView registerClass:YBIBSheetCell.self forCellReuseIdentifier:NSStringFromClass(YBIBSheetCell.self)];
  205 + }
  206 + return _tableView;
  207 +}
  208 +
  209 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/ToolView/YBIBToolViewHandler.h 0 → 100644
  1 +//
  2 +// YBIBToolViewHandler.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/7/7.
  6 +// Copyright © 2019 杨波. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBSheetView.h"
  10 +#import "YBIBTopView.h"
  11 +#import "YBIBDataProtocol.h"
  12 +#import "YBIBOrientationReceiveProtocol.h"
  13 +#import "YBIBOperateBrowserProtocol.h"
  14 +
  15 +NS_ASSUME_NONNULL_BEGIN
  16 +
  17 +@protocol YBIBToolViewHandler <YBIBGetBaseInfoProtocol, YBIBOperateBrowserProtocol, YBIBOrientationReceiveProtocol>
  18 +
  19 +@required
  20 +
  21 +/**
  22 + 容器视图准备好了,可进行子视图的添加和布局
  23 + */
  24 +- (void)yb_containerViewIsReadied;
  25 +
  26 +/**
  27 + 隐藏视图
  28 +
  29 + @param hide 是否隐藏
  30 + */
  31 +- (void)yb_hide:(BOOL)hide;
  32 +
  33 +@optional
  34 +
  35 +/// 当前数据
  36 +@property (nonatomic, copy) id<YBIBDataProtocol>(^yb_currentData)(void);
  37 +
  38 +/**
  39 + 页码变化了
  40 + */
  41 +- (void)yb_pageChanged;
  42 +
  43 +/**
  44 + 偏移量变化了
  45 +
  46 + @param offsetX 当前偏移量
  47 + */
  48 +- (void)yb_offsetXChanged:(CGFloat)offsetX;
  49 +
  50 +/**
  51 + 响应长按手势
  52 + */
  53 +- (void)yb_respondsToLongPress;
  54 +
  55 +@end
  56 +
  57 +@interface YBIBToolViewHandler : NSObject <YBIBToolViewHandler>
  58 +
  59 +/// 弹出表单视图
  60 +@property (nonatomic, strong, readonly) YBIBSheetView *sheetView;
  61 +
  62 +/// 顶部显示页码视图
  63 +@property (nonatomic, strong, readonly) YBIBTopView *topView;
  64 +
  65 +@end
  66 +
  67 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/ToolView/YBIBToolViewHandler.m 0 → 100644
  1 +//
  2 +// YBIBToolViewHandler.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/7/7.
  6 +// Copyright © 2019 杨波. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBToolViewHandler.h"
  10 +#import "YBIBCopywriter.h"
  11 +#import "YBIBUtilities.h"
  12 +
  13 +@interface YBIBToolViewHandler ()
  14 +@property (nonatomic, strong) YBIBSheetView *sheetView;
  15 +@property (nonatomic, strong) YBIBSheetAction *saveAction;
  16 +@property (nonatomic, strong) YBIBTopView *topView;
  17 +@end
  18 +
  19 +@implementation YBIBToolViewHandler
  20 +
  21 +#pragma mark - <YBIBToolViewHandler>
  22 +
  23 +@synthesize yb_containerView = _yb_containerView;
  24 +@synthesize yb_containerSize = _yb_containerSize;
  25 +@synthesize yb_currentPage = _yb_currentPage;
  26 +@synthesize yb_totalPage = _yb_totalPage;
  27 +@synthesize yb_currentOrientation = _yb_currentOrientation;
  28 +@synthesize yb_currentData = _yb_currentData;
  29 +
  30 +- (void)yb_containerViewIsReadied {
  31 + [self.yb_containerView addSubview:self.topView];
  32 + [self layoutWithExpectOrientation:self.yb_currentOrientation()];
  33 +}
  34 +
  35 +- (void)yb_pageChanged {
  36 + if (self.topView.operationType == YBIBTopViewOperationTypeSave) {
  37 + self.topView.operationButton.hidden = [self currentDataShouldHideSaveButton];
  38 + }
  39 + [self.topView setPage:self.yb_currentPage() totalPage:self.yb_totalPage()];
  40 +}
  41 +
  42 +- (void)yb_respondsToLongPress {
  43 + [self showSheetView];
  44 +}
  45 +
  46 +- (void)yb_hide:(BOOL)hide {
  47 + self.topView.hidden = hide;
  48 + [self.sheetView hideWithAnimation:NO];
  49 +}
  50 +
  51 +- (void)yb_orientationWillChangeWithExpectOrientation:(UIDeviceOrientation)orientation {
  52 + [self.sheetView hideWithAnimation:NO];
  53 +}
  54 +
  55 +- (void)yb_orientationChangeAnimationWithExpectOrientation:(UIDeviceOrientation)orientation {
  56 + [self layoutWithExpectOrientation:orientation];
  57 +}
  58 +
  59 +#pragma mark - private
  60 +
  61 +- (BOOL)currentDataShouldHideSaveButton {
  62 + id<YBIBDataProtocol> data = self.yb_currentData();
  63 + BOOL allow = [data respondsToSelector:@selector(yb_allowSaveToPhotoAlbum)] && [data yb_allowSaveToPhotoAlbum];
  64 + BOOL can = [data respondsToSelector:@selector(yb_saveToPhotoAlbum)];
  65 + return !(allow && can);
  66 +}
  67 +
  68 +- (void)layoutWithExpectOrientation:(UIDeviceOrientation)orientation {
  69 + CGSize containerSize = self.yb_containerSize(orientation);
  70 + UIEdgeInsets padding = YBIBPaddingByBrowserOrientation(orientation);
  71 +
  72 + self.topView.frame = CGRectMake(padding.left, padding.top, containerSize.width - padding.left - padding.right, [YBIBTopView defaultHeight]);
  73 +}
  74 +
  75 +- (void)showSheetView {
  76 + if ([self currentDataShouldHideSaveButton]) {
  77 + [self.sheetView.actions removeObject:self.saveAction];
  78 + } else {
  79 + if (![self.sheetView.actions containsObject:self.saveAction]) {
  80 + [self.sheetView.actions addObject:self.saveAction];
  81 + }
  82 + }
  83 + [self.sheetView showToView:self.yb_containerView orientation:self.yb_currentOrientation()];
  84 +}
  85 +
  86 +#pragma mark - getters
  87 +
  88 +- (YBIBSheetView *)sheetView {
  89 + if (!_sheetView) {
  90 + _sheetView = [YBIBSheetView new];
  91 + __weak typeof(self) wSelf = self;
  92 + [_sheetView setCurrentdata:^id<YBIBDataProtocol>{
  93 + __strong typeof(wSelf) self = wSelf;
  94 + if (!self) return nil;
  95 + return self.yb_currentData();
  96 + }];
  97 + }
  98 + return _sheetView;
  99 +}
  100 +
  101 +- (YBIBSheetAction *)saveAction {
  102 + if (!_saveAction) {
  103 + __weak typeof(self) wSelf = self;
  104 + _saveAction = [YBIBSheetAction actionWithName:[YBIBCopywriter sharedCopywriter].saveToPhotoAlbum action:^(id<YBIBDataProtocol> data) {
  105 + __strong typeof(wSelf) self = wSelf;
  106 + if (!self) return;
  107 + if ([data respondsToSelector:@selector(yb_saveToPhotoAlbum)]) {
  108 + [data yb_saveToPhotoAlbum];
  109 + }
  110 + [self.sheetView hideWithAnimation:YES];
  111 + }];
  112 + }
  113 + return _saveAction;
  114 +}
  115 +
  116 +- (YBIBTopView *)topView {
  117 + if (!_topView) {
  118 + _topView = [YBIBTopView new];
  119 + _topView.operationType = YBIBTopViewOperationTypeMore;
  120 + __weak typeof(self) wSelf = self;
  121 + [_topView setClickOperation:^(YBIBTopViewOperationType type) {
  122 + __strong typeof(wSelf) self = wSelf;
  123 + if (!self) return;
  124 + switch (type) {
  125 + case YBIBTopViewOperationTypeSave: {
  126 + id<YBIBDataProtocol> data = self.yb_currentData();
  127 + if ([data respondsToSelector:@selector(yb_saveToPhotoAlbum)]) {
  128 + [data yb_saveToPhotoAlbum];
  129 + }
  130 + }
  131 + break;
  132 + case YBIBTopViewOperationTypeMore: {
  133 + [self showSheetView];
  134 + }
  135 + break;
  136 + default:
  137 + break;
  138 + }
  139 + }];
  140 + }
  141 + return _topView;
  142 +}
  143 +
  144 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/ToolView/YBIBTopView.h 0 → 100644
  1 +//
  2 +// YBIBTopView.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/7/6.
  6 +// Copyright © 2019 杨波. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +typedef NS_ENUM(NSInteger, YBIBTopViewOperationType) {
  14 + YBIBTopViewOperationTypeSave, //保存
  15 + YBIBTopViewOperationTypeMore //更多
  16 +};
  17 +
  18 +@interface YBIBTopView : UIView
  19 +
  20 +/// 页码标签
  21 +@property (nonatomic, strong, readonly) UILabel *pageLabel;
  22 +
  23 +/// 操作按钮(自定义:直接修改图片或文字,然后添加点击事件)
  24 +@property (nonatomic, strong, readonly) UIButton *operationButton;
  25 +
  26 +/// 按钮类型
  27 +@property (nonatomic, assign) YBIBTopViewOperationType operationType;
  28 +
  29 +/**
  30 + 设置页码
  31 +
  32 + @param page 当前页码
  33 + @param totalPage 总页码数
  34 + */
  35 +- (void)setPage:(NSInteger)page totalPage:(NSInteger)totalPage;
  36 +
  37 +/// 点击操作按钮的回调
  38 +@property (nonatomic, copy) void(^clickOperation)(YBIBTopViewOperationType type);
  39 +
  40 ++ (CGFloat)defaultHeight;
  41 +
  42 +@end
  43 +
  44 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/ToolView/YBIBTopView.m 0 → 100644
  1 +//
  2 +// YBIBTopView.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/7/6.
  6 +// Copyright © 2019 杨波. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBTopView.h"
  10 +#import "YBIBIconManager.h"
  11 +#import "YBIBUtilities.h"
  12 +
  13 +@interface YBIBTopView ()
  14 +@property (nonatomic, strong) UILabel *pageLabel;
  15 +@property (nonatomic, strong) UIButton *operationButton;
  16 +@end
  17 +
  18 +@implementation YBIBTopView
  19 +
  20 +#pragma mark - life cycle
  21 +
  22 +- (instancetype)initWithFrame:(CGRect)frame {
  23 + self = [super initWithFrame:frame];
  24 + if (self) {
  25 + [self addSubview:self.pageLabel];
  26 + [self addSubview:self.operationButton];
  27 +
  28 + [self setOperationType:YBIBTopViewOperationTypeMore];
  29 + }
  30 + return self;
  31 +}
  32 +
  33 +- (void)layoutSubviews {
  34 + [super layoutSubviews];
  35 + CGFloat height = self.bounds.size.height, width = self.bounds.size.width;
  36 + self.pageLabel.frame = CGRectMake(16, 0, width / 3, height);
  37 + CGFloat buttonWidth = 54;
  38 + self.operationButton.frame = CGRectMake(width - buttonWidth, 0, buttonWidth, height);
  39 +}
  40 +
  41 +#pragma mark - public
  42 +
  43 ++ (CGFloat)defaultHeight {
  44 + return 50;
  45 +}
  46 +
  47 +- (void)setPage:(NSInteger)page totalPage:(NSInteger)totalPage {
  48 + if (totalPage <= 1) {
  49 + self.pageLabel.hidden = YES;
  50 + } else {
  51 + self.pageLabel.hidden = NO;
  52 +
  53 + NSString *text = [NSString stringWithFormat:@"%ld/%ld", page + (NSInteger)1, totalPage];
  54 + NSShadow *shadow = [NSShadow new];
  55 + shadow.shadowBlurRadius = 4;
  56 + shadow.shadowOffset = CGSizeMake(0, 1);
  57 + shadow.shadowColor = UIColor.darkGrayColor;
  58 + NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithString:text attributes:@{NSShadowAttributeName:shadow}];
  59 + self.pageLabel.attributedText = attr;
  60 + }
  61 +}
  62 +
  63 +#pragma mark - event
  64 +
  65 +- (void)clickOperationButton:(UIButton *)button {
  66 + if (self.clickOperation) self.clickOperation(self.operationType);
  67 +}
  68 +
  69 +#pragma mark - getters & setters
  70 +
  71 +- (void)setOperationType:(YBIBTopViewOperationType)operationType {
  72 + _operationType = operationType;
  73 +
  74 + UIImage *image = nil;
  75 + switch (operationType) {
  76 + case YBIBTopViewOperationTypeSave:
  77 + image = [YBIBIconManager sharedManager].toolSaveImage();
  78 + break;
  79 + case YBIBTopViewOperationTypeMore:
  80 + image = [YBIBIconManager sharedManager].toolMoreImage();
  81 + break;
  82 + }
  83 +
  84 + [self.operationButton setImage:image forState:UIControlStateNormal];
  85 +}
  86 +
  87 +- (UILabel *)pageLabel {
  88 + if (!_pageLabel) {
  89 + _pageLabel = [UILabel new];
  90 + _pageLabel.textColor = [UIColor whiteColor];
  91 + _pageLabel.font = [UIFont boldSystemFontOfSize:16];
  92 + _pageLabel.textAlignment = NSTextAlignmentLeft;
  93 + _pageLabel.adjustsFontSizeToFitWidth = YES;
  94 + }
  95 + return _pageLabel;
  96 +}
  97 +
  98 +- (UIButton *)operationButton {
  99 + if (!_operationButton) {
  100 + _operationButton = [UIButton buttonWithType:UIButtonTypeCustom];
  101 + _operationButton.titleLabel.font = [UIFont boldSystemFontOfSize:16];
  102 + _operationButton.titleLabel.adjustsFontSizeToFitWidth = YES;
  103 + [_operationButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
  104 + [_operationButton addTarget:self action:@selector(clickOperationButton:) forControlEvents:UIControlEventTouchUpInside];
  105 + _operationButton.layer.shadowColor = UIColor.darkGrayColor.CGColor;
  106 + _operationButton.layer.shadowOffset = CGSizeMake(0, 1);
  107 + _operationButton.layer.shadowOpacity = 1;
  108 + _operationButton.layer.shadowRadius = 4;
  109 + }
  110 + return _operationButton;
  111 +}
  112 +
  113 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/WebImageMediator/YBIBDefaultWebImageMediator.h 0 → 100644
  1 +//
  2 +// YBIBDefaultWebImageMediator.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/8/27.
  6 +// Copyright © 2019 杨波. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBWebImageMediator.h"
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +@interface YBIBDefaultWebImageMediator : NSObject <YBIBWebImageMediator>
  14 +
  15 +@end
  16 +
  17 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/WebImageMediator/YBIBDefaultWebImageMediator.m 0 → 100644
  1 +//
  2 +// YBIBDefaultWebImageMediator.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/8/27.
  6 +// Copyright © 2019 杨波. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBDefaultWebImageMediator.h"
  10 +#import "YBIBUtilities.h"
  11 +#if __has_include(<SDWebImage/SDWebImage.h>)
  12 +#import <SDWebImage/SDWebImage.h>
  13 +#else
  14 +#import "SDWebImage.h"
  15 +#endif
  16 +
  17 +@implementation YBIBDefaultWebImageMediator
  18 +
  19 +#pragma mark - <YBIBWebImageMediator>
  20 +
  21 +- (id)yb_downloadImageWithURL:(NSURL *)URL requestModifier:(nullable YBIBWebImageRequestModifierBlock)requestModifier progress:(nonnull YBIBWebImageProgressBlock)progress success:(nonnull YBIBWebImageSuccessBlock)success failed:(nonnull YBIBWebImageFailedBlock)failed {
  22 + if (!URL) return nil;
  23 +
  24 + SDWebImageContext *context = nil;
  25 + if (requestModifier) {
  26 + SDWebImageDownloaderRequestModifier *modifier = [SDWebImageDownloaderRequestModifier requestModifierWithBlock:requestModifier];
  27 + context = @{SDWebImageContextDownloadRequestModifier:modifier};
  28 + }
  29 +
  30 + SDWebImageDownloaderOptions options = SDWebImageDownloaderLowPriority | SDWebImageDownloaderAvoidDecodeImage;
  31 +
  32 + SDWebImageDownloadToken *token = [[SDWebImageDownloader sharedDownloader] downloadImageWithURL:URL options:options context:context progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
  33 + if (progress) progress(receivedSize, expectedSize);
  34 + } completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
  35 + if (error) {
  36 + if (failed) failed(error, finished);
  37 + } else {
  38 + if (success) success(data, finished);
  39 + }
  40 + }];
  41 + return token;
  42 +}
  43 +
  44 +- (void)yb_cancelTaskWithDownloadToken:(id)token {
  45 + if (token && [token isKindOfClass:SDWebImageDownloadToken.class]) {
  46 + [((SDWebImageDownloadToken *)token) cancel];
  47 + }
  48 +}
  49 +
  50 +- (void)yb_storeToDiskWithImageData:(NSData *)data forKey:(NSURL *)key {
  51 + if (!key) return;
  52 + NSString *cacheKey = [SDWebImageManager.sharedManager cacheKeyForURL:key];
  53 + if (!cacheKey) return;
  54 +
  55 + YBIB_DISPATCH_ASYNC(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
  56 + [[SDImageCache sharedImageCache] storeImageDataToDisk:data forKey:cacheKey];
  57 + })
  58 +}
  59 +
  60 +- (void)yb_queryCacheOperationForKey:(NSURL *)key completed:(YBIBWebImageCacheQueryCompletedBlock)completed {
  61 +#define QUERY_CACHE_FAILED if (completed) {completed(nil, nil); return;}
  62 + if (!key) QUERY_CACHE_FAILED
  63 + NSString *cacheKey = [SDWebImageManager.sharedManager cacheKeyForURL:key];
  64 + if (!cacheKey) QUERY_CACHE_FAILED
  65 +#undef QUERY_CACHE_FAILED
  66 +
  67 + // 'NSData' of image must be read to ensure decoding correctly.
  68 + SDImageCacheOptions options = SDImageCacheQueryMemoryData | SDImageCacheAvoidDecodeImage;
  69 + [[SDImageCache sharedImageCache] queryCacheOperationForKey:cacheKey options:options done:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) {
  70 + if (completed) completed(image, data);
  71 + }];
  72 +}
  73 +
  74 +@end
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/WebImageMediator/YBIBWebImageMediator.h 0 → 100644
  1 +//
  2 +// YBIBWebImageMediator.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/8/27.
  6 +// Copyright © 2019 杨波. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +
  11 +NS_ASSUME_NONNULL_BEGIN
  12 +
  13 +typedef NSURLRequest * _Nullable (^YBIBWebImageRequestModifierBlock)(NSURLRequest *request);
  14 +typedef void(^YBIBWebImageProgressBlock)(NSInteger receivedSize, NSInteger expectedSize);
  15 +typedef void(^YBIBWebImageSuccessBlock)(NSData * _Nullable imageData, BOOL finished);
  16 +typedef void(^YBIBWebImageFailedBlock)(NSError * _Nullable error, BOOL finished);
  17 +typedef void(^YBIBWebImageCacheQueryCompletedBlock)(UIImage * _Nullable image, NSData * _Nullable imageData);
  18 +
  19 +@protocol YBIBWebImageMediator <NSObject>
  20 +
  21 +@required
  22 +
  23 +/**
  24 + 下载图片
  25 +
  26 + @param URL 图片地址
  27 + @param requestModifier 修改默认 NSURLRequest 的闭包
  28 + @param progress 进度回调
  29 + @param success 成功回调
  30 + @param failed 失败回调
  31 + @return 下载 token (可为空)
  32 + */
  33 +- (id)yb_downloadImageWithURL:(NSURL *)URL requestModifier:(nullable YBIBWebImageRequestModifierBlock)requestModifier progress:(YBIBWebImageProgressBlock)progress success:(YBIBWebImageSuccessBlock)success failed:(YBIBWebImageFailedBlock)failed;
  34 +
  35 +/**
  36 + 缓存图片数据到磁盘
  37 +
  38 + @param data 图片数据
  39 + @param key 缓存标识
  40 + */
  41 +- (void)yb_storeToDiskWithImageData:(nullable NSData *)data forKey:(NSURL *)key;
  42 +
  43 +/**
  44 + 读取图片数据
  45 +
  46 + @param key 缓存标识
  47 + @param completed 读取回调
  48 + */
  49 +- (void)yb_queryCacheOperationForKey:(NSURL *)key completed:(YBIBWebImageCacheQueryCompletedBlock)completed;
  50 +
  51 +@optional
  52 +
  53 +/**
  54 + 取消下载
  55 +
  56 + @param token 下载 token
  57 + */
  58 +- (void)yb_cancelTaskWithDownloadToken:(id)token;
  59 +
  60 +@end
  61 +
  62 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/YBImageBrowser.h 0 → 100644
  1 +//
  2 +// YBImageBrowser.h
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/5.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBIBCollectionView.h"
  10 +#import "YBImageBrowserDataSource.h"
  11 +#import "YBImageBrowserDelegate.h"
  12 +#import "YBIBDataProtocol.h"
  13 +#import "YBIBCellProtocol.h"
  14 +#import "YBIBAnimatedTransition.h"
  15 +#import "YBIBAuxiliaryViewHandler.h"
  16 +#import "YBIBToolViewHandler.h"
  17 +#import "YBIBWebImageMediator.h"
  18 +#import "YBIBImageData.h"
  19 +
  20 +NS_ASSUME_NONNULL_BEGIN
  21 +
  22 +@interface YBImageBrowser : UIView
  23 +
  24 +/// 数据源数组
  25 +@property (nonatomic, copy) NSArray<id<YBIBDataProtocol>> *dataSourceArray;
  26 +
  27 +/// 数据源代理
  28 +@property (nonatomic, weak) id<YBImageBrowserDataSource> dataSource;
  29 +
  30 +/// 状态回调代理
  31 +@property (nonatomic, weak) id<YBImageBrowserDelegate> delegate;
  32 +
  33 +/**
  34 + 展示图片浏览器
  35 +
  36 + @param view 指定父视图(view 的大小不能为 CGSizeZero,但允许变化)
  37 + @param containerSize 容器大小(当 view 的大小允许变化时,必须指定确切的 containerSize)
  38 + */
  39 +- (void)showToView:(UIView *)view containerSize:(CGSize)containerSize;
  40 +- (void)showToView:(UIView *)view;
  41 +- (void)show;
  42 +
  43 +/**
  44 + 隐藏图片浏览器(不建议外部持有图片浏览器重复使用)
  45 + */
  46 +- (void)hide;
  47 +
  48 +/// 当前页码
  49 +@property (nonatomic, assign) NSInteger currentPage;
  50 +
  51 +/// 分页间距
  52 +@property (nonatomic, assign) CGFloat distanceBetweenPages;
  53 +
  54 +/// 当前图片浏览器的方向
  55 +@property (nonatomic, assign, readonly) UIDeviceOrientation currentOrientation;
  56 +
  57 +/// 图片浏览器支持的方向 (仅当前控制器不支持旋转时有效,否则将跟随控制器旋转)
  58 +@property (nonatomic, assign) UIInterfaceOrientationMask supportedOrientations;
  59 +
  60 +/// 是否自动隐藏 id<YBIBImageData> 设置的映射视图,默认为 YES
  61 +@property (nonatomic, assign) BOOL autoHideProjectiveView;
  62 +
  63 +/// 是否正在转场
  64 +@property (nonatomic, assign, readonly, getter=isTransitioning) BOOL transitioning;
  65 +/// 是否正在进行展示过程转场
  66 +@property (nonatomic, assign, readonly, getter=isShowTransitioning) BOOL showTransitioning;
  67 +/// 是否正在进行隐藏过程转场
  68 +@property (nonatomic, assign, readonly, getter=isHideTransitioning) BOOL hideTransitioning;
  69 +
  70 +/// 预加载数量 (默认为 2,低内存设备默认为 0)
  71 +@property (nonatomic, assign) NSUInteger preloadCount;
  72 +
  73 +/**
  74 + 重载数据,请保证数据源被正确修改
  75 + */
  76 +- (void)reloadData;
  77 +
  78 +/**
  79 + 获取当前展示的数据对象
  80 +
  81 + @return 数据对象
  82 + */
  83 +- (id<YBIBDataProtocol>)currentData;
  84 +
  85 +/// 是否隐藏状态栏,默认为 YES(该值为 YES 时需要在 info.plist 中添加 View controller-based status bar appearance : NO 才能生效)
  86 +@property (nonatomic, assign) BOOL shouldHideStatusBar;
  87 +
  88 +/// 工具视图处理器
  89 +/// 赋值可自定义,实现者可以直接用 UIView,或者创建一个中介者管理一系列的 UIView。
  90 +/// 内部消息是按照数组下标顺序调度的,所以如果有多个处理器注意添加 UIView 的视图层级。
  91 +@property (nonatomic, copy) NSArray<id<YBIBToolViewHandler>> *toolViewHandlers;
  92 +/// 默认工具视图处理器
  93 +@property (nonatomic, weak, readonly) YBIBToolViewHandler *defaultToolViewHandler;
  94 +
  95 +/// Toast/Loading 处理器 (赋值可自定义)
  96 +@property (nonatomic, strong) id<YBIBAuxiliaryViewHandler> auxiliaryViewHandler;
  97 +
  98 +/// 转场实现类 (赋值可自定义)
  99 +@property (nonatomic, strong) id<YBIBAnimatedTransition> animatedTransition;
  100 +/// 默认转场实现类 (可配置其属性)
  101 +@property (nonatomic, weak, readonly) YBIBAnimatedTransition *defaultAnimatedTransition;
  102 +
  103 +/// 图片下载缓存相关的中介者(赋值可自定义)
  104 +@property (nonatomic, strong) id<YBIBWebImageMediator> webImageMediator;
  105 +
  106 +/// 核心集合视图
  107 +@property (nonatomic, strong, readonly) YBIBCollectionView *collectionView;
  108 +
  109 +@end
  110 +
  111 +NS_ASSUME_NONNULL_END
CNLiveOrganizationValidationModule/Classes/CNLiveOrganizationValidationModule/YBImageBrowser/YBImageBrowser.m 0 → 100644
  1 +//
  2 +// YBImageBrowser.m
  3 +// YBImageBrowserDemo
  4 +//
  5 +// Created by 波儿菜 on 2019/6/5.
  6 +// Copyright © 2019 波儿菜. All rights reserved.
  7 +//
  8 +
  9 +#import "YBImageBrowser.h"
  10 +#import "YBIBUtilities.h"
  11 +#import "YBIBCellProtocol.h"
  12 +#import "YBIBDataMediator.h"
  13 +#import "YBIBScreenRotationHandler.h"
  14 +#import "NSObject+YBImageBrowser.h"
  15 +#import "YBImageBrowser+Internal.h"
  16 +#if __has_include("YBIBDefaultWebImageMediator.h")
  17 +#import "YBIBDefaultWebImageMediator.h"
  18 +#endif
  19 +
  20 +@interface YBImageBrowser () <UICollectionViewDelegate, UICollectionViewDataSource>
  21 +@property (nonatomic, strong) YBIBCollectionView *collectionView;
  22 +@property (nonatomic, strong) YBIBDataMediator *dataMediator;
  23 +@property (nonatomic, strong) YBIBScreenRotationHandler *rotationHandler;
  24 +@end
  25 +
  26 +@implementation YBImageBrowser {
  27 + BOOL _originStatusBarHidden;
  28 +}
  29 +
  30 +#pragma mark - life cycle
  31 +
  32 +- (void)dealloc {
  33 + self.hiddenProjectiveView = nil;
  34 + [self showStatusBar];
  35 +}
  36 +
  37 +- (instancetype)initWithFrame:(CGRect)frame {
  38 + self = [super initWithFrame:frame];
  39 + if (self) {
  40 + self.backgroundColor = UIColor.blackColor;
  41 + UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToLongPress:)];
  42 + [self addGestureRecognizer:longPress];
  43 + [self initValue];
  44 + }
  45 + return self;
  46 +}
  47 +
  48 +- (void)initValue {
  49 + _transitioning = _showTransitioning = _hideTransitioning = NO;
  50 + _defaultAnimatedTransition = _animatedTransition = [YBIBAnimatedTransition new];
  51 + _toolViewHandlers = @[[YBIBToolViewHandler new]];
  52 + _defaultToolViewHandler = _toolViewHandlers[0];
  53 + _auxiliaryViewHandler = [YBIBAuxiliaryViewHandler new];
  54 + _shouldHideStatusBar = YES;
  55 + _autoHideProjectiveView = YES;
  56 +#if __has_include("YBIBDefaultWebImageMediator.h")
  57 + _webImageMediator = [YBIBDefaultWebImageMediator new];
  58 +#endif
  59 +}
  60 +
  61 +#pragma mark - private
  62 +
  63 +- (void)build {
  64 + [self addSubview:self.collectionView];
  65 + self.collectionView.frame = self.bounds;
  66 + self.collectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  67 +
  68 + [self addSubview:self.containerView];
  69 + self.containerView.frame = self.bounds;
  70 + self.containerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  71 +
  72 + [self buildToolView];
  73 +
  74 + [self layoutIfNeeded];
  75 +
  76 + [self collectionViewScrollToPage:self.currentPage];
  77 + [self.rotationHandler startObserveDeviceOrientation];
  78 +}
  79 +
  80 +- (void)buildToolView {
  81 + for (id<YBIBToolViewHandler> handler in self.toolViewHandlers) {
  82 + [self implementGetBaseInfoProtocol:handler];
  83 + [self implementOperateBrowserProtocol:handler];
  84 + __weak typeof(self) wSelf = self;
  85 + if ([handler respondsToSelector:@selector(setYb_currentData:)]) {
  86 + [handler setYb_currentData:^id<YBIBDataProtocol>{
  87 + __strong typeof(wSelf) self = wSelf;
  88 + if (!self) return nil;
  89 + return self.currentData;
  90 + }];
  91 + }
  92 + [handler yb_containerViewIsReadied];
  93 + [handler yb_hide:NO];
  94 + }
  95 +}
  96 +
  97 +- (void)rebuild {
  98 + self.hiddenProjectiveView = nil;
  99 + [self showStatusBar];
  100 + [self.containerView removeFromSuperview];
  101 + _containerView = nil;
  102 + [self.collectionView removeFromSuperview];
  103 + _collectionView = nil;
  104 + [self.dataMediator clear];
  105 + [self.rotationHandler clear];
  106 +}
  107 +
  108 +- (void)collectionViewScrollToPage:(NSInteger)page {
  109 + [self.collectionView scrollToPage:page];
  110 + [self pageNumberChanged];
  111 +}
  112 +
  113 +- (void)pageNumberChanged {
  114 + id<YBIBDataProtocol> data = self.currentData;
  115 + UIView *projectiveView = nil;
  116 + if ([data respondsToSelector:@selector(yb_projectiveView)]) {
  117 + projectiveView = [data yb_projectiveView];
  118 + }
  119 + self.hiddenProjectiveView = projectiveView;
  120 +
  121 + if (self.delegate && [self.delegate respondsToSelector:@selector(yb_imageBrowser:pageChanged:data:)]) {
  122 + [self.delegate yb_imageBrowser:self pageChanged:self.currentPage data:data];
  123 + }
  124 + for (id<YBIBToolViewHandler> handler in self.toolViewHandlers) {
  125 + if ([handler respondsToSelector:@selector(yb_pageChanged)]) {
  126 + [handler yb_pageChanged];
  127 + }
  128 + }
  129 + NSArray *visibleCells = self.collectionView.visibleCells;
  130 + for (UICollectionViewCell<YBIBCellProtocol> *cell in visibleCells) {
  131 + if ([cell respondsToSelector:@selector(yb_pageChanged)]) {
  132 + [cell yb_pageChanged];
  133 + }
  134 + }
  135 +}
  136 +
  137 +- (void)showStatusBar {
  138 + if (self.shouldHideStatusBar) {
  139 + [UIApplication sharedApplication].statusBarHidden = _originStatusBarHidden;
  140 + }
  141 +}
  142 +
  143 +- (void)hideStatusBar {
  144 + if (self.shouldHideStatusBar) {
  145 + [UIApplication sharedApplication].statusBarHidden = YES;
  146 + }
  147 +}
  148 +
  149 +#pragma mark - public
  150 +
  151 +- (void)show {
  152 + [self showToView:[UIApplication sharedApplication].keyWindow];
  153 +}
  154 +
  155 +- (void)showToView:(UIView *)view {
  156 + [self showToView:view containerSize:view.bounds.size];
  157 +}
  158 +
  159 +- (void)showToView:(UIView *)view containerSize:(CGSize)containerSize {
  160 + [self.rotationHandler startObserveStatusBarOrientation];
  161 +
  162 + [view addSubview:self];
  163 + self.frame = view.bounds;
  164 + self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  165 +
  166 + _originStatusBarHidden = [UIApplication sharedApplication].isStatusBarHidden;
  167 +
  168 + [self.rotationHandler configContainerSize:containerSize];
  169 +
  170 + [self.dataMediator preloadWithPage:self.currentPage];
  171 +
  172 + __kindof UIView *startView;
  173 + UIImage *startImage;
  174 + CGRect endFrame = CGRectZero;
  175 + id<YBIBDataProtocol> data = [self.dataMediator dataForCellAtIndex:self.currentPage];
  176 + if ([data respondsToSelector:@selector(yb_projectiveView)]) {
  177 + startView = data.yb_projectiveView;
  178 + self.hiddenProjectiveView = startView;
  179 + if ([startView isKindOfClass:UIImageView.class]) {
  180 + startImage = ((UIImageView *)startView).image;
  181 + } else {
  182 + startImage = YBIBSnapshotView(startView);
  183 + }
  184 + }
  185 + if ([data respondsToSelector:@selector(yb_imageViewFrameWithContainerSize:imageSize:orientation:)]) {
  186 + endFrame = [data yb_imageViewFrameWithContainerSize:self.bounds.size imageSize:startImage.size orientation:self.rotationHandler.currentOrientation];
  187 + }
  188 +
  189 + [self setTransitioning:YES isShow:YES];
  190 + [self.animatedTransition yb_showTransitioningWithContainer:self startView:startView startImage:startImage endFrame:endFrame orientation:self.rotationHandler.currentOrientation completion:^{
  191 + [self hideStatusBar];
  192 + [self build];
  193 + [self setTransitioning:NO isShow:YES];
  194 + }];
  195 +}
  196 +
  197 +- (void)hide {
  198 + __kindof UIView *startView;
  199 + __kindof UIView *endView;
  200 + UICollectionViewCell<YBIBCellProtocol> *cell = (UICollectionViewCell<YBIBCellProtocol> *)self.collectionView.centerCell;
  201 + if ([cell respondsToSelector:@selector(yb_foregroundView)]) {
  202 + startView = cell.yb_foregroundView;
  203 + }
  204 + if ([cell.yb_cellData respondsToSelector:@selector(yb_projectiveView)]) {
  205 + endView = cell.yb_cellData.yb_projectiveView;
  206 + }
  207 +
  208 + for (id<YBIBToolViewHandler> handler in self.toolViewHandlers) {
  209 + [handler yb_hide:YES];
  210 + }
  211 + [self showStatusBar];
  212 +
  213 + [self setTransitioning:YES isShow:NO];
  214 + [self.animatedTransition yb_hideTransitioningWithContainer:self startView:startView endView:endView orientation:self.rotationHandler.currentOrientation completion:^{
  215 + [self rebuild];
  216 + [self removeFromSuperview];
  217 + [self setTransitioning:NO isShow:NO];
  218 + }];
  219 +}
  220 +
  221 +- (void)reloadData {
  222 + [self.dataMediator clear];
  223 + NSInteger page = self.currentPage;
  224 + [self.collectionView reloadData];
  225 + self.currentPage = page;
  226 +}
  227 +
  228 +- (id<YBIBDataProtocol>)currentData {
  229 + return [self.dataMediator dataForCellAtIndex:self.currentPage];
  230 +}
  231 +
  232 +#pragma mark - internal
  233 +
  234 +- (void)setHiddenProjectiveView:(NSObject *)hiddenProjectiveView {
  235 + if (_hiddenProjectiveView && [_hiddenProjectiveView respondsToSelector:@selector(setAlpha:)]) {
  236 + CGFloat originAlpha = _hiddenProjectiveView.ybib_originAlpha;
  237 + if (originAlpha != 1) {
  238 + [_hiddenProjectiveView setValue:@(1) forKey:@"alpha"];
  239 + [UIView animateWithDuration:0.2 animations:^{
  240 + [self->_hiddenProjectiveView setValue:@(originAlpha) forKey:@"alpha"];
  241 + }];
  242 + } else {
  243 + [_hiddenProjectiveView setValue:@(originAlpha) forKey:@"alpha"];
  244 + }
  245 + }
  246 + _hiddenProjectiveView = hiddenProjectiveView;
  247 +
  248 + if (!self.autoHideProjectiveView) return;
  249 +
  250 + if (hiddenProjectiveView && [hiddenProjectiveView respondsToSelector:@selector(setAlpha:)]) {
  251 + hiddenProjectiveView.ybib_originAlpha = ((NSNumber *)[hiddenProjectiveView valueForKey:@"alpha"]).floatValue;
  252 + [hiddenProjectiveView setValue:@(0) forKey:@"alpha"];
  253 + }
  254 +}
  255 +
  256 +- (void)implementOperateBrowserProtocol:(id<YBIBOperateBrowserProtocol>)obj {
  257 + __weak typeof(self) wSelf = self;
  258 + if ([obj respondsToSelector:@selector(setYb_hideBrowser:)]) {
  259 + [obj setYb_hideBrowser:^{
  260 + __strong typeof(wSelf) self = wSelf;
  261 + if (!self) return;
  262 + [self hide];
  263 + }];
  264 + }
  265 + if ([obj respondsToSelector:@selector(setYb_hideStatusBar:)]) {
  266 + [obj setYb_hideStatusBar:^(BOOL hide) {
  267 + __strong typeof(wSelf) self = wSelf;
  268 + if (!self) return;
  269 + hide ? [self hideStatusBar] : [self showStatusBar];
  270 + }];
  271 + }
  272 + if ([obj respondsToSelector:@selector(setYb_hideToolViews:)]) {
  273 + [obj setYb_hideToolViews:^(BOOL hide) {
  274 + __strong typeof(wSelf) self = wSelf;
  275 + if (!self) return;
  276 + for (id<YBIBToolViewHandler> handler in self.toolViewHandlers) {
  277 + [handler yb_hide:hide];
  278 + }
  279 + }];
  280 + }
  281 +}
  282 +
  283 +- (void)implementGetBaseInfoProtocol:(id<YBIBGetBaseInfoProtocol>)obj {
  284 + __weak typeof(self) wSelf = self;
  285 + if ([obj respondsToSelector:@selector(setYb_currentOrientation:)]) {
  286 + [obj setYb_currentOrientation:^UIDeviceOrientation{
  287 + __strong typeof(wSelf) self = wSelf;
  288 + if (!self) return UIDeviceOrientationPortrait;
  289 + return self.rotationHandler.currentOrientation;
  290 + }];
  291 + }
  292 + if ([obj respondsToSelector:@selector(setYb_containerSize:)]) {
  293 + [obj setYb_containerSize:^CGSize(UIDeviceOrientation orientation) {
  294 + __strong typeof(wSelf) self = wSelf;
  295 + if (!self) return CGSizeZero;
  296 + return [self.rotationHandler containerSizeWithOrientation:orientation];
  297 + }];
  298 + }
  299 + if ([obj respondsToSelector:@selector(setYb_auxiliaryViewHandler:)]) {
  300 + [obj setYb_auxiliaryViewHandler:^id<YBIBAuxiliaryViewHandler>{
  301 + __strong typeof(wSelf) self = wSelf;
  302 + if (!self) return nil;
  303 + return self.auxiliaryViewHandler;
  304 + }];
  305 + }
  306 + if ([obj respondsToSelector:@selector(setYb_webImageMediator:)]) {
  307 + [obj setYb_webImageMediator:^id<YBIBWebImageMediator> {
  308 + __strong typeof(wSelf) self = wSelf;
  309 + if (!self) return nil;
  310 + NSAssert(self.webImageMediator, @"'webImageMediator' should not be nil.");
  311 + return self.webImageMediator;
  312 + }];
  313 + }
  314 + if ([obj respondsToSelector:@selector(setYb_currentPage:)]) {
  315 + [obj setYb_currentPage:^NSInteger{
  316 + __strong typeof(wSelf) self = wSelf;
  317 + if (!self) return 0;
  318 + return self.currentPage;
  319 + }];
  320 + }
  321 + if ([obj respondsToSelector:@selector(setYb_totalPage:)]) {
  322 + [obj setYb_totalPage:^NSInteger{
  323 + __strong typeof(wSelf) self = wSelf;
  324 + if (!self) return 0;
  325 + return [self.dataMediator numberOfCells];
  326 + }];
  327 + }
  328 + if ([obj respondsToSelector:@selector(setYb_backView:)]) {
  329 + obj.yb_backView = self;
  330 + }
  331 + if ([obj respondsToSelector:@selector(setYb_containerView:)]) {
  332 + obj.yb_containerView = self.containerView;
  333 + }
  334 + if ([obj respondsToSelector:@selector(setYb_collectionView:)]) {
  335 + [obj setYb_collectionView:^__kindof UICollectionView *{
  336 + __strong typeof(wSelf) self = wSelf;
  337 + if (!self) return nil;
  338 + return self.collectionView;
  339 + }];
  340 + }
  341 + if ([obj respondsToSelector:@selector(setYb_cellIsInCenter:)]) {
  342 + [obj setYb_cellIsInCenter:^BOOL{
  343 + __strong typeof(wSelf) self = wSelf;
  344 + CGFloat pageF = self.collectionView.contentOffset.x / self.collectionView.bounds.size.width;
  345 + // '0.001' is admissible error.
  346 + return ABS(pageF - (NSInteger)pageF) <= 0.001;
  347 + }];
  348 + }
  349 + if ([obj respondsToSelector:@selector(setYb_isTransitioning:)]) {
  350 + [obj setYb_isTransitioning:^BOOL{
  351 + __strong typeof(wSelf) self = wSelf;
  352 + if (!self) return NO;
  353 + return self.isTransitioning;
  354 + }];
  355 + }
  356 + if ([obj respondsToSelector:@selector(setYb_isShowTransitioning:)]) {
  357 + [obj setYb_isShowTransitioning:^BOOL{
  358 + __strong typeof(wSelf) self = wSelf;
  359 + if (!self) return NO;
  360 + return self.isShowTransitioning;
  361 + }];
  362 + }
  363 + if ([obj respondsToSelector:@selector(setYb_isHideTransitioning:)]) {
  364 + [obj setYb_isHideTransitioning:^BOOL{
  365 + __strong typeof(wSelf) self = wSelf;
  366 + if (!self) return NO;
  367 + return self.isHideTransitioning;
  368 + }];
  369 + }
  370 + if ([obj respondsToSelector:@selector(setYb_isRotating:)]) {
  371 + [obj setYb_isRotating:^BOOL{
  372 + __strong typeof(wSelf) self = wSelf;
  373 + if (!self) return NO;
  374 + return self.rotationHandler.isRotating;
  375 + }];
  376 + }
  377 +}
  378 +
  379 +#pragma mark - <UICollectionViewDataSource>
  380 +
  381 +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
  382 + return [self.dataMediator numberOfCells];
  383 +}
  384 +
  385 +- (UICollectionViewCell *)collectionView:(YBIBCollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
  386 + id<YBIBDataProtocol> data = [self.dataMediator dataForCellAtIndex:indexPath.row];
  387 +
  388 + UICollectionViewCell<YBIBCellProtocol> *cell = [collectionView dequeueReusableCellWithReuseIdentifier:[collectionView reuseIdentifierForCellClass:data.yb_classOfCell] forIndexPath:indexPath];
  389 +
  390 + [self implementGetBaseInfoProtocol:cell];
  391 + [self implementOperateBrowserProtocol:cell];
  392 +
  393 + if ([cell respondsToSelector:@selector(setYb_selfPage:)]) {
  394 + [cell setYb_selfPage:^NSInteger{
  395 + return indexPath.row;
  396 + }];
  397 + }
  398 +
  399 + cell.yb_cellData = data;
  400 +
  401 + if ([cell respondsToSelector:@selector(yb_pageChanged)]) {
  402 + [cell yb_pageChanged];
  403 + }
  404 +
  405 + [self.dataMediator preloadWithPage:indexPath.row];
  406 +
  407 + return cell;
  408 +}
  409 +
  410 +#pragma mark - <UICollectionViewDelegate>
  411 +
  412 +- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
  413 + CGFloat pageF = scrollView.contentOffset.x / scrollView.bounds.size.width;
  414 + NSInteger page = (NSInteger)(pageF + 0.5);
  415 +
  416 + for (id<YBIBToolViewHandler> handler in self.toolViewHandlers) {
  417 + if ([handler respondsToSelector:@selector(yb_offsetXChanged:)]) {
  418 + [handler yb_offsetXChanged:pageF];
  419 + }
  420 + }
  421 +
  422 + if (!scrollView.isDecelerating && !scrollView.isDragging) {
  423 + // Return if not scrolled by finger.
  424 + return;
  425 + }
  426 + if (page < 0 || page > [self.dataMediator numberOfCells] - 1) return;
  427 + if (self.rotationHandler.isRotating) return;
  428 +
  429 + if (page != _currentPage) {
  430 + _currentPage = page;
  431 + [self pageNumberChanged];
  432 + }
  433 +}
  434 +
  435 +#pragma mark - event
  436 +
  437 +- (void)respondsToLongPress:(UILongPressGestureRecognizer *)sender {
  438 + if (sender.state == UIGestureRecognizerStateBegan) {
  439 + if ([self.delegate respondsToSelector:@selector(yb_imageBrowser:respondsToLongPressWithData:)]) {
  440 + [self.delegate yb_imageBrowser:self respondsToLongPressWithData:[self currentData]];
  441 + } else {
  442 + for (id<YBIBToolViewHandler> handler in self.toolViewHandlers) {
  443 + if ([handler respondsToSelector:@selector(yb_respondsToLongPress)]) {
  444 + [handler yb_respondsToLongPress];
  445 + }
  446 + }
  447 + }
  448 + }
  449 +}
  450 +
  451 +#pragma mark - getters & setters
  452 +
  453 +- (YBIBContainerView *)containerView {
  454 + if (!_containerView) {
  455 + _containerView = [YBIBContainerView new];
  456 + _containerView.backgroundColor = UIColor.clearColor;
  457 + _containerView.layer.masksToBounds = YES;
  458 + }
  459 + return _containerView;
  460 +}
  461 +
  462 +- (YBIBCollectionView *)collectionView {
  463 + if (!_collectionView) {
  464 + _collectionView = [YBIBCollectionView new];
  465 + _collectionView.delegate = self;
  466 + _collectionView.dataSource = self;
  467 + }
  468 + return _collectionView;
  469 +}
  470 +- (void)setCurrentPage:(NSInteger)currentPage {
  471 + NSInteger maxPage = self.dataMediator.numberOfCells - 1;
  472 + if (currentPage > maxPage) {
  473 + currentPage = maxPage;
  474 + }
  475 + _currentPage = currentPage;
  476 + if (self.collectionView.superview) {
  477 + [self collectionViewScrollToPage:currentPage];
  478 + }
  479 +}
  480 +- (void)setDistanceBetweenPages:(CGFloat)distanceBetweenPages {
  481 + self.collectionView.layout.distanceBetweenPages = distanceBetweenPages;
  482 +}
  483 +- (CGFloat)distanceBetweenPages {
  484 + return self.collectionView.layout.distanceBetweenPages;
  485 +}
  486 +
  487 +- (void)setTransitioning:(BOOL)transitioning isShow:(BOOL)isShow {
  488 + _transitioning = transitioning;
  489 + _showTransitioning = transitioning && isShow;
  490 + _hideTransitioning = transitioning && !isShow;
  491 +
  492 + // Make 'self.userInteractionEnabled' always 'YES' to block external interaction.
  493 + self.containerView.userInteractionEnabled = !transitioning;
  494 + self.collectionView.userInteractionEnabled = !transitioning;
  495 +
  496 + if (transitioning) {
  497 + if ([self.delegate respondsToSelector:@selector(yb_imageBrowser:beginTransitioningWithIsShow:)]) {
  498 + [self.delegate yb_imageBrowser:self beginTransitioningWithIsShow:isShow];
  499 + }
  500 + } else {
  501 + if ([self.delegate respondsToSelector:@selector(yb_imageBrowser:endTransitioningWithIsShow:)]) {
  502 + [self.delegate yb_imageBrowser:self endTransitioningWithIsShow:isShow];
  503 + }
  504 + }
  505 +}
  506 +
  507 +- (YBIBDataMediator *)dataMediator {
  508 + if (!_dataMediator) {
  509 + _dataMediator = [[YBIBDataMediator alloc] initWithBrowser:self];
  510 + _dataMediator.dataCacheCountLimit = YBIBLowMemory() ? 9 : 27;
  511 + _dataMediator.preloadCount = YBIBLowMemory() ? 0 : 2;
  512 + }
  513 + return _dataMediator;
  514 +}
  515 +- (void)setPreloadCount:(NSUInteger)preloadCount {
  516 + self.dataMediator.preloadCount = preloadCount;
  517 +}
  518 +- (NSUInteger)preloadCount {
  519 + return self.dataMediator.preloadCount;
  520 +}
  521 +
  522 +- (YBIBScreenRotationHandler *)rotationHandler {
  523 + if (!_rotationHandler) {
  524 + _rotationHandler = [[YBIBScreenRotationHandler alloc] initWithBrowser:self];
  525 + }
  526 + return _rotationHandler;
  527 +}
  528 +- (void)setSupportedOrientations:(UIInterfaceOrientationMask)supportedOrientations {
  529 + self.rotationHandler.supportedOrientations = supportedOrientations;
  530 +}
  531 +- (UIInterfaceOrientationMask)supportedOrientations {
  532 + return self.rotationHandler.supportedOrientations;
  533 +}
  534 +- (UIDeviceOrientation)currentOrientation {
  535 + return self.rotationHandler.currentOrientation;
  536 +}
  537 +
  538 +@end
Example/CNLiveOrganizationValidationModule.xcodeproj/project.pbxproj
@@ -386,8 +386,6 @@ @@ -386,8 +386,6 @@
386 "${BUILT_PRODUCTS_DIR}/SGPagingView/SGPagingView.framework", 386 "${BUILT_PRODUCTS_DIR}/SGPagingView/SGPagingView.framework",
387 "${BUILT_PRODUCTS_DIR}/SSZipArchive/SSZipArchive.framework", 387 "${BUILT_PRODUCTS_DIR}/SSZipArchive/SSZipArchive.framework",
388 "${BUILT_PRODUCTS_DIR}/WebViewJavascriptBridge/WebViewJavascriptBridge.framework", 388 "${BUILT_PRODUCTS_DIR}/WebViewJavascriptBridge/WebViewJavascriptBridge.framework",
389 - "${BUILT_PRODUCTS_DIR}/YBImageBrowser/YBImageBrowser.framework",  
390 - "${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework",  
391 "${BUILT_PRODUCTS_DIR}/YYKit/YYKit.framework", 389 "${BUILT_PRODUCTS_DIR}/YYKit/YYKit.framework",
392 "${BUILT_PRODUCTS_DIR}/libksygpulive_ks3/libksygpulive.framework", 390 "${BUILT_PRODUCTS_DIR}/libksygpulive_ks3/libksygpulive.framework",
393 "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework", 391 "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework",
@@ -430,8 +428,6 @@ @@ -430,8 +428,6 @@
430 "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SGPagingView.framework", 428 "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SGPagingView.framework",
431 "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SSZipArchive.framework", 429 "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SSZipArchive.framework",
432 "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebViewJavascriptBridge.framework", 430 "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebViewJavascriptBridge.framework",
433 - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YBImageBrowser.framework",  
434 - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYImage.framework",  
435 "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYKit.framework", 431 "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYKit.framework",
436 "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libksygpulive.framework", 432 "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libksygpulive.framework",
437 "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework", 433 "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework",