首次提交
49
SweetParty/第三方库/KNPhotoBrowser/KNActionSheet/KNActionSheet.h
Executable file
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// KNActionSheet.h
|
||||
// test
|
||||
//
|
||||
// Created by LuKane on 2019/12/18.
|
||||
// Copyright © 2019 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void(^ActionSheetBlock)(NSInteger buttonIndex);
|
||||
|
||||
@interface KNActionSheet : UIView
|
||||
|
||||
+ (KNActionSheet *)share;
|
||||
|
||||
- (instancetype)initWithTitle:(NSString *)title
|
||||
cancelTitle:(NSString *)cancelTitle
|
||||
titleArray:(NSMutableArray <NSString *> *)titleArray
|
||||
actionSheetBlock:(ActionSheetBlock)sheetBlock;
|
||||
|
||||
- (instancetype)initWithTitle:(NSString *)title
|
||||
cancelTitle:(NSString *)cancelTitle
|
||||
titleArray:(NSMutableArray <NSString *> *)titleArray
|
||||
destructiveArray:(NSMutableArray <NSString *> *)destructiveArray
|
||||
actionSheetBlock:(ActionSheetBlock)sheetBlock;
|
||||
|
||||
|
||||
- (instancetype)initWithTitle:(NSString *)title
|
||||
titleColor:(nullable UIColor *)titleColor
|
||||
cancelTitle:(NSString *)cancelTitle
|
||||
titleArray:(NSMutableArray <NSString *> *)titleArray
|
||||
actionSheetBlock:(ActionSheetBlock)sheetBlock;
|
||||
|
||||
- (instancetype)initWithTitle:(NSString *)title
|
||||
titleColor:(nullable UIColor *)titleColor
|
||||
cancelTitle:(NSString *)cancelTitle
|
||||
titleArray:(NSMutableArray <NSString *> *)titleArray
|
||||
destructiveArray:(NSMutableArray <NSString *> *)destructiveArray
|
||||
actionSheetBlock:(ActionSheetBlock)sheetBlock;
|
||||
|
||||
- (void)showOnView:(UIView *)view;
|
||||
- (void)dismiss;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
334
SweetParty/第三方库/KNPhotoBrowser/KNActionSheet/KNActionSheet.m
Executable file
@@ -0,0 +1,334 @@
|
||||
//
|
||||
// KNActionSheet.m
|
||||
// test
|
||||
//
|
||||
// Created by LuKane on 2019/12/18.
|
||||
// Copyright © 2019 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import "KNActionSheet.h"
|
||||
#import "KNActionSheetItem.h"
|
||||
|
||||
#ifndef ScreenWidth
|
||||
#define ScreenWidth [UIScreen mainScreen].bounds.size.width
|
||||
#endif
|
||||
|
||||
#ifndef ScreenHeight
|
||||
#define ScreenHeight [UIScreen mainScreen].bounds.size.height
|
||||
#endif
|
||||
|
||||
#define kKNActionCoverBackgroundColor [UIColor colorWithRed:30/255.f green:30/255.f blue:30/255.f alpha:1.f]
|
||||
#define kKNActionBgViewBackgroundColor [UIColor colorWithRed:220/255.f green:220/255.f blue:220/255.f alpha:1.f]
|
||||
|
||||
#define kKNActionDuration 0.3
|
||||
|
||||
@interface KNActionSheet()<KNActionSheetItemDelegate>{
|
||||
NSString *_title;
|
||||
UIColor *_titleColor;
|
||||
NSString *_cancelTitle;
|
||||
ActionSheetBlock _sheetBlock;
|
||||
NSMutableArray *_titleArray;
|
||||
NSMutableArray *_destruciveArray;
|
||||
|
||||
KNActionSheetItem *_cancelItem;
|
||||
|
||||
UIView *_coverView;
|
||||
UIView *_bgView;
|
||||
UIView *_bottomView;
|
||||
NSMutableArray *_itemsArr;
|
||||
NSMutableArray *_lineArr;
|
||||
BOOL _isShow;
|
||||
|
||||
CGFloat _kKNActionItemHeight;
|
||||
CGFloat _padding;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation KNActionSheet
|
||||
|
||||
+ (KNActionSheet *)share{
|
||||
static dispatch_once_t onceToken;
|
||||
static KNActionSheet *share;
|
||||
dispatch_once(&onceToken, ^{
|
||||
share = [[self alloc] init];
|
||||
});
|
||||
return share;
|
||||
}
|
||||
|
||||
- (instancetype)initWithTitle:(NSString *)title
|
||||
cancelTitle:(NSString *)cancelTitle
|
||||
titleArray:(NSMutableArray <NSString *> *)titleArray
|
||||
actionSheetBlock:(ActionSheetBlock)sheetBlock{
|
||||
return [self initWithTitle:title titleColor:nil cancelTitle:cancelTitle titleArray:titleArray actionSheetBlock:sheetBlock];
|
||||
}
|
||||
|
||||
- (instancetype)initWithTitle:(NSString *)title
|
||||
cancelTitle:(NSString *)cancelTitle
|
||||
titleArray:(NSMutableArray <NSString *> *)titleArray
|
||||
destructiveArray:(NSMutableArray <NSString *> *)destructiveArray
|
||||
actionSheetBlock:(ActionSheetBlock)sheetBlock{
|
||||
return [self initWithTitle:title titleColor:nil cancelTitle:cancelTitle titleArray:titleArray destructiveArray:destructiveArray actionSheetBlock:sheetBlock];
|
||||
}
|
||||
|
||||
- (instancetype)initWithTitle:(NSString *)title
|
||||
titleColor:(nullable UIColor *)titleColor
|
||||
cancelTitle:(NSString *)cancelTitle
|
||||
titleArray:(NSMutableArray <NSString *> *)titleArray
|
||||
actionSheetBlock:(ActionSheetBlock)sheetBlock{
|
||||
if (self = [super init]) {
|
||||
_title = title;
|
||||
_titleColor = titleColor;
|
||||
_cancelTitle = cancelTitle;
|
||||
_titleArray = titleArray;
|
||||
_sheetBlock = sheetBlock;
|
||||
[self setupSubViews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithTitle:(NSString *)title
|
||||
titleColor:(nullable UIColor *)titleColor
|
||||
cancelTitle:(NSString *)cancelTitle
|
||||
titleArray:(NSMutableArray <NSString *> *)titleArray
|
||||
destructiveArray:(NSMutableArray <NSString *> *)destructiveArray
|
||||
actionSheetBlock:(ActionSheetBlock)sheetBlock{
|
||||
if (self = [super init]) {
|
||||
_title = title;
|
||||
_titleColor = titleColor;
|
||||
_cancelTitle = cancelTitle;
|
||||
_titleArray = titleArray;
|
||||
_sheetBlock = sheetBlock;
|
||||
_destruciveArray = destructiveArray;
|
||||
[self setupSubViews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setupSubViews{
|
||||
|
||||
[self.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
[obj removeFromSuperview];
|
||||
}];
|
||||
|
||||
_kKNActionItemHeight = 48.7;
|
||||
_padding = 0.3;
|
||||
|
||||
if (ScreenWidth <= 375) {
|
||||
_kKNActionItemHeight = 48.5;
|
||||
_padding = 0.5;
|
||||
}
|
||||
|
||||
if ([self isEmptyArray:_titleArray]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (![self isEmptyArray:_destruciveArray]) {
|
||||
if (_destruciveArray.count > _titleArray.count) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(deviceWillOrientation)
|
||||
name:UIApplicationWillChangeStatusBarOrientationNotification
|
||||
object:nil];
|
||||
|
||||
[self setBackgroundColor:[UIColor clearColor]];
|
||||
|
||||
[self setFrame:[UIApplication sharedApplication].windows.lastObject.bounds];
|
||||
|
||||
UIView *coverView = [[UIView alloc] initWithFrame:self.bounds];
|
||||
[coverView setBackgroundColor:kKNActionCoverBackgroundColor];
|
||||
[coverView setAlpha:0];
|
||||
[coverView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(coverViewDidClick)]];
|
||||
[self addSubview:coverView];
|
||||
_coverView = coverView;
|
||||
|
||||
UIView *bgView = [[UIView alloc] init];
|
||||
[bgView setBackgroundColor:[UIColor whiteColor]];
|
||||
[self addSubview:bgView];
|
||||
_bgView = bgView;
|
||||
|
||||
if ([self isEmptyString:_cancelTitle]) {
|
||||
_cancelTitle = @"取消";
|
||||
}
|
||||
|
||||
_itemsArr = [NSMutableArray array];
|
||||
_lineArr = [NSMutableArray array];
|
||||
|
||||
if (![self isEmptyString:_title]) {
|
||||
KNActionSheetItem *item = [[KNActionSheetItem alloc] init];
|
||||
item.userInteractionEnabled = false;
|
||||
if (_titleColor) {
|
||||
item.color = _titleColor;
|
||||
}
|
||||
item.title = _title;
|
||||
[_itemsArr addObject:item];
|
||||
[bgView addSubview:item];
|
||||
|
||||
CALayer *line = [CALayer layer];
|
||||
[line setBackgroundColor:[kKNActionBgViewBackgroundColor CGColor]];
|
||||
[bgView.layer addSublayer:line];
|
||||
[_lineArr addObject:line];
|
||||
}
|
||||
|
||||
for (NSInteger i = 0; i < _titleArray.count; i++) {
|
||||
KNActionSheetItem *item = [[KNActionSheetItem alloc] init];
|
||||
for (NSInteger j = 0; j < _destruciveArray.count; j++) {
|
||||
NSInteger jIndex = [_destruciveArray[j] integerValue];
|
||||
if (jIndex < _titleArray.count) {
|
||||
if (i == jIndex) {
|
||||
item.color = [UIColor redColor];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item.title = _titleArray[i];
|
||||
item.tag = i;
|
||||
item.delegate = self;
|
||||
[_itemsArr addObject:item];
|
||||
[bgView addSubview:item];
|
||||
|
||||
CALayer *line = [CALayer layer];
|
||||
[line setBackgroundColor:[kKNActionBgViewBackgroundColor CGColor]];
|
||||
[bgView.layer addSublayer:line];
|
||||
[_lineArr addObject:line];
|
||||
}
|
||||
|
||||
KNActionSheetItem *cancelItem = [[KNActionSheetItem alloc] init];
|
||||
cancelItem.title = _cancelTitle;
|
||||
[cancelItem addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(coverViewDidClick)]];
|
||||
[bgView addSubview:cancelItem];
|
||||
_cancelItem = cancelItem;
|
||||
}
|
||||
|
||||
- (void)deviceWillOrientation{
|
||||
[self dismiss];
|
||||
}
|
||||
|
||||
- (void)actionSheetItemDidClick:(NSInteger)index{
|
||||
if (_sheetBlock) {
|
||||
_sheetBlock(index);
|
||||
}
|
||||
[self dismiss];
|
||||
}
|
||||
|
||||
- (void)coverViewDidClick{
|
||||
if (_sheetBlock) {
|
||||
_sheetBlock(-1);
|
||||
}
|
||||
[self dismiss];
|
||||
}
|
||||
|
||||
- (void)showOnView:(UIView *)view{
|
||||
[_coverView setAlpha:0];
|
||||
[_bgView setTransform:CGAffineTransformIdentity];
|
||||
|
||||
[view addSubview:self];
|
||||
_isShow = true;
|
||||
}
|
||||
|
||||
- (void)dismiss{
|
||||
[UIView animateWithDuration:kKNActionDuration animations:^{
|
||||
[self->_coverView setAlpha:0];
|
||||
[self->_bgView setTransform:CGAffineTransformIdentity];
|
||||
} completion:^(BOOL finished) {
|
||||
[self removeFromSuperview];
|
||||
}];
|
||||
_isShow = false;
|
||||
}
|
||||
|
||||
- (BOOL)isEmptyString:(NSString *)string{
|
||||
if(string == nil || string == NULL || [string isKindOfClass:[NSNull class]] || [[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
- (BOOL)isEmptyArray:(NSArray *)array{
|
||||
if(array == nil || [array isKindOfClass:[NSNull class]] || array.count == 0){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews{
|
||||
[super layoutSubviews];
|
||||
|
||||
_coverView.frame = self.bounds;
|
||||
|
||||
if (@available(iOS 11.0, *)){
|
||||
UIEdgeInsets insets = self.safeAreaInsets;
|
||||
CGFloat height = _kKNActionItemHeight * (_itemsArr.count + 1) + 8;
|
||||
_bgView.frame = CGRectMake(0, self.frame.size.height, self.frame.size.width, height + insets.bottom);
|
||||
[self addRectCorners:UIRectCornerTopLeft | UIRectCornerTopRight width:13 view:_bgView];
|
||||
for (NSInteger i = 0; i < _itemsArr.count; i++) {
|
||||
KNActionSheetItem *item = _itemsArr[i];
|
||||
if (i == 0) {
|
||||
[self addRectCorners:UIRectCornerTopLeft | UIRectCornerTopRight width:13 view:item];
|
||||
}
|
||||
|
||||
item.frame = CGRectMake(0, (_kKNActionItemHeight + _padding) * i, _bgView.frame.size.width, _kKNActionItemHeight);
|
||||
CALayer *line = _lineArr[i];
|
||||
if (i != _itemsArr.count - 1) {
|
||||
line.frame = CGRectMake(0, CGRectGetMaxY(item.frame), _bgView.frame.size.width, _padding);
|
||||
}else{
|
||||
line.frame = CGRectMake(0, CGRectGetMaxY(item.frame), _bgView.frame.size.width, _padding + 8);
|
||||
}
|
||||
}
|
||||
|
||||
_cancelItem.frame = CGRectMake(0, _bgView.frame.size.height - _kKNActionItemHeight - insets.bottom, _bgView.frame.size.width, _kKNActionItemHeight);
|
||||
|
||||
if (_isShow) {
|
||||
_isShow = false;
|
||||
[UIView animateWithDuration:kKNActionDuration animations:^{
|
||||
[self->_coverView setAlpha:0.3];
|
||||
[self->_bgView setTransform:CGAffineTransformMakeTranslation(0, -self->_bgView.frame.size.height)];
|
||||
}];
|
||||
}
|
||||
} else {
|
||||
CGFloat height = _kKNActionItemHeight * (_itemsArr.count + 1) + 8;
|
||||
_bgView.frame = CGRectMake(0, self.frame.size.height, self.frame.size.width, height);
|
||||
[self addRectCorners:UIRectCornerTopLeft | UIRectCornerTopRight width:13 view:_bgView];
|
||||
for (NSInteger i = 0; i < _itemsArr.count; i++) {
|
||||
KNActionSheetItem *item = _itemsArr[i];
|
||||
if (i == 0) {
|
||||
[self addRectCorners:UIRectCornerTopLeft | UIRectCornerTopRight width:13 view:item];
|
||||
}
|
||||
item.frame = CGRectMake(0, (_kKNActionItemHeight + _padding) * i, _bgView.frame.size.width, _kKNActionItemHeight);
|
||||
CALayer *line = _lineArr[i];
|
||||
if (i != _itemsArr.count - 1) {
|
||||
line.frame = CGRectMake(0, CGRectGetMaxY(item.frame), _bgView.frame.size.width, _padding);
|
||||
}else{
|
||||
line.frame = CGRectMake(0, CGRectGetMaxY(item.frame), _bgView.frame.size.width, _padding + 8);
|
||||
}
|
||||
}
|
||||
|
||||
_cancelItem.frame = CGRectMake(0, _bgView.frame.size.height - _kKNActionItemHeight, _bgView.frame.size.width, _kKNActionItemHeight);
|
||||
|
||||
if (_isShow) {
|
||||
_isShow = false;
|
||||
[UIView animateWithDuration:kKNActionDuration animations:^{
|
||||
[self->_coverView setAlpha:0.3];
|
||||
[self->_bgView setTransform:CGAffineTransformMakeTranslation(0, -self->_bgView.frame.size.height)];
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dealloc{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)addRectCorners:(UIRectCorner)corners width:(CGFloat)width view:(UIView *)view{
|
||||
if (width > view.frame.size.width) {
|
||||
return;
|
||||
}
|
||||
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(width, width)];
|
||||
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
|
||||
maskLayer.frame = view.bounds;
|
||||
maskLayer.path = maskPath.CGPath;
|
||||
view.layer.mask = maskLayer;
|
||||
}
|
||||
|
||||
@end
|
||||
34
SweetParty/第三方库/KNPhotoBrowser/KNActionSheet/KNActionSheetItem.h
Executable file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// KNActionSheetItem.h
|
||||
// test
|
||||
//
|
||||
// Created by LuKane on 2019/12/18.
|
||||
// Copyright © 2019 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol KNActionSheetItemDelegate <NSObject>
|
||||
|
||||
/// item did click
|
||||
/// @param index self's tag
|
||||
- (void)actionSheetItemDidClick:(NSInteger)index;
|
||||
|
||||
@end
|
||||
|
||||
@interface KNActionSheetItem : UIView
|
||||
|
||||
/// delegate
|
||||
@property (nonatomic,weak ) id<KNActionSheetItemDelegate> delegate;
|
||||
|
||||
/// title
|
||||
@property (nonatomic,copy ) NSString *title;
|
||||
|
||||
/// title color
|
||||
@property (nonatomic,copy ) UIColor *color;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
69
SweetParty/第三方库/KNPhotoBrowser/KNActionSheet/KNActionSheetItem.m
Executable file
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// KNActionSheetItem.m
|
||||
// test
|
||||
//
|
||||
// Created by LuKane on 2019/12/18.
|
||||
// Copyright © 2019 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import "KNActionSheetItem.h"
|
||||
|
||||
@implementation KNActionSheetItem
|
||||
|
||||
- (instancetype)init{
|
||||
if (self = [super init]) {
|
||||
_color = [UIColor blackColor];
|
||||
[self setBackgroundColor:[UIColor whiteColor]];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
|
||||
[self setBackgroundColor:[UIColor colorWithRed:220 / 255.f green:220 / 255.f blue:220 / 255.f alpha:1.f]];
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
|
||||
[self setBackgroundColor:[UIColor whiteColor]];
|
||||
[self setAlpha:1.f];
|
||||
|
||||
UITouch *touch = [[event allTouches] anyObject];
|
||||
CGPoint point = [touch locationInView:[touch view]];
|
||||
|
||||
CGFloat x = point.x;
|
||||
CGFloat y = point.y;
|
||||
|
||||
if(x < self.frame.size.width && x > 0 && y >0 && y < self.frame.size.height){
|
||||
if([_delegate respondsToSelector:@selector(actionSheetItemDidClick:)]){
|
||||
[_delegate actionSheetItemDidClick:self.tag];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
|
||||
[self setBackgroundColor:[UIColor whiteColor]];
|
||||
[self setAlpha:1.f];
|
||||
}
|
||||
|
||||
- (void)setTitle:(NSString *)title{
|
||||
_title = title;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (void)setColor:(UIColor *)color{
|
||||
_color = color;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)rect{
|
||||
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
|
||||
dict[NSFontAttributeName] = [UIFont systemFontOfSize:16];
|
||||
dict[NSForegroundColorAttributeName] = _color;
|
||||
|
||||
NSMutableParagraphStyle *paragrap = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
|
||||
paragrap.alignment = NSTextAlignmentCenter;
|
||||
|
||||
dict[NSParagraphStyleAttributeName] = paragrap;
|
||||
[_title drawInRect:(CGRect){{0,self.bounds.size.height * 0.3},self.bounds.size} withAttributes:dict];
|
||||
}
|
||||
|
||||
@end
|
||||
58
SweetParty/第三方库/KNPhotoBrowser/KNPhotoAVPlayer/KNPhotoAVPlayerActionBar.h
Executable file
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// KNPhotoAVPlayerActionBar.h
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 2019/6/14.
|
||||
// Copyright © 2019 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol KNPhotoAVPlayerActionBarDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
/**
|
||||
actionBar pause or stop btn did click
|
||||
|
||||
@param isNeedPlay isNeedPlay
|
||||
*/
|
||||
- (void)photoAVPlayerActionBarClickWithIsPlay:(BOOL)isNeedPlay;
|
||||
|
||||
/**
|
||||
actionBar value has changed by slider
|
||||
|
||||
@param value value
|
||||
*/
|
||||
- (void)photoAVPlayerActionBarChangeValue:(float)value;
|
||||
|
||||
@end
|
||||
|
||||
@interface KNPhotoAVPlayerActionBar : UIView
|
||||
|
||||
/**
|
||||
current play time of the video
|
||||
*/
|
||||
@property (nonatomic,assign) float currentTime;
|
||||
|
||||
/**
|
||||
duration of the video
|
||||
*/
|
||||
@property (nonatomic,assign) float allDuration;
|
||||
|
||||
@property (nonatomic,weak ) id<KNPhotoAVPlayerActionBarDelegate> delegate;
|
||||
|
||||
/**
|
||||
setter or getter of isPlaying of ActionBar
|
||||
*/
|
||||
@property (nonatomic,assign) BOOL isPlaying;
|
||||
|
||||
/**
|
||||
reset all information of ActionBar
|
||||
*/
|
||||
- (void)resetActionBarAllInfo;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
220
SweetParty/第三方库/KNPhotoBrowser/KNPhotoAVPlayer/KNPhotoAVPlayerActionBar.m
Executable file
@@ -0,0 +1,220 @@
|
||||
//
|
||||
// KNPhotoAVPlayerActionBar.m
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 2019/6/14.
|
||||
// Copyright © 2019 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import "KNPhotoAVPlayerActionBar.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@interface KNPhotoAVPlayerSlider : UISlider
|
||||
|
||||
@end
|
||||
|
||||
@implementation KNPhotoAVPlayerSlider
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
NSBundle *bundle = [NSBundle bundleForClass:NSClassFromString(@"KNPhotoBrowser")];
|
||||
if(UIScreen.mainScreen.scale < 3) {
|
||||
[self setThumbImage:[UIImage imageNamed:@"KNPhotoBrowser.bundle/circlePoint@2x.png" inBundle:bundle compatibleWithTraitCollection:nil] forState:UIControlStateNormal];
|
||||
}else {
|
||||
[self setThumbImage:[UIImage imageNamed:@"KNPhotoBrowser.bundle/circlePoint@3x.png" inBundle:bundle compatibleWithTraitCollection:nil] forState:UIControlStateNormal];
|
||||
}
|
||||
[self setMinimumTrackTintColor:[UIColor whiteColor]];
|
||||
[self setMaximumTrackTintColor:[[UIColor whiteColor] colorWithAlphaComponent:0.5]];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CGRect)minimumValueImageRectForBounds:(CGRect)bounds{
|
||||
CGRect frame = [super minimumValueImageRectForBounds:bounds];
|
||||
return CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, 15);
|
||||
}
|
||||
|
||||
- (CGRect)maximumValueImageRectForBounds:(CGRect)bounds{
|
||||
CGRect frame = [super maximumValueImageRectForBounds:bounds];
|
||||
return CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, 15);
|
||||
}
|
||||
|
||||
- (CGRect)trackRectForBounds:(CGRect)bounds{
|
||||
CGRect frame = [super trackRectForBounds:bounds];
|
||||
return CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, 3);
|
||||
}
|
||||
|
||||
- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value {
|
||||
CGRect frame = [super thumbRectForBounds:bounds trackRect:rect value:value];
|
||||
return CGRectMake(frame.origin.x - 10, frame.origin.y - 10, frame.size.width + 20, frame.size.height + 20);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface KNPhotoAVPlayerActionBar ()
|
||||
|
||||
@property (nonatomic,strong) UIButton *pauseStopBtn;
|
||||
@property (nonatomic,strong) UILabel *preTimeLabel;
|
||||
@property (nonatomic,strong) UILabel *endTimeLabel;
|
||||
@property (nonatomic,strong) KNPhotoAVPlayerSlider *slider;
|
||||
@property (nonatomic, strong) NSBundle *bundle;
|
||||
|
||||
@end
|
||||
|
||||
@implementation KNPhotoAVPlayerActionBar {
|
||||
BOOL _isDragging;
|
||||
}
|
||||
|
||||
/****************************** == lazy == ********************************/
|
||||
|
||||
- (NSBundle *)bundle {
|
||||
if (!_bundle) {
|
||||
_bundle = [NSBundle bundleForClass:NSClassFromString(@"KNPhotoBrowser")];
|
||||
}
|
||||
return _bundle;
|
||||
}
|
||||
|
||||
- (UIButton *)pauseStopBtn{
|
||||
if (!_pauseStopBtn) {
|
||||
_pauseStopBtn = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
if(UIScreen.mainScreen.scale < 3) {
|
||||
[_pauseStopBtn setImage:[UIImage imageNamed:@"KNPhotoBrowser.bundle/pause@2x.png" inBundle:self.bundle compatibleWithTraitCollection:nil] forState:UIControlStateNormal];
|
||||
}else {
|
||||
[_pauseStopBtn setImage:[UIImage imageNamed:@"KNPhotoBrowser.bundle/pause@3x.png" inBundle:self.bundle compatibleWithTraitCollection:nil] forState:UIControlStateNormal];
|
||||
}
|
||||
[_pauseStopBtn addTarget:self action:@selector(pauseStopBtnDidClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
return _pauseStopBtn;
|
||||
}
|
||||
|
||||
- (UILabel *)preTimeLabel{
|
||||
if (!_preTimeLabel) {
|
||||
_preTimeLabel = [UILabel new];
|
||||
[_preTimeLabel setText:@"00:00"];
|
||||
[_preTimeLabel setFont:[UIFont systemFontOfSize:11]];
|
||||
[_preTimeLabel setTextAlignment:NSTextAlignmentCenter];
|
||||
[_preTimeLabel setAdjustsFontSizeToFitWidth:true];
|
||||
[_preTimeLabel setTextColor:UIColor.whiteColor];
|
||||
}
|
||||
return _preTimeLabel;
|
||||
}
|
||||
|
||||
- (UILabel *)endTimeLabel{
|
||||
if (!_endTimeLabel) {
|
||||
_endTimeLabel = [[UILabel alloc] init];
|
||||
[_endTimeLabel setText:self.preTimeLabel.text];
|
||||
[_endTimeLabel setFont:self.preTimeLabel.font];
|
||||
[_endTimeLabel setTextColor:self.preTimeLabel.textColor];
|
||||
[_endTimeLabel setAdjustsFontSizeToFitWidth:true];
|
||||
[_endTimeLabel setTextAlignment:self.preTimeLabel.textAlignment];
|
||||
}
|
||||
return _endTimeLabel;
|
||||
}
|
||||
|
||||
- (KNPhotoAVPlayerSlider *)slider{
|
||||
if (!_slider) {
|
||||
_slider = [[KNPhotoAVPlayerSlider alloc] init];
|
||||
[_slider addTarget:self action:@selector(actionBarSliderFinished:) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchCancel | UIControlEventTouchUpOutside];
|
||||
[_slider addTarget:self action:@selector(actionBarSliderDown:) forControlEvents:UIControlEventTouchDown];
|
||||
}
|
||||
return _slider;
|
||||
}
|
||||
|
||||
/****************************** == lazy == ********************************/
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
self.layer.cornerRadius = 5;
|
||||
self.clipsToBounds = true;
|
||||
[self addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(dragViewMoved:)]];
|
||||
[self addSubview:self.pauseStopBtn];
|
||||
[self addSubview:self.preTimeLabel];
|
||||
[self addSubview:self.endTimeLabel];
|
||||
[self addSubview:self.slider];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dragViewMoved:(UIPanGestureRecognizer *)panGestureRecognizer{
|
||||
|
||||
}
|
||||
|
||||
- (void)pauseStopBtnDidClick{
|
||||
if ([_delegate respondsToSelector:@selector(photoAVPlayerActionBarClickWithIsPlay:)]) {
|
||||
[_delegate photoAVPlayerActionBarClickWithIsPlay:!_isPlaying];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)actionBarSliderFinished:(KNPhotoAVPlayerSlider *)slider{
|
||||
if ([_delegate respondsToSelector:@selector(photoAVPlayerActionBarChangeValue:)]) {
|
||||
[_delegate photoAVPlayerActionBarChangeValue:slider.value];
|
||||
}
|
||||
_isDragging = false;
|
||||
[_slider setUserInteractionEnabled:true];
|
||||
}
|
||||
- (void)actionBarSliderDown:(KNPhotoAVPlayerSlider *)slider{
|
||||
_isDragging = true;
|
||||
[slider setUserInteractionEnabled:false];
|
||||
}
|
||||
|
||||
- (void)setIsPlaying:(BOOL)isPlaying{
|
||||
_isPlaying = isPlaying;
|
||||
|
||||
if (isPlaying) {
|
||||
_isDragging = false;
|
||||
[_slider setUserInteractionEnabled:true];
|
||||
if(UIScreen.mainScreen.scale < 3) {
|
||||
[_pauseStopBtn setImage:[UIImage imageNamed:@"KNPhotoBrowser.bundle/pause@2x.png" inBundle:self.bundle compatibleWithTraitCollection:nil] forState:UIControlStateNormal];
|
||||
}else {
|
||||
[_pauseStopBtn setImage:[UIImage imageNamed:@"KNPhotoBrowser.bundle/pause@3x.png" inBundle:self.bundle compatibleWithTraitCollection:nil] forState:UIControlStateNormal];
|
||||
}
|
||||
}else{
|
||||
if(UIScreen.mainScreen.scale < 3) {
|
||||
[_pauseStopBtn setImage:[UIImage imageNamed:@"KNPhotoBrowser.bundle/play@2x.png" inBundle:self.bundle compatibleWithTraitCollection:nil] forState:UIControlStateNormal];
|
||||
}else {
|
||||
[_pauseStopBtn setImage:[UIImage imageNamed:@"KNPhotoBrowser.bundle/play@3x.png" inBundle:self.bundle compatibleWithTraitCollection:nil] forState:UIControlStateNormal];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setCurrentTime:(float)currentTime{
|
||||
if (isnan(currentTime)) return;
|
||||
_currentTime = currentTime;
|
||||
[_preTimeLabel setText:[self caluTimeFormatWithSeconds:currentTime]];
|
||||
|
||||
if (_isDragging == false) {
|
||||
[_slider setValue:currentTime animated:false];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setAllDuration:(float)allDuration{
|
||||
if (isnan(allDuration)) return;
|
||||
_allDuration = allDuration;
|
||||
_slider.maximumValue = allDuration;
|
||||
[_endTimeLabel setText:[self caluTimeFormatWithSeconds:allDuration]];
|
||||
}
|
||||
|
||||
- (NSString *)caluTimeFormatWithSeconds:(NSInteger)seconds{
|
||||
if(seconds > 60 * 60){ // one hour or more than
|
||||
return [NSString stringWithFormat:@"%02zd:%02zd:%02zd",(NSInteger)(seconds / 3600),(NSInteger)((seconds % 3600) / 60) , (NSInteger)(seconds % 60)];
|
||||
}else{ // in one hour
|
||||
return [NSString stringWithFormat:@"%02zd:%02zd", (NSInteger)((seconds % 3600) / 60), (NSInteger)(seconds % 60)];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resetActionBarAllInfo{
|
||||
self.isPlaying = false;
|
||||
_preTimeLabel.text = @"00:00";
|
||||
_endTimeLabel.text = @"00:00";
|
||||
}
|
||||
|
||||
- (void)layoutSubviews{
|
||||
[super layoutSubviews];
|
||||
|
||||
self.pauseStopBtn.frame = CGRectMake(10, 10, 20, 20);
|
||||
self.preTimeLabel.frame = CGRectMake(30, 5, 55, 30);
|
||||
self.endTimeLabel.frame = CGRectMake(self.bounds.size.width - 55, 5, 55, 30);
|
||||
self.slider.frame = CGRectMake(85, 0, CGRectGetMinX(self.endTimeLabel.frame) - CGRectGetMaxX(self.preTimeLabel.frame), 40);
|
||||
}
|
||||
|
||||
@end
|
||||
59
SweetParty/第三方库/KNPhotoBrowser/KNPhotoAVPlayer/KNPhotoAVPlayerActionView.h
Executable file
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// KNPhotoAVPlayerActionView.h
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 2019/6/14.
|
||||
// Copyright © 2019 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol KNPhotoAVPlayerActionViewDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
/**
|
||||
actionView's Pause imageView
|
||||
*/
|
||||
- (void)photoAVPlayerActionViewPauseOrStop;
|
||||
|
||||
/**
|
||||
actionView's dismiss imageView
|
||||
*/
|
||||
- (void)photoAVPlayerActionViewDismiss;
|
||||
|
||||
/**
|
||||
actionView
|
||||
*/
|
||||
- (void)photoAVPlayerActionViewDidClickIsHidden:(BOOL)isHidden;
|
||||
|
||||
@end
|
||||
|
||||
@interface KNPhotoAVPlayerActionView : UIView
|
||||
|
||||
/**
|
||||
avPlayerActionView need hidden or not
|
||||
*/
|
||||
- (void)avplayerActionViewNeedHidden:(BOOL)isHidden;
|
||||
|
||||
@property (nonatomic,weak ) id<KNPhotoAVPlayerActionViewDelegate> delegate;
|
||||
|
||||
/**
|
||||
player is buffering or not
|
||||
*/
|
||||
@property (nonatomic,assign) BOOL isBuffering;
|
||||
|
||||
/**
|
||||
current player is playing
|
||||
*/
|
||||
@property (nonatomic,assign) BOOL isPlaying;
|
||||
|
||||
/**
|
||||
* current player is downloading
|
||||
*/
|
||||
@property (nonatomic,assign) BOOL isDownloading;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
162
SweetParty/第三方库/KNPhotoBrowser/KNPhotoAVPlayer/KNPhotoAVPlayerActionView.m
Executable file
@@ -0,0 +1,162 @@
|
||||
//
|
||||
// KNPhotoAVPlayerActionView.m
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 2019/6/14.
|
||||
// Copyright © 2019 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import "KNPhotoAVPlayerActionView.h"
|
||||
#import "KNPhotoBrowserPch.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@interface KNPhotoAVPlayerActionView()
|
||||
|
||||
/**
|
||||
stop || play view
|
||||
*/
|
||||
@property (nonatomic,weak ) UIImageView *pauseImgView;
|
||||
|
||||
/**
|
||||
dismiss view
|
||||
*/
|
||||
@property (nonatomic,weak ) UIImageView *dismissImgView;
|
||||
|
||||
/**
|
||||
loading view
|
||||
*/
|
||||
@property (nonatomic,strong) UIActivityIndicatorView *indicatorView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation KNPhotoAVPlayerActionView
|
||||
|
||||
- (UIActivityIndicatorView *)indicatorView{
|
||||
if (!_indicatorView) {
|
||||
_indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
|
||||
[_indicatorView setHidesWhenStopped:true];
|
||||
}
|
||||
return _indicatorView;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
[self setBackgroundColor:UIColor.clearColor];
|
||||
[self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(actionViewDidClick)]];
|
||||
[self setupSubViews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setupSubViews{
|
||||
|
||||
NSBundle *bundle = [NSBundle bundleForClass:NSClassFromString(@"KNPhotoBrowser")];
|
||||
|
||||
// 1.stop || play imageView
|
||||
UIImageView *pauseImgView = [[UIImageView alloc] init];
|
||||
[pauseImgView setUserInteractionEnabled:true];
|
||||
[pauseImgView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(pauseImageViewDidClick)]];
|
||||
if(UIScreen.mainScreen.scale < 3) {
|
||||
[pauseImgView setImage:[UIImage imageNamed:@"KNPhotoBrowser.bundle/playCenter@2x" inBundle:bundle compatibleWithTraitCollection:nil]];
|
||||
}else {
|
||||
[pauseImgView setImage:[UIImage imageNamed:@"KNPhotoBrowser.bundle/playCenter@3x" inBundle:bundle compatibleWithTraitCollection:nil]];
|
||||
}
|
||||
[self addSubview:pauseImgView];
|
||||
_pauseImgView = pauseImgView;
|
||||
|
||||
// 2.dismiss imageView
|
||||
UIImageView *dismissImageView = [[UIImageView alloc] init];
|
||||
[dismissImageView setUserInteractionEnabled:true];
|
||||
[dismissImageView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissImageViewDidClick)]];
|
||||
if(UIScreen.mainScreen.scale < 3) {
|
||||
[dismissImageView setImage:[UIImage imageNamed:@"KNPhotoBrowser.bundle/dismiss@2x" inBundle:bundle compatibleWithTraitCollection:nil]];
|
||||
}else {
|
||||
[dismissImageView setImage:[UIImage imageNamed:@"KNPhotoBrowser.bundle/dismiss@3x" inBundle:bundle compatibleWithTraitCollection:nil]];
|
||||
}
|
||||
[dismissImageView setHidden:true];
|
||||
[self addSubview:dismissImageView];
|
||||
_dismissImgView = dismissImageView;
|
||||
|
||||
// 3.loading imageView
|
||||
[self addSubview:self.indicatorView];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews{
|
||||
[super layoutSubviews];
|
||||
_pauseImgView.frame = CGRectMake((self.frame.size.width - 80) * 0.5, (self.frame.size.height - 80) * 0.5, 80, 80);
|
||||
|
||||
|
||||
CGFloat y = 25;
|
||||
CGFloat x = 10;
|
||||
if(PBDeviceHasBang){
|
||||
y = 45;
|
||||
x = 20;
|
||||
}
|
||||
|
||||
if(!isPortrait){
|
||||
y = 15;
|
||||
x = 35;
|
||||
}
|
||||
_dismissImgView.frame = CGRectMake(x, y, 20, 20);
|
||||
_indicatorView.frame = CGRectMake((self.frame.size.width - 30) * 0.5, (self.frame.size.height - 30) * 0.5, 30, 30);
|
||||
}
|
||||
|
||||
- (void)pauseImageViewDidClick{
|
||||
|
||||
if (_pauseImgView.hidden == false) {
|
||||
_pauseImgView.hidden = true;
|
||||
}
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(photoAVPlayerActionViewPauseOrStop)]) {
|
||||
[_delegate photoAVPlayerActionViewPauseOrStop];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dismissImageViewDidClick{
|
||||
if ([_delegate respondsToSelector:@selector(photoAVPlayerActionViewDismiss)]) {
|
||||
[_delegate photoAVPlayerActionViewDismiss];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)actionViewDidClick{
|
||||
|
||||
[_dismissImgView setHidden:!_dismissImgView.hidden];
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(photoAVPlayerActionViewDidClickIsHidden:)]) {
|
||||
[_delegate photoAVPlayerActionViewDidClickIsHidden:_dismissImgView.isHidden];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
avPlayerActionView need hidden or not
|
||||
*/
|
||||
- (void)avplayerActionViewNeedHidden:(BOOL)isHidden{
|
||||
if (isHidden == true) {
|
||||
[_dismissImgView setHidden:true];
|
||||
[_indicatorView setHidden:true];
|
||||
[_pauseImgView setHidden:true];
|
||||
}else {
|
||||
[_pauseImgView setHidden:false];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setIsBuffering:(BOOL)isBuffering{
|
||||
_isBuffering = isBuffering;
|
||||
if (isBuffering) {
|
||||
[_indicatorView startAnimating];
|
||||
}else{
|
||||
[_indicatorView stopAnimating];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setIsPlaying:(BOOL)isPlaying{
|
||||
_isPlaying = isPlaying;
|
||||
[_pauseImgView setHidden:isPlaying];
|
||||
}
|
||||
|
||||
- (void)setIsDownloading:(BOOL)isDownloading{
|
||||
_isDownloading = isDownloading;
|
||||
[_pauseImgView setHidden:isDownloading];
|
||||
}
|
||||
|
||||
@end
|
||||
84
SweetParty/第三方库/KNPhotoBrowser/KNPhotoAVPlayer/KNPhotoAVPlayerView.h
Executable file
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// KNPhotoAVPlayerView.h
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 2019/6/14.
|
||||
// Copyright © 2019 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import "KNPhotoPlayerProtocol.h"
|
||||
#import "KNPhotoBrowser.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface KNPhotoAVPlayerView : UIView
|
||||
|
||||
/// create locate player with photoItems
|
||||
/// @param photoItems photoItem
|
||||
/// @param placeHolder placeHolder image
|
||||
- (void)playerOnLinePhotoItems:(KNPhotoItems *)photoItems placeHolder:(UIImage *_Nullable)placeHolder;
|
||||
|
||||
/// reset AVPlayer
|
||||
- (void)playerWillReset;
|
||||
|
||||
/// AVPlayer will be swiped by hand
|
||||
- (void)playerWillSwipe;
|
||||
|
||||
/// AVPlayer will cancel swipe
|
||||
- (void)playerWillSwipeCancel;
|
||||
|
||||
/// AVPlayer play as rate
|
||||
/// @param rate rate
|
||||
- (void)playerRate:(CGFloat)rate;
|
||||
|
||||
/**
|
||||
* is or not need Video placeHolder
|
||||
*/
|
||||
@property (nonatomic,assign) BOOL isNeedVideoPlaceHolder;
|
||||
|
||||
/**
|
||||
* auto play when you need
|
||||
*/
|
||||
@property (nonatomic,assign) BOOL isNeedAutoPlay;
|
||||
|
||||
/**
|
||||
* player view
|
||||
*/
|
||||
@property (nonatomic,strong,nullable) UIView *playerView;
|
||||
|
||||
/**
|
||||
* player background view (as locate current location for swipe)
|
||||
*/
|
||||
@property (nonatomic,strong,nullable) UIView *playerBgView;
|
||||
|
||||
/**
|
||||
* placeHolder imageView
|
||||
*/
|
||||
@property (nonatomic,strong,nullable) UIImageView *placeHolderImgView;
|
||||
|
||||
/**
|
||||
* layer of player
|
||||
*/
|
||||
@property (nonatomic,strong,nullable) AVPlayerLayer *playerLayer;
|
||||
|
||||
/**
|
||||
* if video has played ,even though one seconds : TRUE
|
||||
*/
|
||||
@property (nonatomic,assign) BOOL isBeginPlayed;
|
||||
|
||||
/**
|
||||
* default is solo ambient : TRUE `AVAudioSessionCategorySoloAmbient`
|
||||
* if set false, that will be `AVAudioSessionCategoryAmbient`
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL isSoloAmbient;
|
||||
|
||||
/**
|
||||
* delegate
|
||||
*/
|
||||
@property (nonatomic,weak ) id<KNPhotoPlayerViewDelegate> delegate;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
361
SweetParty/第三方库/KNPhotoBrowser/KNPhotoAVPlayer/KNPhotoAVPlayerView.m
Executable file
@@ -0,0 +1,361 @@
|
||||
//
|
||||
// KNPhotoAVPlayerView.m
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 2019/6/14.
|
||||
// Copyright © 2019 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import "KNPhotoAVPlayerView.h"
|
||||
#import "KNPhotoAVPlayerActionBar.h"
|
||||
#import "KNPhotoAVPlayerActionView.h"
|
||||
#import "KNPhotoBrowserPch.h"
|
||||
|
||||
@interface KNPhotoAVPlayerView ()<KNPhotoAVPlayerActionViewDelegate,KNPhotoAVPlayerActionBarDelegate>
|
||||
|
||||
@property (nonatomic,strong) AVPlayer *player;
|
||||
@property (nonatomic,strong) AVPlayerItem *item;
|
||||
|
||||
@property (nonatomic,strong) KNPhotoAVPlayerActionView *actionView;
|
||||
@property (nonatomic,strong) KNPhotoAVPlayerActionBar *actionBar;
|
||||
|
||||
@property (nonatomic,copy ) NSString *url;
|
||||
@property (nonatomic,strong) UIImage *placeHolder;
|
||||
|
||||
@property (nonatomic,strong) id timeObserver;
|
||||
|
||||
@property (nonatomic,assign) BOOL isPlaying;
|
||||
@property (nonatomic,assign) BOOL isGetAllPlayItem;
|
||||
@property (nonatomic,assign) BOOL isDragging;
|
||||
@property (nonatomic,assign) BOOL isEnterBackground;
|
||||
@property (nonatomic,assign) BOOL isAddObserver;
|
||||
|
||||
@end
|
||||
|
||||
@implementation KNPhotoAVPlayerView
|
||||
|
||||
- (KNPhotoAVPlayerActionView *)actionView{
|
||||
if (!_actionView) {
|
||||
_actionView = [[KNPhotoAVPlayerActionView alloc] init];
|
||||
[_actionView addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(photoAVPlayerActionViewDidLongPress:)]];
|
||||
_actionView.delegate = self;
|
||||
_actionView.isBuffering = false;
|
||||
_actionView.isPlaying = false;
|
||||
}
|
||||
return _actionView;
|
||||
}
|
||||
- (KNPhotoAVPlayerActionBar *)actionBar{
|
||||
if (!_actionBar) {
|
||||
_actionBar = [[KNPhotoAVPlayerActionBar alloc] init];
|
||||
_actionBar.backgroundColor = [UIColor colorWithRed:45/255.0 green:45/255.0 blue:45/255.0 alpha:1.];
|
||||
_actionBar.delegate = self;
|
||||
_actionBar.isPlaying = false;
|
||||
_actionBar.hidden = true;
|
||||
}
|
||||
return _actionBar;
|
||||
}
|
||||
|
||||
- (UIView *)playerBgView{
|
||||
if (!_playerBgView) {
|
||||
_playerBgView = [[UIView alloc] init];
|
||||
}
|
||||
return _playerBgView;
|
||||
}
|
||||
- (UIView *)playerView{
|
||||
if (!_playerView) {
|
||||
_playerView = [[UIView alloc] init];
|
||||
_playerView.backgroundColor = UIColor.clearColor;
|
||||
}
|
||||
return _playerView;
|
||||
}
|
||||
- (UIImageView *)placeHolderImgView{
|
||||
if (!_placeHolderImgView) {
|
||||
_placeHolderImgView = [[UIImageView alloc] init];
|
||||
_placeHolderImgView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
}
|
||||
return _placeHolderImgView;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
self.player = [AVPlayer playerWithPlayerItem:_item];
|
||||
self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];
|
||||
[self.playerView.layer addSublayer:_playerLayer];
|
||||
|
||||
[self.playerBgView addSubview:self.placeHolderImgView];
|
||||
[self.playerBgView addSubview:self.playerView];
|
||||
|
||||
[self addSubview:self.playerBgView];
|
||||
[self addSubview:self.actionView];
|
||||
[self addSubview:self.actionBar];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)playerOnLinePhotoItems:(KNPhotoItems *)photoItems placeHolder:(UIImage *_Nullable)placeHolder{
|
||||
|
||||
[self removePlayerItemObserver];
|
||||
[self removeTimeObserver];
|
||||
[self addObserverAndAudioSession];
|
||||
|
||||
_url = photoItems.url;
|
||||
_placeHolder = placeHolder;
|
||||
|
||||
if (placeHolder) {
|
||||
_placeHolderImgView.image = placeHolder;
|
||||
}
|
||||
|
||||
if ([photoItems.url hasPrefix:@"http"]) {
|
||||
_item = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:photoItems.url]];
|
||||
}else {
|
||||
_item = [AVPlayerItem playerItemWithAsset:[AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:_url] options:nil]];
|
||||
}
|
||||
|
||||
_item.canUseNetworkResourcesForLiveStreamingWhilePaused = true;
|
||||
[self.player replaceCurrentItemWithPlayerItem:_item];
|
||||
|
||||
[_actionView avplayerActionViewNeedHidden:false];
|
||||
|
||||
_isEnterBackground = _isAddObserver = _isDragging = _isPlaying = false;
|
||||
|
||||
[self addPlayerItemObserver];
|
||||
|
||||
/// default rate
|
||||
_player.rate = 1.0;
|
||||
|
||||
[_player pause];
|
||||
}
|
||||
|
||||
- (void)addObserverAndAudioSession{
|
||||
// AudioSession setting
|
||||
AVAudioSession *session = [AVAudioSession sharedInstance];
|
||||
[session setActive:true error:nil];
|
||||
if(_isSoloAmbient == true) {
|
||||
[session setCategory:AVAudioSessionCategorySoloAmbient error:nil];
|
||||
}else {
|
||||
[session setCategory:AVAudioSessionCategoryAmbient error:nil];
|
||||
}
|
||||
|
||||
// Notification
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive) name:UIApplicationWillResignActiveNotification object:nil];
|
||||
}
|
||||
|
||||
/// notification function
|
||||
- (void)applicationWillResignActive{
|
||||
_isEnterBackground = true;
|
||||
if (_isPlaying) [self photoAVPlayerActionBarClickWithIsPlay:false];
|
||||
}
|
||||
|
||||
/// remove item observer
|
||||
- (void)removePlayerItemObserver{
|
||||
if (_item && _isAddObserver) {
|
||||
[_item removeObserver:self forKeyPath:@"status" context:nil];
|
||||
[_item removeObserver:self forKeyPath:@"loadedTimeRanges" context:nil];
|
||||
_isAddObserver = false;
|
||||
}
|
||||
}
|
||||
/// add item observer
|
||||
- (void)addPlayerItemObserver{
|
||||
if (_item) {
|
||||
[_item addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
|
||||
[_item addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
|
||||
_isAddObserver = true;
|
||||
}
|
||||
|
||||
__weak typeof(self) weakself = self;
|
||||
self.timeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
|
||||
weakself.isBeginPlayed = true;
|
||||
__strong typeof(weakself) strongself = weakself;
|
||||
if (CMTimeGetSeconds(time) == strongself.actionBar.allDuration) {
|
||||
if (strongself.actionBar.allDuration != 0) {
|
||||
[strongself videoDidPlayToEndTime];
|
||||
}
|
||||
strongself.actionBar.currentTime = 0;
|
||||
}else{
|
||||
if (strongself.isDragging == true) {
|
||||
strongself.isDragging = false;
|
||||
return;
|
||||
}
|
||||
strongself.actionBar.currentTime = CMTimeGetSeconds(time);
|
||||
}
|
||||
}];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(videoDidPlayToEndTime)
|
||||
name:AVPlayerItemDidPlayToEndTimeNotification
|
||||
object:nil];
|
||||
|
||||
}
|
||||
|
||||
/// remove time observer
|
||||
- (void)removeTimeObserver{
|
||||
if (_timeObserver && _player) {
|
||||
@try {
|
||||
[_player removeTimeObserver: _timeObserver];
|
||||
} @catch (NSException *exception) {
|
||||
|
||||
} @finally {
|
||||
_timeObserver = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)videoDidPlayToEndTime{
|
||||
_isGetAllPlayItem = false;
|
||||
_isPlaying = false;
|
||||
if (_player) {
|
||||
__weak typeof(self) weakself = self;
|
||||
if (_player.currentItem.status == AVPlayerStatusReadyToPlay) {
|
||||
[_player seekToTime:CMTimeMake(1, 1) completionHandler:^(BOOL finished) {
|
||||
if (finished) {
|
||||
weakself.actionBar.currentTime = 0;
|
||||
weakself.actionBar.isPlaying = false;
|
||||
weakself.actionView.isPlaying = false;
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
|
||||
|
||||
if (object != self.item) return;
|
||||
|
||||
if (_isEnterBackground) return;
|
||||
|
||||
if ([keyPath isEqualToString:@"status"]) { // play
|
||||
if (_player.currentItem.status == AVPlayerStatusReadyToPlay) {
|
||||
_actionView.isBuffering = false;
|
||||
}
|
||||
}else if ([keyPath isEqualToString:@"loadedTimeRanges"]) { // buffering
|
||||
if (!_isGetAllPlayItem) {
|
||||
_isGetAllPlayItem = true;
|
||||
_actionBar.allDuration = CMTimeGetSeconds(_player.currentItem.duration);
|
||||
}
|
||||
} else {
|
||||
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
||||
}
|
||||
}
|
||||
|
||||
/// function
|
||||
- (void)playerWillReset{
|
||||
[_player pause];
|
||||
_isPlaying = false;
|
||||
[self removeTimeObserver];
|
||||
[self removePlayerItemObserver];
|
||||
}
|
||||
- (void)playerWillSwipe{
|
||||
[_actionView avplayerActionViewNeedHidden:true];
|
||||
_actionBar.hidden = true;
|
||||
}
|
||||
|
||||
- (void)playerWillSwipeCancel {
|
||||
|
||||
}
|
||||
|
||||
- (void)playerRate:(CGFloat)rate{
|
||||
if (_isPlaying == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
_player.rate = rate;
|
||||
}
|
||||
|
||||
/// setter
|
||||
- (void)setIsNeedAutoPlay:(BOOL)isNeedAutoPlay {
|
||||
_isNeedAutoPlay = isNeedAutoPlay;
|
||||
if (isNeedAutoPlay) {
|
||||
_actionView.isBuffering = true;
|
||||
[self photoAVPlayerActionViewPauseOrStop];
|
||||
}
|
||||
}
|
||||
- (void)setIsNeedVideoPlaceHolder:(BOOL)isNeedVideoPlaceHolder{
|
||||
_isNeedVideoPlaceHolder = isNeedVideoPlaceHolder;
|
||||
self.placeHolderImgView.hidden = !isNeedVideoPlaceHolder;
|
||||
}
|
||||
|
||||
- (void)photoAVPlayerActionViewDidLongPress:(UILongPressGestureRecognizer *)longPress{
|
||||
if (_isPlaying == false) {
|
||||
return;
|
||||
}
|
||||
if ([_delegate respondsToSelector:@selector(photoPlayerLongPress:)]) {
|
||||
[_delegate photoPlayerLongPress:longPress];
|
||||
}
|
||||
}
|
||||
|
||||
/// delegate
|
||||
/**
|
||||
actionView's Pause imageView
|
||||
*/
|
||||
- (void)photoAVPlayerActionViewPauseOrStop{
|
||||
_isEnterBackground = false;
|
||||
if (_isPlaying == false) {
|
||||
[_player play];
|
||||
_actionBar.isPlaying = true;
|
||||
_actionView.isPlaying = true;
|
||||
}else {
|
||||
[_player pause];
|
||||
_actionView.isPlaying = false;
|
||||
_actionBar.isPlaying = false;
|
||||
}
|
||||
|
||||
_isPlaying = !_isPlaying;
|
||||
}
|
||||
- (void)photoAVPlayerActionViewDismiss{
|
||||
if ([_delegate respondsToSelector:@selector(photoPlayerViewDismiss)]) {
|
||||
[_delegate photoPlayerViewDismiss];
|
||||
}
|
||||
}
|
||||
- (void)photoAVPlayerActionViewDidClickIsHidden:(BOOL)isHidden{
|
||||
[_actionBar setHidden:isHidden];
|
||||
}
|
||||
- (void)photoAVPlayerActionBarClickWithIsPlay:(BOOL)isNeedPlay{
|
||||
if (isNeedPlay) {
|
||||
[_player play];
|
||||
_actionView.isPlaying = true;
|
||||
_actionBar.isPlaying = true;
|
||||
_isPlaying = true;
|
||||
}else {
|
||||
[_player pause];
|
||||
_actionView.isPlaying = false;
|
||||
_actionBar.isPlaying = false;
|
||||
_isPlaying = false;
|
||||
}
|
||||
}
|
||||
- (void)photoAVPlayerActionBarChangeValue:(float)value{
|
||||
_isDragging = true;
|
||||
[_player seekToTime:CMTimeMake(value, 1) completionHandler:^(BOOL finished) {
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews{
|
||||
[super layoutSubviews];
|
||||
|
||||
self.playerBgView.frame = CGRectMake(10, 0, self.frame.size.width - 20, self.frame.size.height);
|
||||
self.playerView.frame = self.playerBgView.bounds;
|
||||
self.playerLayer.frame = self.playerView.bounds;
|
||||
self.actionView.frame = self.playerBgView.frame;
|
||||
self.placeHolderImgView.frame = self.playerBgView.bounds;
|
||||
|
||||
if (PBDeviceHasBang) {
|
||||
self.actionBar.frame = CGRectMake(15, self.frame.size.height - 70, self.frame.size.width - 30, 40);
|
||||
}else {
|
||||
self.actionBar.frame = CGRectMake(15, self.frame.size.height - 50, self.frame.size.width - 30, 40);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dealloc{
|
||||
[self removeObserverAndAudioSesstion];
|
||||
if (_player && self.timeObserver) {
|
||||
[_player removeTimeObserver:self.timeObserver];
|
||||
self.timeObserver = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeObserverAndAudioSesstion{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[[AVAudioSession sharedInstance] setActive:false withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
95
SweetParty/第三方库/KNPhotoBrowser/KNPhotoAVPlayer/KNPhotoLocateAVPlayerView.h
Executable file
@@ -0,0 +1,95 @@
|
||||
//
|
||||
// KNPhotoLocateAVPlayerView.h
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 2021/5/12.
|
||||
// Copyright © 2021 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import "KNPhotoPlayerProtocol.h"
|
||||
#import "KNPhotoBrowser.h"
|
||||
#import "KNPhotoDownloadMgr.h"
|
||||
|
||||
@class KNProgressHUD;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface KNPhotoLocateAVPlayerView : UIView
|
||||
|
||||
/// create locate player with photoItems
|
||||
/// @param photoItems photoItems
|
||||
/// @param progressHUD progressHUD
|
||||
/// @param placeHolder placeHolder image
|
||||
- (void)playerLocatePhotoItems:(KNPhotoItems *)photoItems progressHUD:(KNProgressHUD *)progressHUD placeHolder:(UIImage *_Nullable)placeHolder;
|
||||
|
||||
/// reset AVPlayer
|
||||
- (void)playerWillReset;
|
||||
|
||||
/// AVPlayer will be swiped by hand
|
||||
- (void)playerWillSwipe;
|
||||
|
||||
/// AVPlayer will cancel swipe
|
||||
- (void)playerWillSwipeCancel;
|
||||
|
||||
/// AVPlayer play as rate
|
||||
/// @param rate rate
|
||||
- (void)playerRate:(CGFloat)rate;
|
||||
|
||||
/// when dismiss, should cancel download task first
|
||||
- (void)cancelDownloadMgrTask;
|
||||
|
||||
/// playerdownload
|
||||
/// @param downloadBlock download callBack
|
||||
- (void)playerDownloadBlock:(PhotoDownLoadBlock)downloadBlock;
|
||||
|
||||
/**
|
||||
* is or not need Video placeHolder
|
||||
*/
|
||||
@property (nonatomic,assign) BOOL isNeedVideoPlaceHolder;
|
||||
|
||||
/**
|
||||
* auto play when you need
|
||||
*/
|
||||
@property (nonatomic,assign) BOOL isNeedAutoPlay;
|
||||
|
||||
/**
|
||||
* player view
|
||||
*/
|
||||
@property (nonatomic,strong,nullable) UIView *playerView;
|
||||
|
||||
/**
|
||||
* player background view (as locate current location for swipe)
|
||||
*/
|
||||
@property (nonatomic,strong,nullable) UIView *playerBgView;
|
||||
|
||||
/**
|
||||
* placeHolder imageView
|
||||
*/
|
||||
@property (nonatomic,strong,nullable) UIImageView *placeHolderImgView;
|
||||
|
||||
/**
|
||||
* layer of player
|
||||
*/
|
||||
@property (nonatomic,strong,nullable) AVPlayerLayer *playerLayer;
|
||||
|
||||
/**
|
||||
* if video has played ,even though one seconds : TRUE
|
||||
*/
|
||||
@property (nonatomic,assign) BOOL isBeginPlayed;
|
||||
|
||||
/**
|
||||
* default is solo ambient : TRUE `AVAudioSessionCategorySoloAmbient`
|
||||
* if set false, that will be `AVAudioSessionCategoryAmbient`
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL isSoloAmbient;
|
||||
|
||||
/**
|
||||
* delegate
|
||||
*/
|
||||
@property (nonatomic,weak ) id<KNPhotoPlayerViewDelegate> delegate;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
454
SweetParty/第三方库/KNPhotoBrowser/KNPhotoAVPlayer/KNPhotoLocateAVPlayerView.m
Executable file
@@ -0,0 +1,454 @@
|
||||
//
|
||||
// KNPhotoLocateAVPlayerView.m
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 2021/5/12.
|
||||
// Copyright © 2021 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import "KNPhotoLocateAVPlayerView.h"
|
||||
#import "KNPhotoAVPlayerActionBar.h"
|
||||
#import "KNPhotoAVPlayerActionView.h"
|
||||
#import "KNPhotoBrowserPch.h"
|
||||
#import "KNProgressHUD.h"
|
||||
#import "KNReachability.h"
|
||||
|
||||
@interface KNPhotoLocateAVPlayerView()<KNPhotoAVPlayerActionViewDelegate,KNPhotoAVPlayerActionBarDelegate>
|
||||
|
||||
@property (nonatomic,strong) AVPlayer *player;
|
||||
@property (nonatomic,strong) AVPlayerItem *item;
|
||||
|
||||
@property (nonatomic,strong) KNPhotoAVPlayerActionView *actionView;
|
||||
@property (nonatomic,strong) KNPhotoAVPlayerActionBar *actionBar;
|
||||
|
||||
@property (nonatomic,copy ) NSString *url;
|
||||
@property (nonatomic,strong) UIImage *placeHolder;
|
||||
|
||||
@property (nonatomic,strong) id timeObserver;
|
||||
|
||||
@property (nonatomic,assign) BOOL isPlaying;
|
||||
@property (nonatomic,assign) BOOL isGetAllPlayItem;
|
||||
@property (nonatomic,assign) BOOL isDragging;
|
||||
@property (nonatomic,assign) BOOL isEnterBackground;
|
||||
@property (nonatomic,assign) BOOL isAddObserver;
|
||||
|
||||
@property (nonatomic,strong) KNPhotoDownloadMgr *downloadMgr;
|
||||
@property (nonatomic,strong) KNPhotoItems *photoItems;
|
||||
@property (nonatomic,weak ) KNProgressHUD *progressHUD;
|
||||
@property (nonatomic,copy ) PhotoDownLoadBlock downloadBlock;
|
||||
@end
|
||||
|
||||
@implementation KNPhotoLocateAVPlayerView
|
||||
|
||||
|
||||
- (KNPhotoAVPlayerActionView *)actionView{
|
||||
if (!_actionView) {
|
||||
_actionView = [[KNPhotoAVPlayerActionView alloc] init];
|
||||
[_actionView addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(photoAVPlayerActionViewDidLongPress:)]];
|
||||
_actionView.delegate = self;
|
||||
_actionView.isBuffering = false;
|
||||
_actionView.isPlaying = false;
|
||||
}
|
||||
return _actionView;
|
||||
}
|
||||
- (KNPhotoAVPlayerActionBar *)actionBar{
|
||||
if (!_actionBar) {
|
||||
_actionBar = [[KNPhotoAVPlayerActionBar alloc] init];
|
||||
_actionBar.backgroundColor = [UIColor colorWithRed:45/255.0 green:45/255.0 blue:45/255.0 alpha:1.];
|
||||
_actionBar.delegate = self;
|
||||
_actionBar.isPlaying = false;
|
||||
_actionBar.hidden = true;
|
||||
}
|
||||
return _actionBar;
|
||||
}
|
||||
|
||||
- (UIView *)playerBgView{
|
||||
if (!_playerBgView) {
|
||||
_playerBgView = [[UIView alloc] init];
|
||||
}
|
||||
return _playerBgView;
|
||||
}
|
||||
- (UIView *)playerView{
|
||||
if (!_playerView) {
|
||||
_playerView = [[UIView alloc] init];
|
||||
_playerView.backgroundColor = UIColor.clearColor;
|
||||
}
|
||||
return _playerView;
|
||||
}
|
||||
- (UIImageView *)placeHolderImgView{
|
||||
if (!_placeHolderImgView) {
|
||||
_placeHolderImgView = [[UIImageView alloc] init];
|
||||
_placeHolderImgView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
}
|
||||
return _placeHolderImgView;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
self.player = [AVPlayer playerWithPlayerItem:_item];
|
||||
self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];
|
||||
[self.playerView.layer addSublayer:_playerLayer];
|
||||
|
||||
[self.playerBgView addSubview:self.placeHolderImgView];
|
||||
[self.playerBgView addSubview:self.playerView];
|
||||
|
||||
[self addSubview:self.playerBgView];
|
||||
[self addSubview:self.actionView];
|
||||
[self addSubview:self.actionBar];
|
||||
|
||||
_downloadBlock = nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)playerLocatePhotoItems:(KNPhotoItems *)photoItems progressHUD:(KNProgressHUD *)progressHUD placeHolder:(UIImage *_Nullable)placeHolder{
|
||||
|
||||
[self cancelDownloadMgrTask];
|
||||
|
||||
[self removePlayerItemObserver];
|
||||
[self removeTimeObserver];
|
||||
[self addObserverAndAudioSession];
|
||||
|
||||
_downloadBlock = nil;
|
||||
|
||||
_url = photoItems.url;
|
||||
_placeHolder = placeHolder;
|
||||
_progressHUD = progressHUD;
|
||||
_photoItems = photoItems;
|
||||
|
||||
_downloadMgr = [[KNPhotoDownloadMgr alloc] init];
|
||||
|
||||
if (placeHolder) {
|
||||
_placeHolderImgView.image = placeHolder;
|
||||
}
|
||||
|
||||
_item = nil;
|
||||
|
||||
if ([photoItems.url hasPrefix:@"http"]) {
|
||||
|
||||
KNPhotoDownloadFileMgr *fileMgr = [[KNPhotoDownloadFileMgr alloc] init];
|
||||
if ([fileMgr startCheckIsExistVideo:photoItems]) {
|
||||
progressHUD.hidden = true;
|
||||
_actionView.isBuffering = true;
|
||||
|
||||
NSString *filePath = [fileMgr startGetFilePath:photoItems];
|
||||
_item = [AVPlayerItem playerItemWithAsset:[AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:filePath] options:nil]];
|
||||
}
|
||||
}else {
|
||||
progressHUD.hidden = true;
|
||||
_actionView.isBuffering = true;
|
||||
_item = [AVPlayerItem playerItemWithAsset:[AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:_url] options:nil]];
|
||||
}
|
||||
|
||||
_item.canUseNetworkResourcesForLiveStreamingWhilePaused = true;
|
||||
[self.player replaceCurrentItemWithPlayerItem:_item];
|
||||
|
||||
[_actionView avplayerActionViewNeedHidden:false];
|
||||
|
||||
_isEnterBackground = _isAddObserver = _isDragging = _isPlaying = false;
|
||||
|
||||
if (_item != nil) [self addPlayerItemObserver];
|
||||
|
||||
/// default rate
|
||||
_player.rate = 1.0;
|
||||
|
||||
[_player pause];
|
||||
}
|
||||
|
||||
- (void)addObserverAndAudioSession{
|
||||
// AudioSession setting
|
||||
AVAudioSession *session = [AVAudioSession sharedInstance];
|
||||
[session setActive:true error:nil];
|
||||
if(_isSoloAmbient == true) {
|
||||
[session setCategory:AVAudioSessionCategorySoloAmbient error:nil];
|
||||
}else {
|
||||
[session setCategory:AVAudioSessionCategoryAmbient error:nil];
|
||||
}
|
||||
|
||||
// Notification
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive) name:UIApplicationWillResignActiveNotification object:nil];
|
||||
}
|
||||
|
||||
/// notification function
|
||||
- (void)applicationWillResignActive{
|
||||
_isEnterBackground = true;
|
||||
if (_isPlaying) [self photoAVPlayerActionBarClickWithIsPlay:false];
|
||||
}
|
||||
|
||||
/// remove item observer
|
||||
- (void)removePlayerItemObserver{
|
||||
if (_item && _isAddObserver) {
|
||||
[_item removeObserver:self forKeyPath:@"status" context:nil];
|
||||
[_item removeObserver:self forKeyPath:@"loadedTimeRanges" context:nil];
|
||||
_isAddObserver = false;
|
||||
}
|
||||
}
|
||||
/// add item observer
|
||||
- (void)addPlayerItemObserver{
|
||||
if (_item) {
|
||||
[_item addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
|
||||
[_item addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
|
||||
_isAddObserver = true;
|
||||
}
|
||||
|
||||
__weak typeof(self) weakself = self;
|
||||
self.timeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
|
||||
weakself.isBeginPlayed = true;
|
||||
__strong typeof(weakself) strongself = weakself;
|
||||
if (CMTimeGetSeconds(time) == strongself.actionBar.allDuration) {
|
||||
if (strongself.actionBar.allDuration != 0) {
|
||||
[strongself videoDidPlayToEndTime];
|
||||
}
|
||||
strongself.actionBar.currentTime = 0;
|
||||
}else{
|
||||
if (strongself.isDragging == true) {
|
||||
strongself.isDragging = false;
|
||||
return;
|
||||
}
|
||||
strongself.actionBar.currentTime = CMTimeGetSeconds(time);
|
||||
}
|
||||
}];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(videoDidPlayToEndTime)
|
||||
name:AVPlayerItemDidPlayToEndTimeNotification
|
||||
object:nil];
|
||||
|
||||
}
|
||||
/// remove time observer
|
||||
- (void)removeTimeObserver{
|
||||
if (_timeObserver && _player) {
|
||||
@try {
|
||||
[_player removeTimeObserver: _timeObserver];
|
||||
} @catch (NSException *exception) {
|
||||
|
||||
} @finally {
|
||||
_timeObserver = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)videoDidPlayToEndTime{
|
||||
_isGetAllPlayItem = false;
|
||||
_isPlaying = false;
|
||||
if (_player) {
|
||||
__weak typeof(self) weakself = self;
|
||||
if (_player.currentItem.status == AVPlayerStatusReadyToPlay) {
|
||||
[_player seekToTime:CMTimeMake(1, 1) completionHandler:^(BOOL finished) {
|
||||
if (finished) {
|
||||
weakself.actionBar.currentTime = 0;
|
||||
weakself.actionBar.isPlaying = false;
|
||||
weakself.actionView.isPlaying = false;
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
|
||||
|
||||
if (object != self.item) return;
|
||||
|
||||
if (_isEnterBackground) return;
|
||||
|
||||
if ([keyPath isEqualToString:@"status"]) { // play
|
||||
if (_player.currentItem.status == AVPlayerStatusReadyToPlay) {
|
||||
_placeHolderImgView.hidden = true;
|
||||
}
|
||||
_actionView.isBuffering = false;
|
||||
}else if ([keyPath isEqualToString:@"loadedTimeRanges"]) { // buffering
|
||||
if (!_isGetAllPlayItem) {
|
||||
_isGetAllPlayItem = true;
|
||||
_actionBar.allDuration = CMTimeGetSeconds(_player.currentItem.duration);
|
||||
}
|
||||
_actionView.isBuffering = false;
|
||||
} else {
|
||||
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
||||
}
|
||||
}
|
||||
|
||||
/// function
|
||||
- (void)playerWillReset{
|
||||
[_player pause];
|
||||
_isPlaying = false;
|
||||
[self removeTimeObserver];
|
||||
[self removePlayerItemObserver];
|
||||
}
|
||||
- (void)playerWillSwipe{
|
||||
[_actionView avplayerActionViewNeedHidden:true];
|
||||
_actionBar.hidden = true;
|
||||
_progressHUD.hidden = true;
|
||||
}
|
||||
/// AVPlayer will cancel swipe
|
||||
- (void)playerWillSwipeCancel{
|
||||
KNPhotoDownloadFileMgr *fileMgr = [[KNPhotoDownloadFileMgr alloc] init];
|
||||
if ([self.photoItems.url hasPrefix:@"http"]) {
|
||||
if ([fileMgr startCheckIsExistVideo:self.photoItems] == false && _progressHUD.progress != 1.0) {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(PhotoBrowserAnimateTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
self->_progressHUD.hidden = false;
|
||||
});
|
||||
}else {
|
||||
_progressHUD.hidden = true;
|
||||
}
|
||||
}else {
|
||||
_progressHUD.hidden = true;
|
||||
}
|
||||
}
|
||||
- (void)playerRate:(CGFloat)rate{
|
||||
if (_isPlaying == false) {
|
||||
return;
|
||||
}
|
||||
_player.rate = rate;
|
||||
}
|
||||
/// when dismiss, should cancel download task first
|
||||
- (void)cancelDownloadMgrTask{
|
||||
if (_downloadMgr) [_downloadMgr cancelTask];
|
||||
}
|
||||
/// playerdownload
|
||||
/// @param downloadBlock download callBack
|
||||
- (void)playerDownloadBlock:(PhotoDownLoadBlock)downloadBlock{
|
||||
_downloadBlock = downloadBlock;
|
||||
}
|
||||
|
||||
/// setter
|
||||
- (void)setIsNeedAutoPlay:(BOOL)isNeedAutoPlay {
|
||||
_isNeedAutoPlay = isNeedAutoPlay;
|
||||
if (isNeedAutoPlay) {
|
||||
[self photoAVPlayerActionViewPauseOrStop];
|
||||
}
|
||||
}
|
||||
- (void)setIsNeedVideoPlaceHolder:(BOOL)isNeedVideoPlaceHolder{
|
||||
_isNeedVideoPlaceHolder = isNeedVideoPlaceHolder;
|
||||
self.placeHolderImgView.hidden = !isNeedVideoPlaceHolder;
|
||||
}
|
||||
|
||||
- (void)photoAVPlayerActionViewDidLongPress:(UILongPressGestureRecognizer *)longPress{
|
||||
if (_isPlaying == false) {
|
||||
return;
|
||||
}
|
||||
if ([_delegate respondsToSelector:@selector(photoPlayerLongPress:)]) {
|
||||
[_delegate photoPlayerLongPress:longPress];
|
||||
}
|
||||
}
|
||||
|
||||
/// delegate
|
||||
/**
|
||||
actionView's Pause imageView
|
||||
*/
|
||||
- (void)photoAVPlayerActionViewPauseOrStop{
|
||||
KNPhotoDownloadFileMgr *fileMgr = [[KNPhotoDownloadFileMgr alloc] init];
|
||||
if ([_photoItems.url hasPrefix:@"http"] == true && [fileMgr startCheckIsExistVideo:_photoItems] == false) {
|
||||
if (![[KNReachability reachabilityForInternetConnection] isReachable]) { // no network
|
||||
[_progressHUD setHidden:true];
|
||||
return;
|
||||
}
|
||||
_actionView.isDownloading = true;
|
||||
[_progressHUD setHidden:false];
|
||||
[_progressHUD setProgress:0.0];
|
||||
__weak typeof(self) weakself = self;
|
||||
[_downloadMgr downloadVideoWithPhotoItems:_photoItems downloadBlock:^(KNPhotoDownloadState downloadState, float progress) {
|
||||
[weakself.progressHUD setProgress:progress];
|
||||
if (downloadState == KNPhotoDownloadStateSuccess) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
KNPhotoDownloadFileMgr *manager = [[KNPhotoDownloadFileMgr alloc] init];
|
||||
NSString *filePath = [manager startGetFilePath:weakself.photoItems];
|
||||
weakself.item = [AVPlayerItem playerItemWithAsset:[AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:filePath] options:nil]];
|
||||
weakself.item.canUseNetworkResourcesForLiveStreamingWhilePaused = true;
|
||||
[weakself.player replaceCurrentItemWithPlayerItem:weakself.item];
|
||||
[weakself.player play];
|
||||
weakself.player.muted = true;
|
||||
|
||||
weakself.progressHUD.progress = 1.0;
|
||||
|
||||
weakself.isPlaying = true;
|
||||
weakself.actionBar.isPlaying = true;
|
||||
weakself.actionView.isBuffering = true;
|
||||
weakself.actionView.isPlaying = true;
|
||||
[weakself addPlayerItemObserver];
|
||||
});
|
||||
}
|
||||
if (downloadState == KNPhotoDownloadStateUnknow || downloadState == KNPhotoDownloadStateFailure) {
|
||||
[weakself.progressHUD setProgress:0.0];
|
||||
}
|
||||
if (weakself.downloadBlock) {
|
||||
weakself.downloadBlock(downloadState, progress);
|
||||
}
|
||||
}];
|
||||
}else {
|
||||
_progressHUD.hidden = true;
|
||||
if (_isPlaying == false) {
|
||||
[_player play];
|
||||
_player.muted = true;
|
||||
_actionBar.isPlaying = true;
|
||||
_actionView.isPlaying = true;
|
||||
}else {
|
||||
[_player pause];
|
||||
_actionView.isPlaying = false;
|
||||
_actionBar.isPlaying = false;
|
||||
}
|
||||
|
||||
_isPlaying = !_isPlaying;
|
||||
}
|
||||
_isEnterBackground = false;
|
||||
}
|
||||
- (void)photoAVPlayerActionViewDismiss{
|
||||
[self cancelDownloadMgrTask];
|
||||
if ([_delegate respondsToSelector:@selector(photoPlayerViewDismiss)]) {
|
||||
[_delegate photoPlayerViewDismiss];
|
||||
}
|
||||
}
|
||||
- (void)photoAVPlayerActionViewDidClickIsHidden:(BOOL)isHidden{
|
||||
[_actionBar setHidden:isHidden];
|
||||
}
|
||||
- (void)photoAVPlayerActionBarClickWithIsPlay:(BOOL)isNeedPlay{
|
||||
if (isNeedPlay) {
|
||||
[_player play];
|
||||
_player.muted = true;
|
||||
_actionView.isPlaying = true;
|
||||
_actionBar.isPlaying = true;
|
||||
_isPlaying = true;
|
||||
}else {
|
||||
[_player pause];
|
||||
_actionView.isPlaying = false;
|
||||
_actionBar.isPlaying = false;
|
||||
_isPlaying = false;
|
||||
}
|
||||
}
|
||||
- (void)photoAVPlayerActionBarChangeValue:(float)value{
|
||||
_isDragging = true;
|
||||
[_player seekToTime:CMTimeMake(value, 1) completionHandler:^(BOOL finished) {
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews{
|
||||
[super layoutSubviews];
|
||||
|
||||
self.playerBgView.frame = CGRectMake(10, 0, self.frame.size.width - 20, self.frame.size.height);
|
||||
self.playerView.frame = self.playerBgView.bounds;
|
||||
self.playerLayer.frame = self.playerView.bounds;
|
||||
self.actionView.frame = self.playerBgView.frame;
|
||||
self.placeHolderImgView.frame = self.playerBgView.bounds;
|
||||
|
||||
if (PBDeviceHasBang) {
|
||||
self.actionBar.frame = CGRectMake(15, self.frame.size.height - 70, self.frame.size.width - 30, 40);
|
||||
}else {
|
||||
self.actionBar.frame = CGRectMake(15, self.frame.size.height - 50, self.frame.size.width - 30, 40);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dealloc{
|
||||
[self removeObserverAndAudioSesstion];
|
||||
if (_player && self.timeObserver) {
|
||||
[_player removeTimeObserver:self.timeObserver];
|
||||
self.timeObserver = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeObserverAndAudioSesstion{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[[AVAudioSession sharedInstance] setActive:false withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
26
SweetParty/第三方库/KNPhotoBrowser/KNPhotoAVPlayer/KNPhotoPlayerProtocol.h
Executable file
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// KNPhotoPlayerProtocol.h
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 2021/5/12.
|
||||
// Copyright © 2021 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol KNPhotoPlayerViewDelegate <NSObject>
|
||||
|
||||
/**
|
||||
avplayer dimiss
|
||||
*/
|
||||
- (void)photoPlayerViewDismiss;
|
||||
/**
|
||||
avplayer long press
|
||||
*/
|
||||
- (void)photoPlayerLongPress:(UILongPressGestureRecognizer *)longPress;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
BIN
SweetParty/第三方库/KNPhotoBrowser/KNPhotoBrowser.bundle/circlePoint@2x.png
Executable file
|
After Width: | Height: | Size: 799 B |
BIN
SweetParty/第三方库/KNPhotoBrowser/KNPhotoBrowser.bundle/circlePoint@3x.png
Executable file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
SweetParty/第三方库/KNPhotoBrowser/KNPhotoBrowser.bundle/dismiss@2x.png
Executable file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
SweetParty/第三方库/KNPhotoBrowser/KNPhotoBrowser.bundle/dismiss@3x.png
Executable file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
SweetParty/第三方库/KNPhotoBrowser/KNPhotoBrowser.bundle/more_tap@2x.png
Executable file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
SweetParty/第三方库/KNPhotoBrowser/KNPhotoBrowser.bundle/more_tap@3x.png
Executable file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
SweetParty/第三方库/KNPhotoBrowser/KNPhotoBrowser.bundle/pause@2x.png
Executable file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
SweetParty/第三方库/KNPhotoBrowser/KNPhotoBrowser.bundle/pause@3x.png
Executable file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
SweetParty/第三方库/KNPhotoBrowser/KNPhotoBrowser.bundle/play@2x.png
Executable file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
SweetParty/第三方库/KNPhotoBrowser/KNPhotoBrowser.bundle/play@3x.png
Executable file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
SweetParty/第三方库/KNPhotoBrowser/KNPhotoBrowser.bundle/playCenter@2x.png
Executable file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
SweetParty/第三方库/KNPhotoBrowser/KNPhotoBrowser.bundle/playCenter@3x.png
Executable file
|
After Width: | Height: | Size: 11 KiB |
271
SweetParty/第三方库/KNPhotoBrowser/KNPhotoBrowser.h
Executable file
@@ -0,0 +1,271 @@
|
||||
//
|
||||
// KNPhotoBrowser.h
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 16/8/18.
|
||||
// Copyright © 2016年 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
/**
|
||||
* 如果 bug ,希望各位在 github 上通过'邮箱' 或者直接 issue 指出, 谢谢
|
||||
* github地址 : https://github.com/LuKane/KNPhotoBrowser
|
||||
* 项目会越来越丰富,也希望大家一起来增加功能 , 欢迎 Star
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import "KNActionSheet.h"
|
||||
|
||||
@class KNPhotoBrowser;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSInteger, KNPhotoDownloadState) {
|
||||
KNPhotoDownloadStateUnknow,
|
||||
KNPhotoDownloadStateDownloading,
|
||||
KNPhotoDownloadStateSuccess,
|
||||
KNPhotoDownloadStateFailure,
|
||||
KNPhotoDownloadStateRepeat,
|
||||
KNPhotoDownloadStateSaveFailure,
|
||||
KNPhotoDownloadStateSaveSuccess
|
||||
};
|
||||
|
||||
/****************************** == KNPhotoItems == ********************************/
|
||||
|
||||
@interface KNPhotoItems : NSObject
|
||||
|
||||
/// if it is network image or (net or locate video), set `url` , do not set `sourceImage`
|
||||
@property (nonatomic,copy ) NSString *url;
|
||||
|
||||
/// if it is locate image, set `sourceImage`, do not set `url`
|
||||
@property (nonatomic,strong) UIImage *sourceImage;
|
||||
|
||||
/// is locate gif image or not, default is false.
|
||||
/// if is locate gif image, set it true.
|
||||
/// if is network gif image or video, do not set it
|
||||
@property (nonatomic,assign) BOOL isLocateGif;
|
||||
|
||||
/// sourceView is current control to show image or video.
|
||||
/// 1. if the sourceView is kind of `UIImageView` or `UIButton` , just only only only set the `sourceView`.
|
||||
/// 2. if the sourceView is the custom view , set the `sourceView`, but do not forget set `sourceLinkArr` && `sourceLinkProperyName`.
|
||||
@property (nonatomic,strong) UIView *sourceView;
|
||||
|
||||
/**
|
||||
eg:
|
||||
CustomSourceView2 *superV = [[CustomSourceView2 alloc] init];
|
||||
[xxxView addsubView: superV];
|
||||
|
||||
CustomSourceImageView2 *superV1 = [[CustomSourceImageView2 alloc] init];
|
||||
[superV addsubView: superV1];
|
||||
|
||||
SDAnimatedImageView *imgV = [[SDAnimatedImageView alloc] init];
|
||||
[superV1 addsubView: imgV];
|
||||
|
||||
NSMutableArray *arr = [NSMutableArray array];
|
||||
[arr addObject:@"CustomSourceImageView2"];
|
||||
[arr addObject:@"SDAnimatedImageView"];
|
||||
|
||||
items.sourceLinkArr = [arr copy];
|
||||
*/
|
||||
|
||||
/// Class of sourceView' s subview (if set sourceLinkArr , then must set sourceLinkProperyName when it's not `UIImageView` or `UIButton`)
|
||||
@property (nonatomic,strong) NSArray<NSString *> *sourceLinkArr;
|
||||
|
||||
/**
|
||||
eg:
|
||||
if the lastObject is kind of UIImageView , the `sourceLinkProperyName` is `image`
|
||||
if the lastObject is kind of UIButton , the `sourceLinkProperyName` is `currentBackgroundImage` or `currentImage`
|
||||
*/
|
||||
|
||||
/// the property'name of the sourceLinkArr lastObject
|
||||
@property (nonatomic,copy ) NSString *sourceLinkProperyName;
|
||||
|
||||
/// is video or not, defalut is false
|
||||
@property (nonatomic,assign) BOOL isVideo;
|
||||
|
||||
/// when `isVideo` is true, and the video is net type, try to set videoPlaceHolderImageUrl, it is like the placeHolderImage of the net video
|
||||
@property (nonatomic,copy ) NSString *videoPlaceHolderImageUrl;
|
||||
|
||||
/// video is downloading or other state, default is unknow
|
||||
@property (nonatomic,assign) KNPhotoDownloadState downloadState;
|
||||
|
||||
/// video is downloading, current progress
|
||||
@property (nonatomic,assign) float downloadProgress;
|
||||
|
||||
@end
|
||||
|
||||
@interface UIDevice(PBExtension)
|
||||
/// device judge did have auth of Album
|
||||
/// @param authorBlock callBack
|
||||
+ (void)deviceAlbumAuth:(void (^)(BOOL isAuthor))authorBlock;
|
||||
|
||||
/// device shake
|
||||
+ (void)deviceShake;
|
||||
|
||||
@end
|
||||
|
||||
/****************************** == KNPhotoBrowserDelegate == ********************************/
|
||||
|
||||
@protocol KNPhotoBrowserDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
/// photoBrowser will dismiss with currentIndex
|
||||
/// @param photoBrowser browser
|
||||
/// @param index current index
|
||||
- (void)photoBrowser:(KNPhotoBrowser *)photoBrowser willDismissWithIndex:(NSInteger)index;
|
||||
|
||||
/// photoBrowser right top button did click with currentIndex (you can custom you right button, but if you custom your right button, that you need implementate your target action)
|
||||
/// @param photoBrowser browser
|
||||
/// @param index current index
|
||||
- (void)photoBrowser:(KNPhotoBrowser *)photoBrowser rightBtnOperationActionWithIndex:(NSInteger)index;
|
||||
|
||||
/// photoBrowser image long press (image or gif) with currentIndex
|
||||
/// @param photoBrowser photoBrowser
|
||||
/// @param index current index
|
||||
- (void)photoBrowser:(KNPhotoBrowser *)photoBrowser imageLongPressWithIndex:(NSInteger)index;
|
||||
|
||||
/// photoBrowser remove image or video source with relative index
|
||||
/// @param photoBrowser browser
|
||||
/// @param relativeIndex relative index
|
||||
- (void)photoBrowser:(KNPhotoBrowser *)photoBrowser removeSourceWithRelativeIndex:(NSInteger)relativeIndex;
|
||||
|
||||
/// photoBrowser remove image or video source with absolute index
|
||||
/// @param photoBrowser browser
|
||||
/// @param absoluteIndex absolute index
|
||||
- (void)photoBrowser:(KNPhotoBrowser *)photoBrowser removeSourceWithAbsoluteIndex:(NSInteger)absoluteIndex;
|
||||
|
||||
/// photoBrowser scroll to current Index
|
||||
/// @param photoBrowser browser
|
||||
/// @param index current index
|
||||
- (void)photoBrowser:(KNPhotoBrowser *)photoBrowser scrollToLocateWithIndex:(NSInteger)index;
|
||||
|
||||
/// photoBrowser did long press with gestureRecognizer and index
|
||||
/// @param photoBrowser browser
|
||||
/// @param longPress gesture Recognize
|
||||
/// @param index current index
|
||||
- (void)photoBrowser:(KNPhotoBrowser *)photoBrowser videoLongPress:(UILongPressGestureRecognizer *)longPress index:(NSInteger)index;
|
||||
|
||||
/// download image or video success or failure or failure reason call back. [If video player is download by auto, it can use delegate. it is only use function `removeImageOrVideoOnPhotoBrowser` can use this delegate]
|
||||
/// @param photoBrowser photoBrowser
|
||||
/// @param state state
|
||||
/// @param progress progress
|
||||
/// @param photoItemRe relative photoItem
|
||||
/// @param photoItemAb absolute photoItem
|
||||
- (void)photoBrowser:(KNPhotoBrowser *)photoBrowser
|
||||
state:(KNPhotoDownloadState)state
|
||||
progress:(float)progress
|
||||
photoItemRelative:(KNPhotoItems *)photoItemRe
|
||||
photoItemAbsolute:(KNPhotoItems *)photoItemAb;
|
||||
|
||||
/// photoBrowser will layout subviews
|
||||
- (void)photoBrowserWillLayoutSubviews;
|
||||
|
||||
@end
|
||||
|
||||
/****************************** == KNPhotoBrowser == ********************************/
|
||||
|
||||
@interface KNPhotoBrowser : UIViewController
|
||||
|
||||
/// current Index
|
||||
@property (nonatomic,assign) NSInteger currentIndex;
|
||||
|
||||
/// itemsArr contain KNPhotoItems : url | sourceView.....
|
||||
@property (nonatomic,strong) NSArray<KNPhotoItems *> *itemsArr;
|
||||
|
||||
/// delegate
|
||||
@property (nonatomic,weak ) id<KNPhotoBrowserDelegate> delegate;
|
||||
|
||||
/// image' control animated mode , default is `UIViewContentModeScaleToFill`
|
||||
@property (nonatomic,assign) UIViewContentMode animatedMode;
|
||||
|
||||
/// image' control presented mode , default is `UIViewContentModeScaleAspectFit`
|
||||
@property (nonatomic,assign) UIViewContentMode presentedMode;
|
||||
|
||||
/// when source image && image && video is not ready, create one image with color to holder, default is UIColor.clear
|
||||
@property (nonatomic,strong) UIColor *placeHolderColor;
|
||||
|
||||
/// is or not need pageNumView , default is false
|
||||
@property (nonatomic,assign) BOOL isNeedPageNumView;
|
||||
|
||||
/// is or not need pageControl , default is false (but if photobrowser contain video,then hidden)
|
||||
@property (nonatomic,assign) BOOL isNeedPageControl;
|
||||
|
||||
/// is or not need RightTopBtn , default is false
|
||||
@property (nonatomic,assign) BOOL isNeedRightTopBtn;
|
||||
|
||||
/// is or not need image or video longPress , default is false.
|
||||
/// image long press : delegate function `photoBrowser: imageLongPressWithIndex:`.
|
||||
/// video long press : delegate function `photoBrowser: videoLongPress: index:`.
|
||||
@property (nonatomic,assign) BOOL isNeedLongPress;
|
||||
|
||||
/// is or not need prefetch image, maxCount is 8 (KNPhotoBrowserPch.h)
|
||||
@property (nonatomic,assign) BOOL isNeedPrefetch;
|
||||
|
||||
/// is or not need pan Gesture, default is false
|
||||
@property (nonatomic,assign) BOOL isNeedPanGesture;
|
||||
|
||||
/// is or not need auto play video, default is false
|
||||
@property (nonatomic,assign) BOOL isNeedAutoPlay;
|
||||
|
||||
/// is or not need online play video, default is false [That means auto download video first]
|
||||
@property (nonatomic,assign) BOOL isNeedOnlinePlay;
|
||||
|
||||
/// is or not solo ambient, default is true `AVAudioSessionCategorySoloAmbient`. If set false ,that will be `AVAudioSessionCategoryAmbient`
|
||||
@property (nonatomic,assign) BOOL isSoloAmbient;
|
||||
|
||||
/// the `numView` & `pageControl` & `operationBtn` is or not need follow photoBrowser , default is false.
|
||||
/// when touch photoBrowser, they will be hidden.
|
||||
/// when you cancel, they will be showed.
|
||||
/// when dismiss the photoBrowser immediately, they will be hidden immediately.
|
||||
@property (nonatomic,assign) BOOL isNeedFollowAnimated;
|
||||
|
||||
/// remove current image or video on photobrowser
|
||||
- (void)removeImageOrVideoOnPhotoBrowser;
|
||||
|
||||
/// download photo or video to Album, but it must be authed at first
|
||||
- (void)downloadImageOrVideoToAlbum;
|
||||
|
||||
/// player's rate immediately to use, default is 1.0 , range is [0.5 <= rate <= 2.0]
|
||||
- (void)setImmediatelyPlayerRate:(CGFloat)rate;
|
||||
|
||||
/**
|
||||
you can use the next function, use the `- (void)createOverlayViewArrOnTopView: animated: followAnimated:`
|
||||
*/
|
||||
- (void)createCustomViewArrOnTopView:(NSArray<UIView *> *)customViewArr
|
||||
animated:(BOOL)animated
|
||||
followAnimated:(BOOL)followAnimated;
|
||||
|
||||
/**
|
||||
create overlay view on the topView(photoBrowser controller's view)
|
||||
for example: create a scrollView on the photoBrowser controller's view, when photoBrowser has scrolled , you can use delegate's function to do something you want
|
||||
delegate's function: 'photoBrowser:scrollToLocateWithIndex:(NSInteger)index'
|
||||
'CustomViewController' in Demo, you can see it how to use
|
||||
@param overlayViewArr overlayViewArr
|
||||
@param animated need animated or not, with photoBrowser present
|
||||
@param followAnimated need animated or not for follow photoBrowser
|
||||
*/
|
||||
- (void)createOverlayViewArrOnTopView:(NSArray<UIView *> *)overlayViewArr
|
||||
animated:(BOOL)animated
|
||||
followAnimated:(BOOL)followAnimated;
|
||||
|
||||
/// photoBrowser will present.
|
||||
/// if `which is already presenting`, use the `- (void)present:(UIViewController *)controller` to instead
|
||||
- (void)present;
|
||||
|
||||
/*
|
||||
by the way , you alse can present as you wish, like:
|
||||
[controller presentViewController:photoBrowser animated:false completion:^{
|
||||
|
||||
}];
|
||||
*/
|
||||
|
||||
/// photoBrowser will present base on current controller
|
||||
/// @param controller current controller
|
||||
- (void)presentOn:(UIViewController *)controller;
|
||||
|
||||
/// photoBrowser dismiss
|
||||
- (void)dismiss;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
1481
SweetParty/第三方库/KNPhotoBrowser/KNPhotoBrowser.m
Executable file
40
SweetParty/第三方库/KNPhotoBrowser/KNPhotoBrowserImageView.h
Executable file
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// KNPhotoBrowserImageView.h
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 16/8/17.
|
||||
// Copyright © 2016年 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <SDWebImage/SDAnimatedImageView.h>
|
||||
|
||||
@class KNProgressHUD;
|
||||
@class KNPhotoItems;
|
||||
|
||||
typedef void(^PhotoBrowerSingleTap)(void);
|
||||
typedef void(^PhotoBrowerLongPressTap)(void);
|
||||
|
||||
@interface KNPhotoBrowserImageView : UIView
|
||||
|
||||
// all base control that can scroll
|
||||
@property (nonatomic, strong) UIScrollView *scrollView;
|
||||
|
||||
@property (nonatomic, strong) SDAnimatedImageView *imageView;
|
||||
|
||||
// single tap
|
||||
@property (nonatomic,copy ) PhotoBrowerSingleTap singleTap;
|
||||
// longPress tap
|
||||
@property (nonatomic,copy ) PhotoBrowerLongPressTap longPressTap;
|
||||
|
||||
/// set image with url
|
||||
/// @param url url
|
||||
/// @param progressHUD progressHUD
|
||||
/// @param placeHolder placeHolder image
|
||||
/// @param photoItem current photoItems
|
||||
- (void)imageWithUrl:(NSURL *)url
|
||||
progressHUD:(KNProgressHUD *)progressHUD
|
||||
placeHolder:(UIImage *)placeHolder
|
||||
photoItem:(KNPhotoItems *)photoItem;
|
||||
|
||||
@end
|
||||
263
SweetParty/第三方库/KNPhotoBrowser/KNPhotoBrowserImageView.m
Executable file
@@ -0,0 +1,263 @@
|
||||
//
|
||||
// KNPhotoBrowserImageView.m
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 16/8/17.
|
||||
// Copyright © 2016年 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import "KNPhotoBrowserImageView.h"
|
||||
#import <SDWebImage/UIImageView+WebCache.h>
|
||||
#import <SDWebImage/SDImageCache.h>
|
||||
#import "KNProgressHUD.h"
|
||||
#import "KNPhotoBrowserPch.h"
|
||||
#import "KNPhotoBrowser.h"
|
||||
#import "KNReachability.h"
|
||||
|
||||
@interface KNPhotoBrowserImageView()<UIScrollViewDelegate>{
|
||||
NSURL *_url;
|
||||
UIImage *_placeHolder;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation KNPhotoBrowserImageView
|
||||
|
||||
- (SDAnimatedImageView *)imageView{
|
||||
if (!_imageView) {
|
||||
_imageView = [[SDAnimatedImageView alloc] init];
|
||||
[_imageView setUserInteractionEnabled:true];
|
||||
[_imageView setFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)];
|
||||
_imageView.layer.cornerRadius = 0.1;
|
||||
_imageView.clipsToBounds = true;
|
||||
}
|
||||
return _imageView;
|
||||
}
|
||||
|
||||
- (UIScrollView *)scrollView{
|
||||
if (!_scrollView) {
|
||||
_scrollView = [[UIScrollView alloc] init];
|
||||
[_scrollView setFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)];
|
||||
[_scrollView addSubview:self.imageView];
|
||||
[_scrollView setDelegate:self];
|
||||
[_scrollView setClipsToBounds:true];
|
||||
}
|
||||
return _scrollView;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
[self addSubview:self.scrollView];
|
||||
[self initDefaultData];
|
||||
if (@available(iOS 11.0, *)){
|
||||
_scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initDefaultData{
|
||||
// 1.tap && doubleTap && longpress
|
||||
UITapGestureRecognizer *tap =
|
||||
[[UITapGestureRecognizer alloc] initWithTarget:self
|
||||
action:@selector(scrollViewDidTap)];
|
||||
|
||||
UITapGestureRecognizer *doubleTap =
|
||||
[[UITapGestureRecognizer alloc] initWithTarget:self
|
||||
action:@selector(scrollViewDidDoubleTap:)];
|
||||
|
||||
UILongPressGestureRecognizer *longPress =
|
||||
[[UILongPressGestureRecognizer alloc] initWithTarget:self
|
||||
action:@selector(longPressDidPress:)];
|
||||
|
||||
// 2.set gesture require
|
||||
[tap setNumberOfTapsRequired:1];
|
||||
[tap setNumberOfTouchesRequired:1];
|
||||
[doubleTap setNumberOfTapsRequired:2];
|
||||
[doubleTap setNumberOfTouchesRequired:1];
|
||||
|
||||
// 3.conflict resolution
|
||||
[tap requireGestureRecognizerToFail:doubleTap];
|
||||
|
||||
// 4.add gesture
|
||||
[self addGestureRecognizer:tap];
|
||||
[self addGestureRecognizer:doubleTap];
|
||||
[self addGestureRecognizer:longPress];
|
||||
}
|
||||
|
||||
#pragma mark - 单击
|
||||
- (void)scrollViewDidTap{
|
||||
if(_singleTap){
|
||||
_singleTap();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 长按
|
||||
- (void)longPressDidPress:(UILongPressGestureRecognizer *)longPress{
|
||||
if(longPress.state == UIGestureRecognizerStateBegan){
|
||||
if(_longPressTap){
|
||||
_longPressTap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 双击
|
||||
- (void)scrollViewDidDoubleTap:(UITapGestureRecognizer *)doubleTap{
|
||||
// if image is download, if not ,just return;
|
||||
if(!_imageView.image) return;
|
||||
|
||||
if(_scrollView.zoomScale <= 1){
|
||||
// 1.catch the postion of the gesture
|
||||
// 2.contentOffset.x of scrollView + location x of gesture
|
||||
CGFloat x = [doubleTap locationInView:self].x + _scrollView.contentOffset.x;
|
||||
|
||||
// 3.contentOffset.y + location y of gesture
|
||||
CGFloat y = [doubleTap locationInView:self].y + _scrollView.contentOffset.y;
|
||||
[_scrollView zoomToRect:(CGRect){{x,y},CGSizeZero} animated:true];
|
||||
}else{
|
||||
// set scrollView zoom to original
|
||||
[_scrollView setZoomScale:1.f animated:true];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)imageWithUrl:(NSURL *)url
|
||||
progressHUD:(KNProgressHUD *)progressHUD
|
||||
placeHolder:(UIImage *)placeHolder
|
||||
photoItem:(KNPhotoItems *)photoItem{
|
||||
|
||||
[progressHUD setHidden:true];
|
||||
|
||||
_url = url;
|
||||
_placeHolder = placeHolder;
|
||||
|
||||
if(!url){
|
||||
if (photoItem.isLocateGif == true) {
|
||||
[_imageView setImage:photoItem.sourceImage];
|
||||
}else {
|
||||
[_imageView setImage:placeHolder];
|
||||
}
|
||||
[self layoutSubviews];
|
||||
return;
|
||||
}
|
||||
|
||||
if (![[KNReachability reachabilityForInternetConnection] isReachable]) { // no network
|
||||
[progressHUD setHidden:true];
|
||||
}else {
|
||||
[progressHUD setHidden:false];
|
||||
}
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
UIImage *image = [[SDImageCache sharedImageCache] imageFromCacheForKey:[url absoluteString]];
|
||||
if(image){
|
||||
[progressHUD setHidden:true];
|
||||
}
|
||||
|
||||
// SDWebImage download image
|
||||
[_imageView sd_setImageWithURL:url placeholderImage:placeHolder options:SDWebImageRetryFailed progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
|
||||
CGFloat progress = ((CGFloat)receivedSize / expectedSize);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if(progressHUD){
|
||||
progressHUD.progress = progress;
|
||||
}
|
||||
});
|
||||
} completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
|
||||
[self->_scrollView setZoomScale:1.f animated:true];
|
||||
if(!error){
|
||||
[progressHUD setProgress:1.f];
|
||||
[weakSelf layoutSubviews];
|
||||
}else{
|
||||
[progressHUD setHidden:true];
|
||||
}
|
||||
}];
|
||||
|
||||
[self layoutSubviews];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews{
|
||||
[super layoutSubviews];
|
||||
_scrollView.frame = CGRectMake(10, 0, self.bounds.size.width - 20, self.bounds.size.height);
|
||||
[self reloadFrames];
|
||||
}
|
||||
|
||||
- (void)reloadFrames{
|
||||
CGRect frame = _scrollView.frame;
|
||||
if(_imageView.image){
|
||||
|
||||
CGSize imageSize = _imageView.image.size;
|
||||
CGRect imageFrame = CGRectMake(0, 0, imageSize.width, imageSize.height);
|
||||
|
||||
// if scrollView.width <= height : that means Screen is not landscap
|
||||
if (frame.size.width <= frame.size.height) {
|
||||
// let width of the image set as width of scrollView, height become radio
|
||||
CGFloat ratio = frame.size.width / imageFrame.size.width;
|
||||
imageFrame.size.height = imageFrame.size.height * ratio;
|
||||
imageFrame.size.width = frame.size.width;
|
||||
}else{
|
||||
if (frame.size.width / frame.size.height <= imageSize.width / imageSize.height) {
|
||||
imageFrame.size.width = frame.size.width;
|
||||
imageFrame.size.height = (frame.size.width / imageSize.width) * imageSize.height;
|
||||
}else {
|
||||
// let width of the image set as width of scrollView, height become radio
|
||||
CGFloat ratio = frame.size.height / imageFrame.size.height;
|
||||
imageFrame.size.width = imageFrame.size.width * ratio;
|
||||
imageFrame.size.height = frame.size.height;
|
||||
}
|
||||
}
|
||||
|
||||
[_imageView setFrame:(CGRect){CGPointZero,imageFrame.size}];
|
||||
|
||||
// set scrollView contentsize
|
||||
_scrollView.contentSize = _imageView.frame.size;
|
||||
|
||||
// set scrollView.contentsize as image.size , and get center of the image
|
||||
_imageView.center = [self centerOfScrollViewContent:_scrollView];
|
||||
// get the radio of scrollView.height and image.height
|
||||
CGFloat maxScale = frame.size.height / imageFrame.size.height;
|
||||
// get radio of the width
|
||||
CGFloat widthRadit = frame.size.width / imageFrame.size.width;
|
||||
|
||||
// get the max radio
|
||||
maxScale = widthRadit > maxScale?widthRadit:maxScale;
|
||||
// if the max radio >= PhotoBrowerImageMaxScale, get max radio , else PhotoBrowerImageMaxScale
|
||||
maxScale = maxScale > 2 ? maxScale:2;
|
||||
|
||||
// set max and min radio of scrollView
|
||||
_scrollView.minimumZoomScale = 1;
|
||||
_scrollView.maximumZoomScale = maxScale;
|
||||
|
||||
// set scrollView zoom original
|
||||
_scrollView.zoomScale = 1.0f;
|
||||
|
||||
}else{
|
||||
frame.origin = CGPointZero;
|
||||
_imageView.frame = frame;
|
||||
_scrollView.contentSize = _imageView.bounds.size;
|
||||
}
|
||||
_scrollView.contentOffset = CGPointZero;
|
||||
}
|
||||
|
||||
- (CGPoint)centerOfScrollViewContent:(UIScrollView *)scrollView{
|
||||
// scrollView.bounds.size.width > scrollView.contentSize.width :that means scrollView.size > image.size
|
||||
CGFloat offsetX = (scrollView.bounds.size.width > scrollView.contentSize.width)?
|
||||
(scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5 : 0.0;
|
||||
|
||||
CGFloat offsetY = (scrollView.bounds.size.height > scrollView.contentSize.height)?
|
||||
(scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5 : 0.0;
|
||||
|
||||
CGPoint actualCenter = CGPointMake(scrollView.contentSize.width * 0.5 + offsetX,
|
||||
scrollView.contentSize.height * 0.5 + offsetY);
|
||||
return actualCenter;
|
||||
}
|
||||
|
||||
#pragma mark UIScrollViewDelegate
|
||||
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{
|
||||
// zoom the subviews of the scrollView
|
||||
return self.imageView;
|
||||
}
|
||||
|
||||
- (void)scrollViewDidZoom:(UIScrollView *)scrollView{
|
||||
// reset the center of image when dragging everytime
|
||||
_imageView.center = [self centerOfScrollViewContent:scrollView];
|
||||
}
|
||||
|
||||
@end
|
||||
18
SweetParty/第三方库/KNPhotoBrowser/KNPhotoBrowserNumView.h
Executable file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// KNPhotoBrowserNumView.h
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 16/9/2.
|
||||
// Copyright © 2016年 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface KNPhotoBrowserNumView : UILabel
|
||||
|
||||
- (void)setCurrentNum:(NSInteger)currentNum totalNum:(NSInteger)totalNum;
|
||||
|
||||
@property (nonatomic, assign) NSInteger currentNum;
|
||||
@property (nonatomic, assign) NSInteger totalNum;
|
||||
|
||||
@end
|
||||
42
SweetParty/第三方库/KNPhotoBrowser/KNPhotoBrowserNumView.m
Executable file
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// KNPhotoBrowserNumView.m
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 16/9/2.
|
||||
// Copyright © 2016年 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import "KNPhotoBrowserNumView.h"
|
||||
|
||||
@implementation KNPhotoBrowserNumView
|
||||
|
||||
- (instancetype)init{
|
||||
if (self = [super init]) {
|
||||
[self setFont:[UIFont boldSystemFontOfSize:20]];
|
||||
[self setTextColor:[UIColor whiteColor]];
|
||||
[self setTextAlignment:NSTextAlignmentCenter];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setCurrentNum:(NSInteger)currentNum totalNum:(NSInteger)totalNum{
|
||||
_currentNum = currentNum;
|
||||
_totalNum = totalNum;
|
||||
[self changeText];
|
||||
}
|
||||
|
||||
- (void)changeText{
|
||||
self.text = [NSString stringWithFormat:@"%zd / %zd",_currentNum,_totalNum];
|
||||
}
|
||||
|
||||
- (void)setCurrentNum:(NSInteger)currentNum{
|
||||
_currentNum = currentNum;
|
||||
[self changeText];
|
||||
}
|
||||
|
||||
- (void)setTotalNum:(NSInteger)totalNum{
|
||||
_totalNum = totalNum;
|
||||
[self changeText];
|
||||
}
|
||||
|
||||
@end
|
||||
51
SweetParty/第三方库/KNPhotoBrowser/KNPhotoBrowserPch.h
Executable file
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// KNPhotoBrowser.h
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 16/8/18.
|
||||
// Copyright © 2016年 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
/**
|
||||
* 如果 bug ,希望各位在 github 上通过'邮箱' 或者直接 issue 指出, 谢谢
|
||||
* github地址 : https://github.com/LuKane/KNPhotoBrowser
|
||||
* 项目会越来越丰富,也希望大家一起来增加功能 , 欢迎 Star
|
||||
*/
|
||||
|
||||
#ifndef ScreenWidth
|
||||
#define ScreenWidth [UIScreen mainScreen].bounds.size.width
|
||||
#endif
|
||||
|
||||
#ifndef ScreenHeight
|
||||
#define ScreenHeight [UIScreen mainScreen].bounds.size.height
|
||||
#endif
|
||||
|
||||
#ifndef PBViewWidth
|
||||
#define PBViewWidth self.view.bounds.size.width
|
||||
#endif
|
||||
|
||||
#ifndef PBViewHeight
|
||||
#define PBViewHeight self.view.bounds.size.height
|
||||
#endif
|
||||
|
||||
#define PBDeviceHasBang \
|
||||
({\
|
||||
BOOL hasBang = false;\
|
||||
if (@available(iOS 11.0, *)) {\
|
||||
hasBang = [UIApplication sharedApplication].keyWindow.safeAreaInsets.bottom;\
|
||||
}\
|
||||
(hasBang);\
|
||||
})
|
||||
|
||||
/// Portrait
|
||||
#ifndef isPortrait
|
||||
#define isPortrait ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortrait || [[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortraitUpsideDown)
|
||||
#endif
|
||||
|
||||
|
||||
#define PhotoBrowserAnimateTime 0.3
|
||||
|
||||
// define SDWebImagePrefetcher max number
|
||||
#define PhotoBrowserPrefetchNum 8
|
||||
|
||||
|
||||
68
SweetParty/第三方库/KNPhotoBrowser/KNPhotoDownloadMgr.h
Executable file
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// KNPhotoDownloadMgr.h
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 2019/7/29.
|
||||
// Copyright © 2019 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "KNPhotoBrowser.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void(^PhotoDownLoadBlock)(KNPhotoDownloadState downloadState, float progress);
|
||||
|
||||
@interface KNPhotoDownloadMgr: NSObject <NSURLSessionDelegate>
|
||||
|
||||
/// single
|
||||
+ (instancetype)shareInstance;
|
||||
|
||||
/// default file path
|
||||
/// [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, true) lastObject] stringByAppendingPathComponent:@"KNPhotoBrowserData"];
|
||||
@property (nonatomic,copy, readonly) NSString *filePath;
|
||||
|
||||
/**
|
||||
download video, when finish it, it will be renamed!
|
||||
eg: url = "https://www.xxxxxx/xxxx/123.mp4"
|
||||
rename = "123" to MD5 encryption, and append ".mp4"
|
||||
|
||||
@param photoItems current item
|
||||
@param downloadBlock block
|
||||
*/
|
||||
- (void)downloadVideoWithPhotoItems:(KNPhotoItems *)photoItems
|
||||
downloadBlock:(PhotoDownLoadBlock)downloadBlock;
|
||||
|
||||
/// cancel all download task
|
||||
- (void)cancelTask;
|
||||
|
||||
@end
|
||||
|
||||
@interface KNPhotoDownloadFileMgr: NSObject
|
||||
|
||||
/// default file path
|
||||
/// [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, true) lastObject] stringByAppendingPathComponent:@"KNPhotoBrowserData"];
|
||||
@property (nonatomic,copy, readonly) NSString *filePath;
|
||||
|
||||
/// check out filePath has contain current video or not
|
||||
/// @param photoItems photoItems
|
||||
- (BOOL)startCheckIsExistVideo:(KNPhotoItems *)photoItems;
|
||||
|
||||
/// get filePath of current video(filePath is like : "123" to MD5 encryption, and append ".mp4" )
|
||||
/// @param photoItems photoItems
|
||||
- (NSString *)startGetFilePath:(KNPhotoItems *)photoItems;
|
||||
|
||||
/// remove video by photoItems
|
||||
/// @param photoItems photoItems
|
||||
- (void)removeVideoByPhotoItems:(KNPhotoItems *)photoItems;
|
||||
|
||||
/// remove video by url string
|
||||
/// @param urlString url string
|
||||
- (void)removeVideoByURLString:(NSString *)urlString;
|
||||
|
||||
/// remove all video
|
||||
- (void)removeAllVideo;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
236
SweetParty/第三方库/KNPhotoBrowser/KNPhotoDownloadMgr.m
Executable file
@@ -0,0 +1,236 @@
|
||||
//
|
||||
// KNPhotoDownloadMgr.m
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 2019/7/29.
|
||||
// Copyright © 2019 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import "KNPhotoDownloadMgr.h"
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
|
||||
@interface KNPhotoDownloadMgr(){
|
||||
NSURLSessionDownloadTask *_downloadTask;
|
||||
}
|
||||
|
||||
@property (nonatomic,copy ) PhotoDownLoadBlock downloadBlock;
|
||||
@property (nonatomic,strong) KNPhotoItems *item;
|
||||
@property (nonatomic,strong) KNPhotoItems *tempItem;
|
||||
|
||||
@end
|
||||
|
||||
@implementation KNPhotoDownloadMgr
|
||||
|
||||
static KNPhotoDownloadMgr *_mgr = nil;
|
||||
|
||||
+ (instancetype)shareInstance{
|
||||
if (_mgr == nil) {
|
||||
_mgr = [[KNPhotoDownloadMgr alloc] init];
|
||||
}
|
||||
return _mgr;
|
||||
}
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
_mgr = [super allocWithZone:zone];
|
||||
});
|
||||
return _mgr;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone{
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)mutableCopyWithZone:(NSZone *)zone{
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init{
|
||||
if (self = [super init]) {
|
||||
_filePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, true) lastObject] stringByAppendingPathComponent:@"KNPhotoBrowserData"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)downloadVideoWithPhotoItems:(KNPhotoItems *)photoItems downloadBlock:(PhotoDownLoadBlock)downloadBlock{
|
||||
if (photoItems.url == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_tempItem == photoItems) {
|
||||
_downloadBlock(KNPhotoDownloadStateRepeat,0.0);
|
||||
return;
|
||||
}
|
||||
|
||||
_item = [[KNPhotoItems alloc] init];
|
||||
_item.url = photoItems.url;
|
||||
|
||||
_tempItem = photoItems;
|
||||
_downloadBlock = downloadBlock;
|
||||
|
||||
[self cancelTask];
|
||||
|
||||
if (photoItems.isVideo == true) {
|
||||
NSURL *url = [NSURL URLWithString:photoItems.url];
|
||||
if ([url.scheme containsString:@"http"]) {
|
||||
[self startDownLoadWithURL:url.absoluteString];
|
||||
}
|
||||
}else {
|
||||
_downloadBlock(KNPhotoDownloadStateUnknow,0.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// cancel all download task
|
||||
- (void)cancelTask{
|
||||
_item.downloadState = KNPhotoDownloadStateFailure;
|
||||
[_downloadTask cancel];
|
||||
}
|
||||
|
||||
- (void)startDownLoadWithURL:(NSString *)url{
|
||||
if (_item.downloadState == KNPhotoDownloadStateDownloading) return;
|
||||
|
||||
_item.downloadState = KNPhotoDownloadStateDownloading;
|
||||
_item.downloadProgress = 0.0;
|
||||
|
||||
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
|
||||
_downloadTask = [session downloadTaskWithURL:[NSURL URLWithString:url]];
|
||||
[_downloadTask resume];
|
||||
}
|
||||
#pragma mark - NSURLSession Delegate --> NSURLSessionTaskDelegate
|
||||
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
|
||||
CGFloat progress = (CGFloat)totalBytesWritten / (CGFloat)totalBytesExpectedToWrite;
|
||||
|
||||
if (progress < 0) progress = 0;
|
||||
if (progress > 1) progress = 1;
|
||||
|
||||
_item.downloadProgress = progress;
|
||||
_item.downloadState = KNPhotoDownloadStateDownloading;
|
||||
if (_downloadBlock) {
|
||||
// NSLog(@"%lld-%lld == > %f",totalBytesWritten,totalBytesExpectedToWrite,progress);
|
||||
_downloadBlock(KNPhotoDownloadStateDownloading,progress);
|
||||
}
|
||||
}
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error {
|
||||
_item.downloadState = KNPhotoDownloadStateSuccess;
|
||||
_item.downloadProgress = 1.0;
|
||||
if (error) {
|
||||
_item.downloadState = KNPhotoDownloadStateFailure;
|
||||
_item.downloadProgress = 0.0;
|
||||
}
|
||||
if (_downloadBlock) {
|
||||
_downloadBlock(_item.downloadState,_item.downloadProgress);
|
||||
}
|
||||
_tempItem = nil;
|
||||
}
|
||||
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
|
||||
|
||||
NSString *file = [_filePath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@",[self md5:_item.url.lastPathComponent.stringByDeletingPathExtension],_item.url.pathExtension]];
|
||||
|
||||
[[NSFileManager defaultManager] copyItemAtURL:location toURL:[NSURL fileURLWithPath:file] error:nil];
|
||||
}
|
||||
|
||||
- (NSString *)md5:(NSString *)str{
|
||||
const char *cStr = [str UTF8String];
|
||||
unsigned char result[16];
|
||||
CC_MD5(cStr, (CC_LONG)strlen(cStr), result); // This is the md5 call
|
||||
return [NSString stringWithFormat:
|
||||
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
result[0], result[1], result[2], result[3],
|
||||
result[4], result[5], result[6], result[7],
|
||||
result[8], result[9], result[10], result[11],
|
||||
result[12], result[13], result[14], result[15]
|
||||
];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation KNPhotoDownloadFileMgr
|
||||
|
||||
- (instancetype)init{
|
||||
if (self = [super init]) {
|
||||
_filePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, true) lastObject] stringByAppendingPathComponent:@"KNPhotoBrowserData"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/// check is contain video or not
|
||||
- (BOOL)startCheckIsExistVideo:(KNPhotoItems *)photoItems {
|
||||
if (photoItems == nil || photoItems.url == nil) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NSFileManager *fileMgr = [NSFileManager defaultManager];
|
||||
|
||||
BOOL isDir = false;
|
||||
BOOL existed = [fileMgr fileExistsAtPath:_filePath isDirectory:&isDir];
|
||||
|
||||
if (!(isDir && existed)) {
|
||||
[fileMgr createDirectoryAtPath:_filePath withIntermediateDirectories:true attributes:nil error:nil];
|
||||
return false;
|
||||
}else {
|
||||
NSString *path = [_filePath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@",[self md5:photoItems.url.lastPathComponent.stringByDeletingPathExtension],photoItems.url.pathExtension]];
|
||||
return [fileMgr fileExistsAtPath:path];
|
||||
}
|
||||
}
|
||||
|
||||
/// get video filepath , but it must download before
|
||||
- (NSString *)startGetFilePath:(KNPhotoItems *)photoItems {
|
||||
NSString *path = [_filePath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@",[self md5:photoItems.url.lastPathComponent.stringByDeletingPathExtension],photoItems.url.pathExtension]];
|
||||
return path;
|
||||
}
|
||||
|
||||
- (NSString *)md5:(NSString *)str{
|
||||
const char *cStr = [str UTF8String];
|
||||
unsigned char result[16];
|
||||
CC_MD5(cStr, (CC_LONG)strlen(cStr), result); // This is the md5 call
|
||||
return [NSString stringWithFormat:
|
||||
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
result[0], result[1], result[2], result[3],
|
||||
result[4], result[5], result[6], result[7],
|
||||
result[8], result[9], result[10], result[11],
|
||||
result[12], result[13], result[14], result[15]
|
||||
];
|
||||
}
|
||||
|
||||
/// remove video by photoItems
|
||||
/// @param photoItems photoItems
|
||||
- (void)removeVideoByPhotoItems:(KNPhotoItems *)photoItems{
|
||||
if (photoItems == nil) {
|
||||
return;
|
||||
}
|
||||
[self removeVideoByURLString:photoItems.url];
|
||||
}
|
||||
|
||||
/// remove video by url string
|
||||
/// @param urlString url string
|
||||
- (void)removeVideoByURLString:(NSString *)urlString{
|
||||
if (urlString == nil) {
|
||||
return;
|
||||
}
|
||||
if ([urlString stringByReplacingOccurrencesOfString:@" " withString:@""].length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSFileManager *fileMgr = [NSFileManager defaultManager];
|
||||
|
||||
BOOL isDir = false;
|
||||
BOOL existed = [fileMgr fileExistsAtPath:_filePath isDirectory:&isDir];
|
||||
|
||||
if ((isDir && existed)) {
|
||||
NSError *err;
|
||||
NSString *path = [_filePath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@",[self md5:urlString.lastPathComponent.stringByDeletingPathExtension],urlString.pathExtension]];
|
||||
[fileMgr removeItemAtPath:path error:&err];
|
||||
}
|
||||
}
|
||||
|
||||
/// remove all video
|
||||
- (void)removeAllVideo{
|
||||
NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtPath:_filePath];
|
||||
for (NSString *fileName in enumerator) {
|
||||
[[NSFileManager defaultManager] removeItemAtPath:[_filePath stringByAppendingPathComponent:fileName] error:nil];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
36
SweetParty/第三方库/KNPhotoBrowser/KNPhotoImageCell.h
Executable file
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// KNPhotoImageCell.h
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 2018/12/14.
|
||||
// Copyright © 2018 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "KNPhotoBrowserImageView.h"
|
||||
#import "KNProgressHUD.h"
|
||||
|
||||
@class KNPhotoItems;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void(^PhotoBrowerSingleTap)(void);
|
||||
typedef void(^PhotoBrowerLongPressTap)(void);
|
||||
|
||||
@interface KNPhotoImageCell : UICollectionViewCell
|
||||
|
||||
/// set image with url
|
||||
/// @param url url
|
||||
/// @param placeHolder placeHolder image
|
||||
/// @param photoItem curent photoItem
|
||||
- (void)imageWithUrl:(NSString *)url placeHolder:(UIImage *)placeHolder photoItem:(KNPhotoItems *)photoItem;
|
||||
|
||||
@property (nonatomic,strong) KNPhotoBrowserImageView *photoBrowerImageView;
|
||||
@property (nonatomic,copy ) PhotoBrowerSingleTap singleTap;
|
||||
@property (nonatomic,copy ) PhotoBrowerLongPressTap longPressTap;
|
||||
|
||||
@property (nonatomic,assign) UIViewContentMode presentedMode;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
77
SweetParty/第三方库/KNPhotoBrowser/KNPhotoImageCell.m
Executable file
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// KNPhotoImageCell.m
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 2018/12/14.
|
||||
// Copyright © 2018 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import "KNPhotoImageCell.h"
|
||||
#import "KNProgressHUD.h"
|
||||
|
||||
@implementation KNPhotoImageCell{
|
||||
KNProgressHUD *_progressHUD;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
[self setupImageView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setupImageView{
|
||||
// 1.photoBrowerView
|
||||
KNPhotoBrowserImageView *photoBrowerView = [[KNPhotoBrowserImageView alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
_photoBrowerImageView = photoBrowerView;
|
||||
[self.contentView addSubview:photoBrowerView];
|
||||
|
||||
// single tap
|
||||
__weak typeof(self) weakself = self;
|
||||
photoBrowerView.singleTap = ^{
|
||||
if (weakself.singleTap) {
|
||||
weakself.singleTap();
|
||||
}
|
||||
};
|
||||
|
||||
// long press
|
||||
photoBrowerView.longPressTap = ^{
|
||||
if (weakself.longPressTap) {
|
||||
weakself.longPressTap();
|
||||
}
|
||||
};
|
||||
|
||||
// 2.progressHUD
|
||||
KNProgressHUD *progressHUD = [[KNProgressHUD alloc] initWithFrame:(CGRect){{([UIScreen mainScreen].bounds.size.width - 40) * 0.5,([UIScreen mainScreen].bounds.size.height - 40) * 0.5},{40,40}}];
|
||||
_progressHUD = progressHUD;
|
||||
[self.contentView addSubview:progressHUD];
|
||||
}
|
||||
|
||||
- (void)imageWithUrl:(NSString *)url
|
||||
placeHolder:(UIImage *)placeHolder
|
||||
photoItem:(nonnull KNPhotoItems *)photoItem{
|
||||
[_photoBrowerImageView imageWithUrl:[NSURL URLWithString:url]
|
||||
progressHUD:_progressHUD
|
||||
placeHolder:placeHolder
|
||||
photoItem:photoItem];
|
||||
}
|
||||
|
||||
- (void)setPresentedMode:(UIViewContentMode)presentedMode {
|
||||
_presentedMode = presentedMode;
|
||||
_photoBrowerImageView.imageView.contentMode = self.presentedMode;
|
||||
}
|
||||
|
||||
- (void)prepareForReuse{
|
||||
[super prepareForReuse];
|
||||
[_photoBrowerImageView.scrollView setZoomScale:1.f animated:false];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews{
|
||||
[super layoutSubviews];
|
||||
|
||||
[_photoBrowerImageView.scrollView setZoomScale:1.f animated:false];
|
||||
_photoBrowerImageView.frame = self.bounds;
|
||||
_progressHUD.center = self.contentView.center;
|
||||
}
|
||||
|
||||
@end
|
||||
54
SweetParty/第三方库/KNPhotoBrowser/KNPhotoVideoCell.h
Executable file
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// KNPhotoVideoCell.h
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 2019/6/11.
|
||||
// Copyright © 2019 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "KNPhotoAVPlayerView.h"
|
||||
#import "KNPhotoLocateAVPlayerView.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol KNPhotoVideoCellDelegate <NSObject>
|
||||
|
||||
/// avplayer will dismmiss
|
||||
- (void)photoVideoAVPlayerDismiss;
|
||||
|
||||
/// avplayer long press
|
||||
/// @param longPress press
|
||||
- (void)photoVideoAVPlayerLongPress:(UILongPressGestureRecognizer *)longPress;
|
||||
|
||||
@end
|
||||
|
||||
@interface KNPhotoVideoCell : UICollectionViewCell
|
||||
|
||||
/// play video on line with photoItems and placeHolder's image
|
||||
/// @param photoItems photoItems
|
||||
/// @param placeHolder placeHolder image
|
||||
- (void)playerOnLinePhotoItems:(KNPhotoItems *)photoItems placeHolder:(UIImage *_Nullable)placeHolder;
|
||||
|
||||
/// play video by download first with photoItems and placeHolder's image
|
||||
/// @param photoItems photoItems
|
||||
/// @param placeHolder placeHolder image
|
||||
- (void)playerLocatePhotoItems:(KNPhotoItems *)photoItems placeHolder:(UIImage *_Nullable)placeHolder;
|
||||
|
||||
- (void)playerWillEndDisplay;
|
||||
|
||||
@property (nonatomic,assign) BOOL isNeedAutoPlay;
|
||||
@property (nonatomic,assign) BOOL isNeedVideoPlaceHolder;
|
||||
@property (nonatomic,assign) BOOL isSoloAmbient;
|
||||
|
||||
@property (nonatomic,weak ) KNPhotoAVPlayerView *onlinePlayerView;
|
||||
@property (nonatomic,weak ) KNPhotoLocateAVPlayerView *locatePlayerView;
|
||||
@property (nonatomic,weak ) KNProgressHUD *progressHUD;
|
||||
|
||||
@property (nonatomic,weak ) id<KNPhotoVideoCellDelegate> delegate;
|
||||
|
||||
@property (nonatomic,assign) UIViewContentMode presentedMode;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
104
SweetParty/第三方库/KNPhotoBrowser/KNPhotoVideoCell.m
Executable file
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// KNPhotoVideoCell.m
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 2019/6/11.
|
||||
// Copyright © 2019 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import "KNPhotoVideoCell.h"
|
||||
#import "KNProgressHUD.h"
|
||||
|
||||
@interface KNPhotoVideoCell()<KNPhotoPlayerViewDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation KNPhotoVideoCell
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
KNPhotoAVPlayerView *playerView = [[KNPhotoAVPlayerView alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
[playerView setDelegate:self];
|
||||
[self.contentView addSubview:playerView];
|
||||
_onlinePlayerView = playerView;
|
||||
|
||||
KNPhotoLocateAVPlayerView *locatePlayerView = [[KNPhotoLocateAVPlayerView alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
[locatePlayerView setDelegate:self];
|
||||
[self.contentView addSubview:locatePlayerView];
|
||||
_locatePlayerView = locatePlayerView;
|
||||
|
||||
KNProgressHUD *progressHUD = [[KNProgressHUD alloc] initWithFrame:(CGRect){{([UIScreen mainScreen].bounds.size.width - 40) * 0.5,([UIScreen mainScreen].bounds.size.height - 40) * 0.5},{40,40}}];
|
||||
[self.contentView addSubview:progressHUD];
|
||||
_progressHUD = progressHUD;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)playerOnLinePhotoItems:(KNPhotoItems *)photoItems placeHolder:(UIImage * _Nullable)placeHolder {
|
||||
_onlinePlayerView.isSoloAmbient = _isSoloAmbient;
|
||||
[_onlinePlayerView playerOnLinePhotoItems:photoItems placeHolder:placeHolder];
|
||||
_onlinePlayerView.hidden = false;
|
||||
_locatePlayerView.hidden = true;
|
||||
_progressHUD.hidden = true;
|
||||
}
|
||||
- (void)playerLocatePhotoItems:(KNPhotoItems *)photoItems placeHolder:(UIImage *)placeHolder {
|
||||
_locatePlayerView.isSoloAmbient = _isSoloAmbient;
|
||||
[_locatePlayerView playerLocatePhotoItems:photoItems progressHUD:_progressHUD placeHolder:placeHolder];
|
||||
_onlinePlayerView.hidden = true;
|
||||
_locatePlayerView.hidden = false;
|
||||
_progressHUD.hidden = true;
|
||||
}
|
||||
|
||||
- (void)setPresentedMode:(UIViewContentMode)presentedMode{
|
||||
_presentedMode = presentedMode;
|
||||
_onlinePlayerView.placeHolderImgView.contentMode = self.presentedMode;
|
||||
_locatePlayerView.placeHolderImgView.contentMode = self.presentedMode;
|
||||
}
|
||||
|
||||
- (void)playerWillEndDisplay{
|
||||
[_onlinePlayerView playerWillReset];
|
||||
[_locatePlayerView playerWillReset];
|
||||
}
|
||||
|
||||
/// setter
|
||||
- (void)setIsNeedAutoPlay:(BOOL)isNeedAutoPlay{
|
||||
_isNeedAutoPlay = isNeedAutoPlay;
|
||||
if (isNeedAutoPlay == true) {
|
||||
if (_onlinePlayerView.isHidden == false) {
|
||||
[_onlinePlayerView setIsNeedAutoPlay:true];
|
||||
}
|
||||
if (_locatePlayerView.isHidden == false) {
|
||||
[_locatePlayerView setIsNeedAutoPlay:true];
|
||||
}
|
||||
}
|
||||
}
|
||||
/// setter
|
||||
- (void)setIsNeedVideoPlaceHolder:(BOOL)isNeedVideoPlaceHolder{
|
||||
_isNeedVideoPlaceHolder = isNeedVideoPlaceHolder;
|
||||
_onlinePlayerView.isNeedVideoPlaceHolder = isNeedVideoPlaceHolder;
|
||||
_locatePlayerView.isNeedVideoPlaceHolder = isNeedVideoPlaceHolder;
|
||||
}
|
||||
|
||||
/// delegate function
|
||||
- (void)photoPlayerViewDismiss{
|
||||
[_onlinePlayerView playerWillReset];
|
||||
[_locatePlayerView playerWillReset];
|
||||
if ([_delegate respondsToSelector:@selector(photoVideoAVPlayerDismiss)]) {
|
||||
[_delegate photoVideoAVPlayerDismiss];
|
||||
}
|
||||
}
|
||||
/// delegate function
|
||||
- (void)photoPlayerLongPress:(UILongPressGestureRecognizer *)longPress{
|
||||
if ([_delegate respondsToSelector:@selector(photoVideoAVPlayerLongPress:)]) {
|
||||
[_delegate photoVideoAVPlayerLongPress:longPress];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)layoutSubviews{
|
||||
[super layoutSubviews];
|
||||
_onlinePlayerView.frame = self.bounds;
|
||||
_locatePlayerView.frame = self.bounds;
|
||||
_progressHUD.center = self.contentView.center;
|
||||
}
|
||||
|
||||
@end
|
||||
18
SweetParty/第三方库/KNPhotoBrowser/KNProgressHUD.h
Executable file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// KNProgressHUD.h
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 16/8/17.
|
||||
// Copyright © 2016年 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface KNProgressHUD : UIView
|
||||
|
||||
/**
|
||||
* progress, range is from 0 to 1
|
||||
*/
|
||||
@property (nonatomic, assign) CGFloat progress;
|
||||
|
||||
@end
|
||||
116
SweetParty/第三方库/KNPhotoBrowser/KNProgressHUD.m
Executable file
@@ -0,0 +1,116 @@
|
||||
//
|
||||
// KNProgressHUD.m
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 16/8/17.
|
||||
// Copyright © 2016年 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import "KNProgressHUD.h"
|
||||
|
||||
@interface KNProgressHUD()
|
||||
|
||||
@property (nonatomic, strong) CAShapeLayer *sectorLayer;
|
||||
@property (nonatomic, strong) CAShapeLayer *loadingLayer;
|
||||
@property (nonatomic, strong) CAShapeLayer *sharpLayer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation KNProgressHUD
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
[self setBackgroundColor:[UIColor clearColor]];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)rect{
|
||||
[self setupSectorLayer];
|
||||
[self setupLoadingLayer];
|
||||
[self setupSharpLayer:rect];
|
||||
}
|
||||
|
||||
// layer of sector
|
||||
- (void)setupSectorLayer{
|
||||
self.sectorLayer= [CAShapeLayer layer];
|
||||
[self.sectorLayer setFillColor:UIColor.clearColor.CGColor];
|
||||
[self.sectorLayer setLineWidth:1.f];
|
||||
[self.sectorLayer setStrokeColor:[UIColor whiteColor].CGColor];
|
||||
[self.sectorLayer setPath:[[UIBezierPath bezierPathWithOvalInRect:[self bounds]] CGPath]];
|
||||
[self.sectorLayer setHidden:YES];
|
||||
[self.layer addSublayer:self.sectorLayer];
|
||||
}
|
||||
|
||||
// loading
|
||||
- (void)setupLoadingLayer{
|
||||
self.loadingLayer = [CAShapeLayer layer];
|
||||
[self.loadingLayer setFrame:[self bounds]];
|
||||
[self.loadingLayer setAnchorPoint:CGPointMake(0.5f, 0.5f)];
|
||||
[self.loadingLayer setFillColor:UIColor.clearColor.CGColor];
|
||||
[self.loadingLayer setLineWidth:1.f];
|
||||
[self.loadingLayer setStrokeColor:[[UIColor whiteColor] CGColor]];
|
||||
|
||||
CGPoint center = CGPointMake(self.bounds.size.width * 0.5, self.bounds.size.height * 0.5);
|
||||
CGFloat loadRadius = self.bounds.size.width * 0.5;
|
||||
CGFloat endAngle = (2 * (float)M_PI) - ((float)M_PI / 8);
|
||||
|
||||
self.loadingLayer.path = [UIBezierPath bezierPathWithArcCenter:center
|
||||
radius:loadRadius
|
||||
startAngle:0
|
||||
endAngle:endAngle
|
||||
clockwise:YES].CGPath;
|
||||
|
||||
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
|
||||
[rotationAnimation setToValue:[NSNumber numberWithFloat:M_PI * 2.0]];
|
||||
[rotationAnimation setDuration:1.f];
|
||||
[rotationAnimation setCumulative:YES];
|
||||
[rotationAnimation setRepeatCount:HUGE_VALF];
|
||||
[self.loadingLayer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
|
||||
[self.layer addSublayer:self.loadingLayer];
|
||||
}
|
||||
|
||||
// sector
|
||||
- (void)setupSharpLayer:(CGRect)rect{
|
||||
CGFloat minSide = MIN(CGRectGetWidth(rect), CGRectGetHeight(rect));
|
||||
CGFloat radius = minSide/2 - 3;
|
||||
|
||||
self.sharpLayer = [CAShapeLayer layer];
|
||||
[self.sharpLayer setFrame:[self bounds]];
|
||||
[self.sharpLayer setAnchorPoint:CGPointMake(0.5f, 0.5f)];
|
||||
[self.sharpLayer setFillColor:[[UIColor clearColor] CGColor]];
|
||||
[self.sharpLayer setStrokeColor:[[UIColor whiteColor] CGColor]];
|
||||
[self.sharpLayer setLineWidth:radius];
|
||||
|
||||
[self.sharpLayer setStrokeStart:0];
|
||||
[self.sharpLayer setStrokeEnd:0];
|
||||
|
||||
CGRect pathRect = CGRectMake(CGRectGetWidth(self.bounds)/2 - radius/2, CGRectGetHeight(self.bounds)/2 - radius/2, radius, radius);
|
||||
[self.sharpLayer setPath:[[UIBezierPath bezierPathWithRoundedRect:pathRect cornerRadius:radius] CGPath]];
|
||||
[self.layer addSublayer:self.sharpLayer];
|
||||
}
|
||||
|
||||
// progress
|
||||
- (void)setProgress:(CGFloat)progress{
|
||||
progress = MAX(0.0f, progress);
|
||||
progress = MIN(1.0f, progress);
|
||||
|
||||
if (progress > 0) {
|
||||
[self.loadingLayer removeAllAnimations];
|
||||
self.sectorLayer.hidden = false;
|
||||
[self.loadingLayer removeFromSuperlayer];
|
||||
}
|
||||
|
||||
if (progress != _progress) {
|
||||
self.sharpLayer.strokeEnd = progress;
|
||||
_progress = progress;
|
||||
}
|
||||
|
||||
if (progress >= 1) {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[self setHidden:true];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
17
SweetParty/第三方库/KNPhotoBrowser/KNReachability/.svn/all-wcprops
Executable file
@@ -0,0 +1,17 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 129
|
||||
/svn/onlineIOS/!svn/ver/1042/trunk/NewMTWorks/NewMWorks/MaiTian/Vendors%E3%80%90%E7%AC%AC%E4%B8%89%E6%96%B9%E3%80%91/Reachability
|
||||
END
|
||||
Reachability.h
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 144
|
||||
/svn/onlineIOS/!svn/ver/1042/trunk/NewMTWorks/NewMWorks/MaiTian/Vendors%E3%80%90%E7%AC%AC%E4%B8%89%E6%96%B9%E3%80%91/Reachability/Reachability.h
|
||||
END
|
||||
Reachability.m
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 144
|
||||
/svn/onlineIOS/!svn/ver/1042/trunk/NewMTWorks/NewMWorks/MaiTian/Vendors%E3%80%90%E7%AC%AC%E4%B8%89%E6%96%B9%E3%80%91/Reachability/Reachability.m
|
||||
END
|
||||
96
SweetParty/第三方库/KNPhotoBrowser/KNReachability/.svn/entries
Executable file
@@ -0,0 +1,96 @@
|
||||
10
|
||||
|
||||
dir
|
||||
2234
|
||||
http://luxikang@172.16.13.104:8080/svn/onlineIOS/trunk/NewMTWorks/NewMWorks/MaiTian/Vendors%E3%80%90%E7%AC%AC%E4%B8%89%E6%96%B9%E3%80%91/Reachability
|
||||
http://luxikang@172.16.13.104:8080/svn/onlineIOS
|
||||
|
||||
|
||||
|
||||
2017-01-03T08:48:49.599819Z
|
||||
1042
|
||||
shenyinlong
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
5114eb03-57ec-48d3-8e50-381a887ab7c2
|
||||
|
||||
Reachability.h
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2017-09-01T04:09:07.000000Z
|
||||
328628dd2d4f9064468e6fc1433364be
|
||||
2017-01-03T08:48:49.599819Z
|
||||
1042
|
||||
shenyinlong
|
||||
has-props
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
3855
|
||||
|
||||
Reachability.m
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2017-09-01T04:09:07.000000Z
|
||||
18fc8ff47ef9d495d5f5a7007c1d9bb3
|
||||
2017-01-03T08:48:49.599819Z
|
||||
1042
|
||||
shenyinlong
|
||||
has-props
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
13153
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
K 14
|
||||
svn:executable
|
||||
V 0
|
||||
|
||||
END
|
||||
@@ -0,0 +1,5 @@
|
||||
K 14
|
||||
svn:executable
|
||||
V 0
|
||||
|
||||
END
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
Copyright (c) 2011, Tony Million.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
|
||||
//! Project version number for MacOSReachability.
|
||||
FOUNDATION_EXPORT double ReachabilityVersionNumber;
|
||||
|
||||
//! Project version string for MacOSReachability.
|
||||
FOUNDATION_EXPORT const unsigned char ReachabilityVersionString[];
|
||||
|
||||
/**
|
||||
* Create NS_ENUM macro if it does not exist on the targeted version of iOS or OS X.
|
||||
*
|
||||
* @see http://nshipster.com/ns_enum-ns_options/
|
||||
**/
|
||||
#ifndef NS_ENUM
|
||||
#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
|
||||
#endif
|
||||
|
||||
#define MTNetWorkAvailable [[Reachability reachabilityForInternetConnection] isReachable]
|
||||
|
||||
extern NSString *const kReachabilityChangedNotification;
|
||||
|
||||
typedef NS_ENUM(NSInteger, NetworkStatus) {
|
||||
// Apple NetworkStatus Compatible Names.
|
||||
NotReachable = 0,
|
||||
ReachableViaWiFi = 2,
|
||||
ReachableViaWWAN = 1
|
||||
};
|
||||
|
||||
@class Reachability;
|
||||
|
||||
typedef void (^NetworkReachable)(Reachability * reachability);
|
||||
typedef void (^NetworkUnreachable)(Reachability * reachability);
|
||||
typedef void (^NetworkReachability)(Reachability * reachability, SCNetworkConnectionFlags flags);
|
||||
|
||||
|
||||
@interface Reachability : NSObject
|
||||
|
||||
@property (nonatomic, copy) NetworkReachable reachableBlock;
|
||||
@property (nonatomic, copy) NetworkUnreachable unreachableBlock;
|
||||
@property (nonatomic, copy) NetworkReachability reachabilityBlock;
|
||||
|
||||
@property (nonatomic, assign) BOOL reachableOnWWAN;
|
||||
|
||||
|
||||
+(instancetype)reachabilityWithHostname:(NSString*)hostname;
|
||||
// This is identical to the function above, but is here to maintain
|
||||
//compatibility with Apples original code. (see .m)
|
||||
+(instancetype)reachabilityWithHostName:(NSString*)hostname;
|
||||
+(instancetype)reachabilityForInternetConnection;
|
||||
+(instancetype)reachabilityWithAddress:(void *)hostAddress;
|
||||
+(instancetype)reachabilityForLocalWiFi;
|
||||
|
||||
-(instancetype)initWithReachabilityRef:(SCNetworkReachabilityRef)ref;
|
||||
|
||||
-(BOOL)startNotifier;
|
||||
-(void)stopNotifier;
|
||||
|
||||
-(BOOL)isReachable;
|
||||
-(BOOL)isReachableViaWWAN;
|
||||
-(BOOL)isReachableViaWiFi;
|
||||
|
||||
// WWAN may be available, but not active until a connection has been established.
|
||||
// WiFi may require a connection for VPN on Demand.
|
||||
-(BOOL)isConnectionRequired; // Identical DDG variant.
|
||||
-(BOOL)connectionRequired; // Apple's routine.
|
||||
// Dynamic, on demand connection?
|
||||
-(BOOL)isConnectionOnDemand;
|
||||
// Is user intervention required?
|
||||
-(BOOL)isInterventionRequired;
|
||||
|
||||
-(NetworkStatus)currentReachabilityStatus;
|
||||
-(SCNetworkReachabilityFlags)reachabilityFlags;
|
||||
-(NSString*)currentReachabilityString;
|
||||
-(NSString*)currentReachabilityFlags;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,475 @@
|
||||
/*
|
||||
Copyright (c) 2011, Tony Million.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "Reachability.h"
|
||||
|
||||
#import <sys/socket.h>
|
||||
#import <netinet/in.h>
|
||||
#import <netinet6/in6.h>
|
||||
#import <arpa/inet.h>
|
||||
#import <ifaddrs.h>
|
||||
#import <netdb.h>
|
||||
|
||||
|
||||
NSString *const kReachabilityChangedNotification = @"kReachabilityChangedNotification";
|
||||
|
||||
|
||||
@interface Reachability ()
|
||||
|
||||
@property (nonatomic, assign) SCNetworkReachabilityRef reachabilityRef;
|
||||
@property (nonatomic, strong) dispatch_queue_t reachabilitySerialQueue;
|
||||
@property (nonatomic, strong) id reachabilityObject;
|
||||
|
||||
-(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags;
|
||||
-(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
static NSString *reachabilityFlags(SCNetworkReachabilityFlags flags)
|
||||
{
|
||||
return [NSString stringWithFormat:@"%c%c %c%c%c%c%c%c%c",
|
||||
#if TARGET_OS_IPHONE
|
||||
(flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-',
|
||||
#else
|
||||
'X',
|
||||
#endif
|
||||
(flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-'];
|
||||
}
|
||||
|
||||
// Start listening for reachability notifications on the current run loop
|
||||
static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
|
||||
{
|
||||
#pragma unused (target)
|
||||
|
||||
Reachability *reachability = ((__bridge Reachability*)info);
|
||||
|
||||
// We probably don't need an autoreleasepool here, as GCD docs state each queue has its own autorelease pool,
|
||||
// but what the heck eh?
|
||||
@autoreleasepool
|
||||
{
|
||||
[reachability reachabilityChanged:flags];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@implementation Reachability
|
||||
|
||||
#pragma mark - Class Constructor Methods
|
||||
|
||||
+(instancetype)reachabilityWithHostName:(NSString*)hostname
|
||||
{
|
||||
return [Reachability reachabilityWithHostname:hostname];
|
||||
}
|
||||
|
||||
+(instancetype)reachabilityWithHostname:(NSString*)hostname
|
||||
{
|
||||
SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(NULL, [hostname UTF8String]);
|
||||
if (ref)
|
||||
{
|
||||
id reachability = [[self alloc] initWithReachabilityRef:ref];
|
||||
|
||||
return reachability;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
+(instancetype)reachabilityWithAddress:(void *)hostAddress
|
||||
{
|
||||
SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress);
|
||||
if (ref)
|
||||
{
|
||||
id reachability = [[self alloc] initWithReachabilityRef:ref];
|
||||
|
||||
return reachability;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
+(instancetype)reachabilityForInternetConnection
|
||||
{
|
||||
struct sockaddr_in zeroAddress;
|
||||
bzero(&zeroAddress, sizeof(zeroAddress));
|
||||
zeroAddress.sin_len = sizeof(zeroAddress);
|
||||
zeroAddress.sin_family = AF_INET;
|
||||
|
||||
return [self reachabilityWithAddress:&zeroAddress];
|
||||
}
|
||||
|
||||
+(instancetype)reachabilityForLocalWiFi
|
||||
{
|
||||
struct sockaddr_in localWifiAddress;
|
||||
bzero(&localWifiAddress, sizeof(localWifiAddress));
|
||||
localWifiAddress.sin_len = sizeof(localWifiAddress);
|
||||
localWifiAddress.sin_family = AF_INET;
|
||||
// IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
|
||||
localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
|
||||
|
||||
return [self reachabilityWithAddress:&localWifiAddress];
|
||||
}
|
||||
|
||||
|
||||
// Initialization methods
|
||||
|
||||
-(instancetype)initWithReachabilityRef:(SCNetworkReachabilityRef)ref
|
||||
{
|
||||
self = [super init];
|
||||
if (self != nil)
|
||||
{
|
||||
self.reachableOnWWAN = YES;
|
||||
self.reachabilityRef = ref;
|
||||
|
||||
// We need to create a serial queue.
|
||||
// We allocate this once for the lifetime of the notifier.
|
||||
|
||||
self.reachabilitySerialQueue = dispatch_queue_create("com.tonymillion.reachability", NULL);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)dealloc
|
||||
{
|
||||
[self stopNotifier];
|
||||
|
||||
if(self.reachabilityRef)
|
||||
{
|
||||
CFRelease(self.reachabilityRef);
|
||||
self.reachabilityRef = nil;
|
||||
}
|
||||
|
||||
self.reachableBlock = nil;
|
||||
self.unreachableBlock = nil;
|
||||
self.reachabilityBlock = nil;
|
||||
self.reachabilitySerialQueue = nil;
|
||||
}
|
||||
|
||||
#pragma mark - Notifier Methods
|
||||
|
||||
// Notifier
|
||||
// NOTE: This uses GCD to trigger the blocks - they *WILL NOT* be called on THE MAIN THREAD
|
||||
// - In other words DO NOT DO ANY UI UPDATES IN THE BLOCKS.
|
||||
// INSTEAD USE dispatch_async(dispatch_get_main_queue(), ^{UISTUFF}) (or dispatch_sync if you want)
|
||||
|
||||
-(BOOL)startNotifier
|
||||
{
|
||||
// allow start notifier to be called multiple times
|
||||
if(self.reachabilityObject && (self.reachabilityObject == self))
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
SCNetworkReachabilityContext context = { 0, NULL, NULL, NULL, NULL };
|
||||
context.info = (__bridge void *)self;
|
||||
|
||||
if(SCNetworkReachabilitySetCallback(self.reachabilityRef, TMReachabilityCallback, &context))
|
||||
{
|
||||
// Set it as our reachability queue, which will retain the queue
|
||||
if(SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, self.reachabilitySerialQueue))
|
||||
{
|
||||
// this should do a retain on ourself, so as long as we're in notifier mode we shouldn't disappear out from under ourselves
|
||||
// woah
|
||||
self.reachabilityObject = self;
|
||||
return YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG
|
||||
NSLog(@"SCNetworkReachabilitySetDispatchQueue() failed: %s", SCErrorString(SCError()));
|
||||
#endif
|
||||
|
||||
// UH OH - FAILURE - stop any callbacks!
|
||||
SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG
|
||||
NSLog(@"SCNetworkReachabilitySetCallback() failed: %s", SCErrorString(SCError()));
|
||||
#endif
|
||||
}
|
||||
|
||||
// if we get here we fail at the internet
|
||||
self.reachabilityObject = nil;
|
||||
return NO;
|
||||
}
|
||||
|
||||
-(void)stopNotifier
|
||||
{
|
||||
// First stop, any callbacks!
|
||||
SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
|
||||
|
||||
// Unregister target from the GCD serial dispatch queue.
|
||||
SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, NULL);
|
||||
|
||||
self.reachabilityObject = nil;
|
||||
}
|
||||
|
||||
#pragma mark - reachability tests
|
||||
|
||||
// This is for the case where you flick the airplane mode;
|
||||
// you end up getting something like this:
|
||||
//Reachability: WR ct-----
|
||||
//Reachability: -- -------
|
||||
//Reachability: WR ct-----
|
||||
//Reachability: -- -------
|
||||
// We treat this as 4 UNREACHABLE triggers - really apple should do better than this
|
||||
|
||||
#define testcase (kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsTransientConnection)
|
||||
|
||||
-(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags
|
||||
{
|
||||
BOOL connectionUP = YES;
|
||||
|
||||
if(!(flags & kSCNetworkReachabilityFlagsReachable))
|
||||
connectionUP = NO;
|
||||
|
||||
if( (flags & testcase) == testcase )
|
||||
connectionUP = NO;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
if(flags & kSCNetworkReachabilityFlagsIsWWAN)
|
||||
{
|
||||
// We're on 3G.
|
||||
if(!self.reachableOnWWAN)
|
||||
{
|
||||
// We don't want to connect when on 3G.
|
||||
connectionUP = NO;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return connectionUP;
|
||||
}
|
||||
|
||||
-(BOOL)isReachable
|
||||
{
|
||||
SCNetworkReachabilityFlags flags;
|
||||
|
||||
if(!SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
|
||||
return NO;
|
||||
|
||||
return [self isReachableWithFlags:flags];
|
||||
}
|
||||
|
||||
-(BOOL)isReachableViaWWAN
|
||||
{
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
SCNetworkReachabilityFlags flags = 0;
|
||||
|
||||
if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
|
||||
{
|
||||
// Check we're REACHABLE
|
||||
if(flags & kSCNetworkReachabilityFlagsReachable)
|
||||
{
|
||||
// Now, check we're on WWAN
|
||||
if(flags & kSCNetworkReachabilityFlagsIsWWAN)
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
-(BOOL)isReachableViaWiFi
|
||||
{
|
||||
SCNetworkReachabilityFlags flags = 0;
|
||||
|
||||
if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
|
||||
{
|
||||
// Check we're reachable
|
||||
if((flags & kSCNetworkReachabilityFlagsReachable))
|
||||
{
|
||||
#if TARGET_OS_IPHONE
|
||||
// Check we're NOT on WWAN
|
||||
if((flags & kSCNetworkReachabilityFlagsIsWWAN))
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
#endif
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
// WWAN may be available, but not active until a connection has been established.
|
||||
// WiFi may require a connection for VPN on Demand.
|
||||
-(BOOL)isConnectionRequired
|
||||
{
|
||||
return [self connectionRequired];
|
||||
}
|
||||
|
||||
-(BOOL)connectionRequired
|
||||
{
|
||||
SCNetworkReachabilityFlags flags;
|
||||
|
||||
if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
|
||||
{
|
||||
return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Dynamic, on demand connection?
|
||||
-(BOOL)isConnectionOnDemand
|
||||
{
|
||||
SCNetworkReachabilityFlags flags;
|
||||
|
||||
if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
|
||||
{
|
||||
return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
|
||||
(flags & (kSCNetworkReachabilityFlagsConnectionOnTraffic | kSCNetworkReachabilityFlagsConnectionOnDemand)));
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Is user intervention required?
|
||||
-(BOOL)isInterventionRequired
|
||||
{
|
||||
SCNetworkReachabilityFlags flags;
|
||||
|
||||
if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
|
||||
{
|
||||
return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
|
||||
(flags & kSCNetworkReachabilityFlagsInterventionRequired));
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - reachability status stuff
|
||||
|
||||
-(NetworkStatus)currentReachabilityStatus
|
||||
{
|
||||
if([self isReachable])
|
||||
{
|
||||
if([self isReachableViaWiFi])
|
||||
return ReachableViaWiFi;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
return ReachableViaWWAN;
|
||||
#endif
|
||||
}
|
||||
|
||||
return NotReachable;
|
||||
}
|
||||
|
||||
-(SCNetworkReachabilityFlags)reachabilityFlags
|
||||
{
|
||||
SCNetworkReachabilityFlags flags = 0;
|
||||
|
||||
if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
|
||||
{
|
||||
return flags;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
-(NSString*)currentReachabilityString
|
||||
{
|
||||
NetworkStatus temp = [self currentReachabilityStatus];
|
||||
|
||||
if(temp == ReachableViaWWAN)
|
||||
{
|
||||
// Updated for the fact that we have CDMA phones now!
|
||||
return NSLocalizedString(@"Cellular", @"");
|
||||
}
|
||||
if (temp == ReachableViaWiFi)
|
||||
{
|
||||
return NSLocalizedString(@"WiFi", @"");
|
||||
}
|
||||
|
||||
return NSLocalizedString(@"No Connection", @"");
|
||||
}
|
||||
|
||||
-(NSString*)currentReachabilityFlags
|
||||
{
|
||||
return reachabilityFlags([self reachabilityFlags]);
|
||||
}
|
||||
|
||||
#pragma mark - Callback function calls this method
|
||||
|
||||
-(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags
|
||||
{
|
||||
if([self isReachableWithFlags:flags])
|
||||
{
|
||||
if(self.reachableBlock)
|
||||
{
|
||||
self.reachableBlock(self);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(self.unreachableBlock)
|
||||
{
|
||||
self.unreachableBlock(self);
|
||||
}
|
||||
}
|
||||
|
||||
if(self.reachabilityBlock)
|
||||
{
|
||||
self.reachabilityBlock(self, flags);
|
||||
}
|
||||
|
||||
// this makes sure the change notification happens on the MAIN THREAD
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kReachabilityChangedNotification
|
||||
object:self];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Debug Description
|
||||
|
||||
- (NSString *) description
|
||||
{
|
||||
NSString *description = [NSString stringWithFormat:@"<%@: %#x (%@)>",
|
||||
NSStringFromClass([self class]), (unsigned int) self, [self currentReachabilityFlags]];
|
||||
return description;
|
||||
}
|
||||
|
||||
@end
|
||||
63
SweetParty/第三方库/KNPhotoBrowser/KNReachability/KNReachability.h
Executable file
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// KNReachability.h
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 2021/3/19.
|
||||
// Copyright © 2021 LuKane. All rights reserved.
|
||||
|
||||
// ***************************************
|
||||
// **** this class is from the github ****
|
||||
// ***************************************
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern NSString *const kKNReachabilityChangedNotification;
|
||||
|
||||
typedef NS_ENUM(NSInteger, NetworkStatus) {
|
||||
NotReachable = 0,
|
||||
ReachableViaWiFi = 2,
|
||||
ReachableViaWWAN = 1
|
||||
};
|
||||
|
||||
@class KNReachability;
|
||||
|
||||
typedef void (^NetworkReachable)(KNReachability * reachability);
|
||||
typedef void (^NetworkUnreachable)(KNReachability * reachability);
|
||||
typedef void (^NetworkReachability)(KNReachability * reachability, SCNetworkConnectionFlags flags);
|
||||
|
||||
@interface KNReachability : NSObject
|
||||
|
||||
@property (nonatomic, copy, nullable) NetworkReachable reachableBlock;
|
||||
@property (nonatomic, copy, nullable) NetworkUnreachable unreachableBlock;
|
||||
@property (nonatomic, copy, nullable) NetworkReachability reachabilityBlock;
|
||||
|
||||
@property (nonatomic, assign) BOOL reachableOnWWAN;
|
||||
|
||||
+ (instancetype)reachabilityWithHostname:(NSString*)hostname;
|
||||
+ (instancetype)reachabilityWithHostName:(NSString*)hostname;
|
||||
+ (instancetype)reachabilityForInternetConnection;
|
||||
+ (instancetype)reachabilityWithAddress:(void *)hostAddress;
|
||||
+ (instancetype)reachabilityForLocalWiFi;
|
||||
|
||||
- (instancetype)initWithReachabilityRef:(SCNetworkReachabilityRef)ref;
|
||||
- (BOOL)startNotifier;
|
||||
- (void)stopNotifier;
|
||||
- (BOOL)isReachable;
|
||||
- (BOOL)isReachableViaWWAN;
|
||||
- (BOOL)isReachableViaWiFi;
|
||||
- (BOOL)isConnectionRequired;
|
||||
- (BOOL)connectionRequired;
|
||||
- (BOOL)isConnectionOnDemand;
|
||||
- (BOOL)isInterventionRequired;
|
||||
- (NetworkStatus)currentReachabilityStatus;
|
||||
- (SCNetworkReachabilityFlags)reachabilityFlags;
|
||||
- (NSString *)currentReachabilityString;
|
||||
- (NSString *)currentReachabilityFlags;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
378
SweetParty/第三方库/KNPhotoBrowser/KNReachability/KNReachability.m
Executable file
@@ -0,0 +1,378 @@
|
||||
//
|
||||
// KNReachability.m
|
||||
// KNPhotoBrowser
|
||||
//
|
||||
// Created by LuKane on 2021/3/19.
|
||||
// Copyright © 2021 LuKane. All rights reserved.
|
||||
//
|
||||
|
||||
#import "KNReachability.h"
|
||||
#import <sys/socket.h>
|
||||
#import <netinet/in.h>
|
||||
#import <netinet6/in6.h>
|
||||
#import <arpa/inet.h>
|
||||
#import <ifaddrs.h>
|
||||
#import <netdb.h>
|
||||
|
||||
NSString *const kKNReachabilityChangedNotification = @"kKNReachabilityChangedNotification";
|
||||
|
||||
|
||||
@interface KNReachability()
|
||||
|
||||
@property (nonatomic, assign) SCNetworkReachabilityRef reachabilityRef;
|
||||
@property (nonatomic, strong) dispatch_queue_t reachabilitySerialQueue;
|
||||
@property (nonatomic, strong) id reachabilityObject;
|
||||
|
||||
- (void)reachabilityChanged:(SCNetworkReachabilityFlags)flags;
|
||||
- (BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags;
|
||||
|
||||
@end
|
||||
|
||||
static NSString *reachabilityFlags(SCNetworkReachabilityFlags flags){
|
||||
return [NSString stringWithFormat:@"%c%c %c%c%c%c%c%c%c",
|
||||
#if TARGET_OS_IPHONE
|
||||
(flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-',
|
||||
#else
|
||||
'X',
|
||||
#endif
|
||||
(flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-'];
|
||||
}
|
||||
|
||||
static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info){
|
||||
#pragma unused (target)
|
||||
|
||||
KNReachability *reachability = ((__bridge KNReachability*)info);
|
||||
|
||||
// We probably don't need an autoreleasepool here, as GCD docs state each queue has its own autorelease pool,
|
||||
// but what the heck eh?
|
||||
@autoreleasepool
|
||||
{
|
||||
[reachability reachabilityChanged:flags];
|
||||
}
|
||||
}
|
||||
|
||||
@implementation KNReachability
|
||||
|
||||
+ (instancetype)reachabilityWithHostName:(NSString*)hostname{
|
||||
return [KNReachability reachabilityWithHostname:hostname];
|
||||
}
|
||||
|
||||
+ (instancetype)reachabilityWithHostname:(NSString*)hostname{
|
||||
SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(NULL, [hostname UTF8String]);
|
||||
if (ref)
|
||||
{
|
||||
id reachability = [[self alloc] initWithReachabilityRef:ref];
|
||||
|
||||
return reachability;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (instancetype)reachabilityWithAddress:(void *)hostAddress{
|
||||
SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress);
|
||||
if (ref)
|
||||
{
|
||||
id reachability = [[self alloc] initWithReachabilityRef:ref];
|
||||
|
||||
return reachability;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (instancetype)reachabilityForInternetConnection{
|
||||
struct sockaddr_in zeroAddress;
|
||||
bzero(&zeroAddress, sizeof(zeroAddress));
|
||||
zeroAddress.sin_len = sizeof(zeroAddress);
|
||||
zeroAddress.sin_family = AF_INET;
|
||||
|
||||
return [self reachabilityWithAddress:&zeroAddress];
|
||||
}
|
||||
|
||||
+ (instancetype)reachabilityForLocalWiFi{
|
||||
struct sockaddr_in localWifiAddress;
|
||||
bzero(&localWifiAddress, sizeof(localWifiAddress));
|
||||
localWifiAddress.sin_len = sizeof(localWifiAddress);
|
||||
localWifiAddress.sin_family = AF_INET;
|
||||
// IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
|
||||
localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
|
||||
|
||||
return [self reachabilityWithAddress:&localWifiAddress];
|
||||
}
|
||||
|
||||
- (instancetype)initWithReachabilityRef:(SCNetworkReachabilityRef)ref{
|
||||
self = [super init];
|
||||
if (self != nil)
|
||||
{
|
||||
self.reachableOnWWAN = YES;
|
||||
self.reachabilityRef = ref;
|
||||
|
||||
// We need to create a serial queue.
|
||||
// We allocate this once for the lifetime of the notifier.
|
||||
|
||||
self.reachabilitySerialQueue = dispatch_queue_create("com.tonymillion.reachability", NULL);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc{
|
||||
[self stopNotifier];
|
||||
if(self.reachabilityRef){
|
||||
CFRelease(self.reachabilityRef);
|
||||
self.reachabilityRef = nil;
|
||||
}
|
||||
self.reachableBlock = nil;
|
||||
self.unreachableBlock = nil;
|
||||
self.reachabilityBlock = nil;
|
||||
self.reachabilitySerialQueue = nil;
|
||||
}
|
||||
|
||||
#pragma mark - Notifier Methods
|
||||
|
||||
// Notifier
|
||||
// NOTE: This uses GCD to trigger the blocks - they *WILL NOT* be called on THE MAIN THREAD
|
||||
// - In other words DO NOT DO ANY UI UPDATES IN THE BLOCKS.
|
||||
// INSTEAD USE dispatch_async(dispatch_get_main_queue(), ^{UISTUFF}) (or dispatch_sync if you want)
|
||||
|
||||
- (BOOL)startNotifier{
|
||||
// allow start notifier to be called multiple times
|
||||
if(self.reachabilityObject && (self.reachabilityObject == self)){
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
SCNetworkReachabilityContext context = { 0, NULL, NULL, NULL, NULL };
|
||||
context.info = (__bridge void *)self;
|
||||
|
||||
if(SCNetworkReachabilitySetCallback(self.reachabilityRef, TMReachabilityCallback, &context)){
|
||||
// Set it as our reachability queue, which will retain the queue
|
||||
if(SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, self.reachabilitySerialQueue)){
|
||||
// this should do a retain on ourself, so as long as we're in notifier mode we shouldn't disappear out from under ourselves
|
||||
// woah
|
||||
self.reachabilityObject = self;
|
||||
return YES;
|
||||
}else {
|
||||
#ifdef DEBUG
|
||||
NSLog(@"SCNetworkReachabilitySetDispatchQueue() failed: %s", SCErrorString(SCError()));
|
||||
#endif
|
||||
|
||||
// UH OH - FAILURE - stop any callbacks!
|
||||
SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
|
||||
}
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
NSLog(@"SCNetworkReachabilitySetCallback() failed: %s", SCErrorString(SCError()));
|
||||
#endif
|
||||
}
|
||||
|
||||
// if we get here we fail at the internet
|
||||
self.reachabilityObject = nil;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)stopNotifier{
|
||||
// First stop, any callbacks!
|
||||
SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
|
||||
|
||||
// Unregister target from the GCD serial dispatch queue.
|
||||
SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, NULL);
|
||||
|
||||
self.reachabilityObject = nil;
|
||||
}
|
||||
|
||||
#pragma mark - reachability tests
|
||||
|
||||
// This is for the case where you flick the airplane mode;
|
||||
// you end up getting something like this:
|
||||
//Reachability: WR ct-----
|
||||
//Reachability: -- -------
|
||||
//Reachability: WR ct-----
|
||||
//Reachability: -- -------
|
||||
// We treat this as 4 UNREACHABLE triggers - really apple should do better than this
|
||||
|
||||
#define testcase (kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsTransientConnection)
|
||||
|
||||
- (BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags{
|
||||
BOOL connectionUP = YES;
|
||||
|
||||
if(!(flags & kSCNetworkReachabilityFlagsReachable))
|
||||
connectionUP = NO;
|
||||
|
||||
if( (flags & testcase) == testcase )
|
||||
connectionUP = NO;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
if(flags & kSCNetworkReachabilityFlagsIsWWAN){
|
||||
// We're on 3G.
|
||||
if(!self.reachableOnWWAN)
|
||||
{
|
||||
// We don't want to connect when on 3G.
|
||||
connectionUP = NO;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return connectionUP;
|
||||
}
|
||||
|
||||
- (BOOL)isReachable{
|
||||
SCNetworkReachabilityFlags flags;
|
||||
|
||||
if(!SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
|
||||
return NO;
|
||||
|
||||
return [self isReachableWithFlags:flags];
|
||||
}
|
||||
|
||||
- (BOOL)isReachableViaWWAN{
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
SCNetworkReachabilityFlags flags = 0;
|
||||
|
||||
if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)){
|
||||
// Check we're REACHABLE
|
||||
if(flags & kSCNetworkReachabilityFlagsReachable){
|
||||
// Now, check we're on WWAN
|
||||
if(flags & kSCNetworkReachabilityFlagsIsWWAN){
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isReachableViaWiFi{
|
||||
SCNetworkReachabilityFlags flags = 0;
|
||||
|
||||
if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)){
|
||||
// Check we're reachable
|
||||
if((flags & kSCNetworkReachabilityFlagsReachable)){
|
||||
#if TARGET_OS_IPHONE
|
||||
// Check we're NOT on WWAN
|
||||
if((flags & kSCNetworkReachabilityFlagsIsWWAN)){
|
||||
return NO;
|
||||
}
|
||||
#endif
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
// WWAN may be available, but not active until a connection has been established.
|
||||
// WiFi may require a connection for VPN on Demand.
|
||||
- (BOOL)isConnectionRequired{
|
||||
return [self connectionRequired];
|
||||
}
|
||||
|
||||
- (BOOL)connectionRequired{
|
||||
SCNetworkReachabilityFlags flags;
|
||||
|
||||
if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)){
|
||||
return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Dynamic, on demand connection?
|
||||
- (BOOL)isConnectionOnDemand{
|
||||
SCNetworkReachabilityFlags flags;
|
||||
|
||||
if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)){
|
||||
return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
|
||||
(flags & (kSCNetworkReachabilityFlagsConnectionOnTraffic | kSCNetworkReachabilityFlagsConnectionOnDemand)));
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Is user intervention required?
|
||||
- (BOOL)isInterventionRequired{
|
||||
SCNetworkReachabilityFlags flags;
|
||||
|
||||
if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)){
|
||||
return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
|
||||
(flags & kSCNetworkReachabilityFlagsInterventionRequired));
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - reachability status stuff
|
||||
|
||||
- (NetworkStatus)currentReachabilityStatus{
|
||||
if([self isReachable]){
|
||||
if([self isReachableViaWiFi])
|
||||
return ReachableViaWiFi;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
return ReachableViaWWAN;
|
||||
#endif
|
||||
}
|
||||
return NotReachable;
|
||||
}
|
||||
|
||||
- (SCNetworkReachabilityFlags)reachabilityFlags{
|
||||
SCNetworkReachabilityFlags flags = 0;
|
||||
|
||||
if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)){
|
||||
return flags;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (NSString *)currentReachabilityString{
|
||||
NetworkStatus temp = [self currentReachabilityStatus];
|
||||
|
||||
if(temp == ReachableViaWWAN){
|
||||
// Updated for the fact that we have CDMA phones now!
|
||||
return NSLocalizedString(@"Cellular", @"");
|
||||
}
|
||||
if (temp == ReachableViaWiFi){
|
||||
return NSLocalizedString(@"WiFi", @"");
|
||||
}
|
||||
|
||||
return NSLocalizedString(@"No Connection", @"");
|
||||
}
|
||||
|
||||
- (NSString *)currentReachabilityFlags{
|
||||
return reachabilityFlags([self reachabilityFlags]);
|
||||
}
|
||||
|
||||
#pragma mark - Callback function calls this method
|
||||
|
||||
- (void)reachabilityChanged:(SCNetworkReachabilityFlags)flags{
|
||||
if([self isReachableWithFlags:flags]){
|
||||
if(self.reachableBlock){
|
||||
self.reachableBlock(self);
|
||||
}
|
||||
}else{
|
||||
if(self.unreachableBlock){
|
||||
self.unreachableBlock(self);
|
||||
}
|
||||
}
|
||||
|
||||
if(self.reachabilityBlock){
|
||||
self.reachabilityBlock(self, flags);
|
||||
}
|
||||
|
||||
// this makes sure the change notification happens on the MAIN THREAD
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kKNReachabilityChangedNotification
|
||||
object:self];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||