Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.可滑动连续多选,类似系统相册选择模式 #1666

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 15 additions & 9 deletions TZImagePickerController.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
48C130DE2B6F41BF00D8827C /* TZPhotoPickerController+Custom.m in Sources */ = {isa = PBXBuildFile; fileRef = 48C130DD2B6F41BF00D8827C /* TZPhotoPickerController+Custom.m */; };
570A348C26D7275C000EEF2E /* Photos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 570A348B26D7275C000EEF2E /* Photos.framework */; };
570A348E26D72762000EEF2E /* PhotosUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 570A348D26D72762000EEF2E /* PhotosUI.framework */; };
570A349026D72768000EEF2E /* PhotosUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 570A348F26D72768000EEF2E /* PhotosUI.framework */; };
Expand Down Expand Up @@ -122,6 +123,8 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
48C130DC2B6F41BF00D8827C /* TZPhotoPickerController+Custom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TZPhotoPickerController+Custom.h"; sourceTree = "<group>"; };
48C130DD2B6F41BF00D8827C /* TZPhotoPickerController+Custom.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "TZPhotoPickerController+Custom.m"; sourceTree = "<group>"; };
570A348B26D7275C000EEF2E /* Photos.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Photos.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk/System/Library/Frameworks/Photos.framework; sourceTree = DEVELOPER_DIR; };
570A348D26D72762000EEF2E /* PhotosUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PhotosUI.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk/System/iOSSupport/System/Library/Frameworks/PhotosUI.framework; sourceTree = DEVELOPER_DIR; };
570A348F26D72768000EEF2E /* PhotosUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PhotosUI.framework; path = System/Library/Frameworks/PhotosUI.framework; sourceTree = SDKROOT; };
Expand Down Expand Up @@ -281,6 +284,8 @@
900E657E1C2BB8D5003D9A9E /* TZImagePickerController */ = {
isa = PBXGroup;
children = (
48C130DC2B6F41BF00D8827C /* TZPhotoPickerController+Custom.h */,
48C130DD2B6F41BF00D8827C /* TZPhotoPickerController+Custom.m */,
5B7A0D4F288691E7002B2603 /* Location */,
901F228F215CABD600F604ED /* FLAnimatedImage */,
900EEFF51C2BBF7600EA709B /* TZImagePickerController */,
Expand Down Expand Up @@ -525,7 +530,7 @@
TargetAttributes = {
900E657B1C2BB8D5003D9A9E = {
CreatedOnToolsVersion = 7.2;
DevelopmentTeam = LTFQDC2QVX;
DevelopmentTeam = Z38FDTGA25;
ProvisioningStyle = Automatic;
};
900E65941C2BB8D5003D9A9E = {
Expand Down Expand Up @@ -615,6 +620,7 @@
B662C36F2662914D006A672A /* TZVideoEditedPreviewController.m in Sources */,
9038D5911C3974F0007DE549 /* TZTestCell.m in Sources */,
900EF00F1C2C0B1100EA709B /* TZAssetModel.m in Sources */,
48C130DE2B6F41BF00D8827C /* TZPhotoPickerController+Custom.m in Sources */,
900EEFFE1C2BD58B00EA709B /* TZPhotoPickerController.m in Sources */,
900EF0021C2BD7E400EA709B /* TZAssetCell.m in Sources */,
6DD0502B1DF659ED0057C78D /* TZProgressView.m in Sources */,
Expand Down Expand Up @@ -781,7 +787,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.2;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -829,7 +835,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.2;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
Expand All @@ -844,13 +850,13 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = LTFQDC2QVX;
DEVELOPMENT_TEAM = Z38FDTGA25;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/TZImagePickerController",
);
INFOPLIST_FILE = TZImagePickerController/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 3.8.4;
PRODUCT_BUNDLE_IDENTIFIER = tanzhenios2022.TZImagePickerController.www;
Expand All @@ -869,13 +875,13 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = LTFQDC2QVX;
DEVELOPMENT_TEAM = Z38FDTGA25;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/TZImagePickerController",
);
INFOPLIST_FILE = TZImagePickerController/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 3.8.4;
PRODUCT_BUNDLE_IDENTIFIER = tanzhenios2022.TZImagePickerController.www;
Expand Down Expand Up @@ -962,7 +968,7 @@
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = TZImagePickerControllerFramework/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MARKETING_VERSION = 3.8.3;
OTHER_LDFLAGS = "-all_load";
Expand Down Expand Up @@ -1003,7 +1009,7 @@
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = TZImagePickerControllerFramework/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MARKETING_VERSION = 3.8.3;
OTHER_LDFLAGS = "-all_load";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,11 +237,13 @@ - (void)setAsset:(PHAsset *)asset {
[self resizeSubviews];
if (self.imageView.tz_height && self.allowCrop) {
CGFloat scale = MAX(self.cropRect.size.width / self.imageView.tz_width, self.cropRect.size.height / self.imageView.tz_height);
if (self.scaleAspectFillCrop && scale > 1) { // 如果设置图片缩放裁剪并且图片需要缩放
if (self.scaleAspectFillCrop) { // 如果设置图片缩放裁剪并且图片需要缩放
CGFloat multiple = self.scrollView.maximumZoomScale / self.scrollView.minimumZoomScale;
self.scrollView.minimumZoomScale = scale;
self.scrollView.maximumZoomScale = scale * MAX(multiple, 2);
[self.scrollView setZoomScale:scale animated:YES];
if (scale > 1) { // 自动放大与框同宽,如果是缩小,等用户手动操作
[self.scrollView setZoomScale:scale animated:YES];
}
}
}

Expand Down
17 changes: 17 additions & 0 deletions TZImagePickerController/TZPhotoPickerController+Custom.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// TZPhotoPickerController+Custom.h
// yxqls
//
// Created by mac17 on 2021/8/24.
//

#import "TZPhotoPickerController.h"
#import "TZImagePickerController.h"

NS_ASSUME_NONNULL_BEGIN

@interface TZPhotoPickerController (Custom)

@end

NS_ASSUME_NONNULL_END
162 changes: 162 additions & 0 deletions TZImagePickerController/TZPhotoPickerController+Custom.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
//
// TZPhotoPickerController+Custom.m
// yxqls
//
// Created by mac17 on 2021/8/24.
//

#import "TZPhotoPickerController+Custom.h"
#import <Photos/PHImageManager.h>
#import <Photos/Photos.h>
#import "TZAssetCell.h"
#import <objc/runtime.h>

@implementation TZPhotoPickerController (Custom)

+ (void)load {
[self swizzleSEL:NSSelectorFromString(@"initSubviews") withSEL:@selector(sw_initSubviews)];
}

+ (void)swizzleSEL:(SEL)originalSEL withSEL:(SEL)swizzledSEL {
Class class = [self class];

Method originalMethod = class_getInstanceMethod(class, originalSEL);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSEL);

BOOL didAddMethod =
class_addMethod(class,
originalSEL,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));

if (didAddMethod) {
class_replaceMethod(class,
swizzledSEL,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}

- (void)sw_initSubviews {
[self sw_initSubviews];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
TZImagePickerController *imgPicker = (TZImagePickerController *)self.navigationController;
if (imgPicker.maxImagesCount == 1 || imgPicker.allowPickingVideo) {
return;//只能选择一个或者能选视频,不启用多选
}
UIPanGestureRecognizer *ges = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(multiSelectAction:)];
[self.view addGestureRecognizer:ges];
});
}

//@property (nonatomic, strong) TZCollectionView *collectionView;UICollectionViewFlowLayout *layout
- (TZCollectionView *)collView {
return [self valueForKey:@"collectionView"];
}
- (UICollectionViewFlowLayout *)collLayout {
return [self valueForKey:@"layout"];
}

static CGFloat itemMargin = 5;//这个变量直接复制自原文件,如果原文件改了这个需要也改一下
- (void)multiSelectAction:(UIGestureRecognizer *)ges {
static NSInteger beginIdx = -1;
CGPoint lc = [ges locationInView:self.collView];
NSInteger h = self.collLayout.itemSize.height + itemMargin;
NSInteger w = self.collLayout.itemSize.width + itemMargin;
NSInteger line = (NSInteger)lc.y / h;
NSInteger column = (NSInteger)lc.x / w;
NSInteger idx = line * self.columnNumber + column;
if (beginIdx == -1) {
beginIdx = idx;
}
[self selectIdx:beginIdx toIdx2:idx];
if (self.collView.contentOffset.y + self.collView.bounds.size.height - lc.y < 40) {
selectingTopEdge = NO;
[self dpLink];
} else if (lc.y - self.collView.contentOffset.y < 40) {
selectingTopEdge = YES;
[self dpLink];
} else {
[dpLink invalidate];
dpLink = nil;
}
if (ges.state == UIGestureRecognizerStateEnded || ges.state == UIGestureRecognizerStateCancelled) {
beginIdx = -1;
isCancel = -1;
previousEndIdx = -1;
currEndIdx = -1;
[dpLink invalidate];
dpLink = nil;
}
}

- (void)selectingEdge {
CGPoint off = self.collView.contentOffset;
off.y += selectingTopEdge ? -5 : 5;
off.y = MAX(- itemMargin, off.y);
off.y = MIN(self.collView.contentSize.height - self.collView.frame.size.height + itemMargin, off.y);
self.collView.contentOffset = off;
}

- (CADisplayLink *)dpLink {
if (!dpLink) {
dpLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(selectingEdge)];
[dpLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
return dpLink;
}

static CADisplayLink *dpLink;
static BOOL selectingTopEdge;
static NSInteger isCancel = -1;
static NSInteger previousEndIdx = -1;//选过的最大index
static NSInteger currEndIdx = -1;
- (void)selectIdx:(NSInteger)idx toIdx2:(NSInteger)idx2 {
if (currEndIdx == idx2) {
return;//手指在同一个cell上多次触发
}
currEndIdx = idx2;
if (previousEndIdx != -1) {
BOOL preBigger = previousEndIdx > idx2 && previousEndIdx > idx;
BOOL preSmaller = previousEndIdx < idx2 && previousEndIdx < idx;
if (preBigger || preSmaller) {//选则过程中往回滑动
//是否反转了方向(例:本来向下滑选择,往回滑动越过了起点变成了向上滑选择)
BOOL isReverse = (idx2 > idx && idx > previousEndIdx) || (idx2 < idx && idx < previousEndIdx);
for (NSInteger i = previousEndIdx; (preBigger && i > MAX(idx, idx2)) || (preSmaller && i < MIN(idx, idx2)); preBigger ? i-- : i++) {
NSIndexPath *idxp = [NSIndexPath indexPathForItem:i inSection:0];
TZAssetCell *cell = (TZAssetCell *)[self.collView cellForItemAtIndexPath:idxp];
[self selectCell:cell isBack:YES];
}
previousEndIdx = idx2;
if (!isReverse) {
return;//之前的已经选中/取消选中,不再走下面的逻辑
}
}
}
BOOL idx2Bigger = idx2 > idx;
for (NSInteger i = idx; (idx2Bigger && i <= idx2) || (!idx2Bigger && i >= idx2); idx2Bigger ? i++ : i--) {
NSIndexPath *idxp = [NSIndexPath indexPathForItem:i inSection:0];
TZAssetCell *cell = (TZAssetCell *)[self.collView cellForItemAtIndexPath:idxp];
if (isCancel == -1 && i == idx) {//如果第一个选择的是已选中状态,则全体都是取消选中,反之为选中
isCancel = cell.selectPhotoButton.selected;
}
[self selectCell:cell isBack:NO];
}
previousEndIdx = idx2;
NSLog(@"%@选择至%ld", isCancel ? @"取消" : @"", (long)idx2);
}

- (void)selectCell:(TZAssetCell *)cell isBack:(BOOL)isBack {
BOOL select = isBack ? !isCancel : isCancel;
SEL selectSel = NSSelectorFromString(@"selectPhotoButtonClick:");
if ([cell respondsToSelector:selectSel] && cell.selectPhotoButton.selected == select) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[cell performSelector:selectSel withObject:cell.selectPhotoButton];
#pragma clang diagnostic pop
}
}

@end