This commit is contained in:
2025-11-10 20:40:11 +08:00
parent a007a77db9
commit dc0c55c495
10 changed files with 1419 additions and 0 deletions

View File

@@ -79,6 +79,8 @@
049FB20B2EC1C13800FAB05D /* KBSkinBottomActionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 049FB20A2EC1C13800FAB05D /* KBSkinBottomActionView.m */; };
049FB20E2EC1CD2800FAB05D /* KBAlert.m in Sources */ = {isa = PBXBuildFile; fileRef = 049FB20D2EC1CD2800FAB05D /* KBAlert.m */; };
049FB2112EC1F72F00FAB05D /* KBMyListCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 049FB2102EC1F72F00FAB05D /* KBMyListCell.m */; };
049FB2172EC20A6600FAB05D /* BMLongPressDragCellCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 049FB2132EC20A6600FAB05D /* BMLongPressDragCellCollectionView.m */; };
049FB21A2EC20A9E00FAB05D /* KBMyKeyBoardVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 049FB2192EC20A9E00FAB05D /* KBMyKeyBoardVC.m */; };
04A9FE0F2EB481100020DB6D /* KBHUD.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC97082EB31B14007BD342 /* KBHUD.m */; };
04A9FE132EB4D0D20020DB6D /* KBFullAccessManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 04A9FE112EB4D0D20020DB6D /* KBFullAccessManager.m */; };
04A9FE162EB873C80020DB6D /* UIViewController+Extension.m in Sources */ = {isa = PBXBuildFile; fileRef = 04A9FE152EB873C80020DB6D /* UIViewController+Extension.m */; };
@@ -133,6 +135,7 @@
A1B2E1012EBC7AAA00000001 /* KBTopThreeView.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2E0022EBC7AAA00000001 /* KBTopThreeView.m */; };
A1B2E1022EBC7AAA00000001 /* HomeHotCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2E0042EBC7AAA00000001 /* HomeHotCell.m */; };
ECC9EE02174D86E8D792472F /* Pods_keyBoard.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 967065BB5230E43F293B3AF9 /* Pods_keyBoard.framework */; };
049FB31D2EC21BCD00FAB05D /* KBMyKeyboardCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 049FB31C2EC21BCD00FAB05D /* KBMyKeyboardCell.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -275,6 +278,14 @@
049FB20D2EC1CD2800FAB05D /* KBAlert.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBAlert.m; sourceTree = "<group>"; };
049FB20F2EC1F72F00FAB05D /* KBMyListCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBMyListCell.h; sourceTree = "<group>"; };
049FB2102EC1F72F00FAB05D /* KBMyListCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBMyListCell.m; sourceTree = "<group>"; };
049FB2122EC20A6600FAB05D /* BMLongPressDragCellCollectionView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BMLongPressDragCellCollectionView.h; sourceTree = "<group>"; };
049FB2132EC20A6600FAB05D /* BMLongPressDragCellCollectionView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BMLongPressDragCellCollectionView.m; sourceTree = "<group>"; };
049FB2142EC20A6600FAB05D /* BMLongPressDragCellCollectionViewDataSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BMLongPressDragCellCollectionViewDataSource.h; sourceTree = "<group>"; };
049FB2152EC20A6600FAB05D /* BMLongPressDragCellCollectionViewDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BMLongPressDragCellCollectionViewDelegate.h; sourceTree = "<group>"; };
049FB2182EC20A9E00FAB05D /* KBMyKeyBoardVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBMyKeyBoardVC.h; sourceTree = "<group>"; };
049FB2192EC20A9E00FAB05D /* KBMyKeyBoardVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBMyKeyBoardVC.m; sourceTree = "<group>"; };
049FB31B2EC21BCD00FAB05D /* KBMyKeyboardCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBMyKeyboardCell.h; sourceTree = "<group>"; };
049FB31C2EC21BCD00FAB05D /* KBMyKeyboardCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBMyKeyboardCell.m; sourceTree = "<group>"; };
04A9A67D2EB9E1690023B8F4 /* KBResponderUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBResponderUtils.h; sourceTree = "<group>"; };
04A9FE102EB4D0D20020DB6D /* KBFullAccessManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBFullAccessManager.h; sourceTree = "<group>"; };
04A9FE112EB4D0D20020DB6D /* KBFullAccessManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBFullAccessManager.m; sourceTree = "<group>"; };
@@ -619,6 +630,7 @@
048908D32EBF618E00FABA60 /* Vender */ = {
isa = PBXGroup;
children = (
049FB2162EC20A6600FAB05D /* BMLongPressDragCellCollectionView */,
048908D92EBF61AF00FABA60 /* UICollectionViewLeftAlignedLayout */,
);
path = Vender;
@@ -633,6 +645,17 @@
path = UICollectionViewLeftAlignedLayout;
sourceTree = "<group>";
};
049FB2162EC20A6600FAB05D /* BMLongPressDragCellCollectionView */ = {
isa = PBXGroup;
children = (
049FB2122EC20A6600FAB05D /* BMLongPressDragCellCollectionView.h */,
049FB2132EC20A6600FAB05D /* BMLongPressDragCellCollectionView.m */,
049FB2142EC20A6600FAB05D /* BMLongPressDragCellCollectionViewDataSource.h */,
049FB2152EC20A6600FAB05D /* BMLongPressDragCellCollectionViewDelegate.h */,
);
path = BMLongPressDragCellCollectionView;
sourceTree = "<group>";
};
04A9FE122EB4D0D20020DB6D /* Manager */ = {
isa = PBXGroup;
children = (
@@ -831,6 +854,8 @@
04FC95BC2EB1E3B1007BD342 /* V */ = {
isa = PBXGroup;
children = (
049FB31B2EC21BCD00FAB05D /* KBMyKeyboardCell.h */,
049FB31C2EC21BCD00FAB05D /* KBMyKeyboardCell.m */,
048908E12EBF760000FABA60 /* MySkinCell.h */,
048908E22EBF760000FABA60 /* MySkinCell.m */,
048908E42EBF841B00FABA60 /* KBSkinDetailTagCell.h */,
@@ -856,6 +881,8 @@
children = (
04FC95D02EB1E7AE007BD342 /* MyVC.h */,
04FC95D12EB1E7AE007BD342 /* MyVC.m */,
049FB2182EC20A9E00FAB05D /* KBMyKeyBoardVC.h */,
049FB2192EC20A9E00FAB05D /* KBMyKeyBoardVC.m */,
048908DE2EBF73DC00FABA60 /* MySkinVC.h */,
048908DF2EBF73DC00FABA60 /* MySkinVC.m */,
048908E12EBF821700FABA60 /* KBSkinDetailVC.h */,
@@ -1355,6 +1382,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
049FB31D2EC21BCD00FAB05D /* KBMyKeyboardCell.m in Sources */,
048909F62EC0AAAA00FABA60 /* KBCategoryTitleCell.m in Sources */,
048909F72EC0AAAA00FABA60 /* KBCategoryTitleView.m in Sources */,
04890A042EC0BBBB00FABA60 /* KBCategoryTitleImageCell.m in Sources */,
@@ -1377,6 +1405,7 @@
048908EF2EBF861800FABA60 /* KBSkinSectionTitleCell.m in Sources */,
048908E32EBF821700FABA60 /* KBSkinDetailVC.m in Sources */,
0477BDF32EBB7B850055D639 /* KBDirectionIndicatorView.m in Sources */,
049FB21A2EC20A9E00FAB05D /* KBMyKeyBoardVC.m in Sources */,
048908D22EBF611D00FABA60 /* KBHistoryMoreCell.m in Sources */,
04FC95D82EB1EA16007BD342 /* BaseCell.m in Sources */,
0477BDF72EBC63A80055D639 /* KBTestVC.m in Sources */,
@@ -1392,6 +1421,7 @@
048908E02EBF73DC00FABA60 /* MySkinVC.m in Sources */,
048908F22EC047FD00FABA60 /* KBShopHeadView.m in Sources */,
048908EC2EBF849300FABA60 /* KBSkinTagsContainerCell.m in Sources */,
049FB2172EC20A6600FAB05D /* BMLongPressDragCellCollectionView.m in Sources */,
0477BDF02EBB76E30055D639 /* HomeSheetVC.m in Sources */,
048908E62EBF841B00FABA60 /* KBSkinDetailTagCell.m in Sources */,
04FC97002EB30A00007BD342 /* KBGuideTopCell.m in Sources */,

View File

@@ -0,0 +1,24 @@
//
// KBMyKeyboardCell.h
// keyBoard
//
// 自定义键盘截图页的标签 Cell带表情、文案、右上角删除圆点
// 使用 Masonry 约束,支持根据文案动态计算宽度。
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface KBMyKeyboardCell : UICollectionViewCell
/// 配置展示内容
- (void)configEmoji:(NSString *)emoji title:(NSString *)title;
/// 根据文案计算自适应尺寸(高度固定,宽度随文本变化)
+ (CGSize)sizeForEmoji:(NSString *)emoji title:(NSString *)title;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,161 @@
//
// KBMyKeyboardCell.m
// keyBoard
//
// cell
// - Emoji
// -
// - /
// - +sizeForEmoji:title: 便 item
//
#import "KBMyKeyboardCell.h"
@interface KBMyKeyboardCell ()
@property (nonatomic, strong) UIView *coverView; //
@property (nonatomic, strong) UILabel *emojiLabel; //
@property (nonatomic, strong) UILabel *titleLabel; //
@property (nonatomic, strong) UIView *minusBadge; //
@property (nonatomic, strong) UILabel *minusLabel; //
@end
@implementation KBMyKeyboardCell
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.contentView.backgroundColor = [UIColor clearColor];
//
self.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.08].CGColor;
self.layer.shadowOpacity = 1.0;
self.layer.shadowOffset = CGSizeMake(0, 2);
self.layer.shadowRadius = 6;
[self.contentView addSubview:self.coverView];
[self.coverView addSubview:self.emojiLabel];
[self.coverView addSubview:self.titleLabel];
[self.contentView addSubview:self.minusBadge];
[self.minusBadge addSubview:self.minusLabel];
[self.coverView mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.left.equalTo(self.contentView).offset(0);
make.height.mas_equalTo(36);
}];
// Masonry 12
[self.emojiLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.coverView).offset(12);
make.centerY.equalTo(self.coverView);
}];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.emojiLabel.mas_right).offset(8);
make.centerY.equalTo(self.coverView);
make.right.lessThanOrEqualTo(self.coverView).offset(-12);
}];
// hidden
[self.minusBadge mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.height.mas_equalTo(11);
make.top.equalTo(self.contentView).offset(0);
make.right.equalTo(self.contentView).offset(0);
}];
[self.minusLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self.minusBadge);
}];
}
return self;
}
- (void)prepareForReuse {
[super prepareForReuse];
self.emojiLabel.text = @"";
self.titleLabel.text = @"";
}
- (void)configEmoji:(NSString *)emoji title:(NSString *)title {
// UI
self.emojiLabel.text = emoji ?: @"";
self.titleLabel.text = title ?: @"";
}
+ (CGSize)sizeForEmoji:(NSString *)emoji title:(NSString *)title {
// 44 = (12+12) + Emoji ( 20 ) + (8) +
UIFont *emojiFont = [UIFont systemFontOfSize:20];
UIFont *titleFont = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold];
CGFloat emojiW = 0;
if (emoji.length > 0) {
CGSize s = [emoji sizeWithAttributes:@{NSFontAttributeName:emojiFont}];
emojiW = ceil(s.width);
} else {
emojiW = 0; //
}
CGFloat textW = 0;
if (title.length > 0) {
CGSize s = [title sizeWithAttributes:@{NSFontAttributeName:titleFont}];
textW = ceil(s.width);
}
CGFloat width = 12 + emojiW + (emojiW > 0 ? 8 : 0) + textW + 12;
CGFloat minW = 84; //
CGFloat maxW = UIScreen.mainScreen.bounds.size.width - 16 * 2; //
width = MAX(minW, MIN(width, maxW));
return CGSizeMake(width, 41.0);
}
#pragma mark - Lazy
- (UILabel *)emojiLabel {
if (!_emojiLabel) {
_emojiLabel = [UILabel new];
_emojiLabel.font = [UIFont systemFontOfSize:20];
_emojiLabel.text = @"😀"; //
}
return _emojiLabel;
}
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [UILabel new];
_titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold];
_titleLabel.textColor = [UIColor colorWithHex:0x1B1F1A];
}
return _titleLabel;
}
- (UIView *)minusBadge {
if (!_minusBadge) {
_minusBadge = [UIView new];
_minusBadge.backgroundColor = [UIColor colorWithHex:KBColorValue];
_minusBadge.layer.cornerRadius = 5.5;
_minusBadge.layer.masksToBounds = YES;
}
return _minusBadge;
}
- (UILabel *)minusLabel {
if (!_minusLabel) {
_minusLabel = [UILabel new];
_minusLabel.text = @"-";
_minusLabel.textColor = [UIColor whiteColor];
_minusLabel.font = [UIFont systemFontOfSize:12 weight:UIFontWeightBold];
_minusLabel.textAlignment = NSTextAlignmentCenter;
}
return _minusLabel;
}
- (UIView *)coverView{
if (!_coverView) {
_coverView = [[UIView alloc] init];
_coverView.backgroundColor = [UIColor whiteColor];
_coverView.layer.cornerRadius = 4;
_coverView.layer.masksToBounds = true;
}
return _coverView;
}
@end

View File

@@ -0,0 +1,16 @@
//
// KBMyKeyBoardVC.h
// keyBoard
//
// Created by Mac on 2025/11/10.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface KBMyKeyBoardVC : UIViewController
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,240 @@
//
// KBMyKeyBoardVC.m
// keyBoard
//
// Created by Mac on 2025/11/10.
//
#import "KBMyKeyBoardVC.h"
#import "BMLongPressDragCellCollectionView.h"
#import "UICollectionViewLeftAlignedLayout.h"
#import "KBMyKeyboardCell.h"
///
static NSString * const kKBMyKeyboardCellId = @"kKBMyKeyboardCellId";
/// -
///
/// 1使 BMLongPressDragCellCollectionView
/// 2cell
/// 3使 Masonry
@interface KBMyKeyBoardVC () <BMLongPressDragCellCollectionViewDelegate, BMLongPressDragCellCollectionViewDataSource>
// UI
@property (nonatomic, strong) UIView *sheetView; //
@property (nonatomic, strong) BMLongPressDragCellCollectionView *collectionView; //
@property (nonatomic, strong) UIButton *saveButton; //
//
@property (nonatomic, strong) NSMutableArray<NSMutableArray<NSDictionary *> *> *dataSourceArray; // {emoji,title}
@end
@interface KBMyKeyBoardVC ()
@end
@implementation KBMyKeyBoardVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor colorWithHex:0xF6F8F9];
self.navigationController.title = @"My KeyBoard";
//
[self.view addSubview:self.sheetView];
[self.sheetView addSubview:self.collectionView];
[self.view addSubview:self.saveButton];
[self.sheetView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.view);
make.top.equalTo(self.view).offset(KB_NAV_TOTAL_HEIGHT + 60);
make.bottom.equalTo(self.view).offset(16);
}];
[self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.top.equalTo(self.sheetView);
make.bottom.equalTo(self.saveButton.mas_top).offset(-15);
}];
[self.saveButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.view).insets(UIEdgeInsetsMake(0, 24, 0, 24));
make.bottom.equalTo(self.view.mas_safeAreaLayoutGuideBottom).offset(-12);
make.height.mas_equalTo(50);
}];
//
[self buildDefaultData];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
//
// [self.navigationController setNavigationBarHidden:YES animated:animated];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// if (self.isMovingFromParentViewController || self.isBeingDismissed) {
// [self.navigationController setNavigationBarHidden:NO animated:animated];
// }
}
#pragma mark - Data
/// 1 section
- (void)buildDefaultData {
NSArray *arr = @[
@{@"emoji":@"😊", @"title":@"Humor"},
@{@"emoji":@"😄", @"title":@"Jokes"},
@{@"emoji":@"🥰", @"title":@"Love"},
@{@"emoji":@"🤔", @"title":@"Thinking"},
@{@"emoji":@"🔥", @"title":@"Hot"},
@{@"emoji":@"🎉", @"title":@"Celebrate"},
@{@"emoji":@"🧠", @"title":@"Brainstorm"},
@{@"emoji":@"🐱", @"title":@"Cats"},
@{@"emoji":@"😂", @"title":@"LOL"},
@{@"emoji":@"📸", @"title":@"Photography"},
@{@"emoji":@"🌟", @"title":@"Star"},
@{@"emoji":@"🍀", @"title":@"Lucky"},
@{@"emoji":@"📚", @"title":@"Knowledge"},
@{@"emoji":@"🎵", @"title":@"Music"},
@{@"emoji":@"🚀", @"title":@"Launch"},
@{@"emoji":@"😊", @"title":@"Humor"},
@{@"emoji":@"😄", @"title":@"Jokes"},
@{@"emoji":@"🥰", @"title":@"Love"},
@{@"emoji":@"🤔", @"title":@"Thinking"},
@{@"emoji":@"🔥", @"title":@"Hot"},
@{@"emoji":@"🎉", @"title":@"Celebrate"},
@{@"emoji":@"🧠", @"title":@"Brainstorm"},
@{@"emoji":@"🐱", @"title":@"Cats"},
@{@"emoji":@"😂", @"title":@"LOL"},
@{@"emoji":@"📸", @"title":@"Photography"},
@{@"emoji":@"🌟", @"title":@"Star"},
@{@"emoji":@"🍀", @"title":@"Lucky"},
@{@"emoji":@"📚", @"title":@"Knowledge"},
@{@"emoji":@"🎵", @"title":@"Music"},
@{@"emoji":@"🚀", @"title":@"Launch"},
@{@"emoji":@"😊", @"title":@"Humor"},
@{@"emoji":@"😄", @"title":@"Jokes"},
@{@"emoji":@"🥰", @"title":@"Love"},
@{@"emoji":@"🤔", @"title":@"Thinking"},
@{@"emoji":@"🔥", @"title":@"Hot"},
@{@"emoji":@"🎉", @"title":@"Celebrate"},
@{@"emoji":@"🧠", @"title":@"Brainstorm"},
@{@"emoji":@"🐱", @"title":@"Cats"},
@{@"emoji":@"😂", @"title":@"LOL"},
@{@"emoji":@"📸", @"title":@"Photography"},
@{@"emoji":@"🌟", @"title":@"Star"},
@{@"emoji":@"🍀", @"title":@"Lucky"},
@{@"emoji":@"📚", @"title":@"Knowledge"},
@{@"emoji":@"🎵", @"title":@"Music"},
@{@"emoji":@"🚀", @"title":@"Launch"},
];
self.dataSourceArray = [@[[arr mutableCopy]] mutableCopy];
[self.collectionView reloadData];
}
#pragma mark - BMLongPressDragCellCollectionViewDataSource
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { return self.dataSourceArray.count; }
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.dataSourceArray[section].count;
}
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
KBMyKeyboardCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kKBMyKeyboardCellId forIndexPath:indexPath];
NSDictionary *d = self.dataSourceArray[indexPath.section][indexPath.item];
[cell configEmoji:d[@"emoji"] title:d[@"title"]];
return cell;
}
//
- (NSArray<NSArray<id> *> *)dataSourceWithDragCellCollectionView:(__kindof BMLongPressDragCellCollectionView *)dragCellCollectionView {
return self.dataSourceArray;
}
//
- (void)dragCellCollectionView:(BMLongPressDragCellCollectionView *)dragCellCollectionView newDataArrayAfterMove:(nullable NSArray<NSArray<id> *> *)newDataArray {
self.dataSourceArray = [newDataArray mutableCopy];
}
#pragma mark - BMLongPressDragCellCollectionViewDelegate ()
// item
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *d = self.dataSourceArray[indexPath.section][indexPath.item];
return [KBMyKeyboardCell sizeForEmoji:d[@"emoji"] title:d[@"title"]];
}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
return UIEdgeInsetsMake(12, 12, 12, 12);
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section { return 10; }
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section { return 10; }
#pragma mark - Actions
- (void)onSave {
//
NSLog(@"保存顺序: %@", self.dataSourceArray);
[KBHUD showInfo:@"已保存"];
}
#pragma mark - Lazy UI
//- (UILabel *)titleLabel {
// if (!_titleLabel) {
// _titleLabel = [UILabel new];
// _titleLabel.text = @"My Keyboard"; //
// _titleLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightSemibold];
// _titleLabel.textColor = [UIColor colorWithHex:0x1B1F1A];
// }
// return _titleLabel;
//}
- (UIView *)sheetView {
if (!_sheetView) {
_sheetView = [UIView new];
_sheetView.backgroundColor = [UIColor whiteColor];
_sheetView.layer.cornerRadius = 32.0;
_sheetView.layer.masksToBounds = YES;
}
return _sheetView;
}
- (BMLongPressDragCellCollectionView *)collectionView {
if (!_collectionView) {
UICollectionViewLeftAlignedLayout *layout = [UICollectionViewLeftAlignedLayout new];
layout.scrollDirection = UICollectionViewScrollDirectionVertical;
// layout.sectionInset = UIEdgeInsetsMake(16, 16, 16, 16);
_collectionView = [[BMLongPressDragCellCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
_collectionView.backgroundColor = [UIColor clearColor];
_collectionView.delegate = self; // BMLongPressDragCellCollectionViewDelegate
_collectionView.dataSource = self; // BMLongPressDragCellCollectionViewDataSource
_collectionView.alwaysBounceVertical = YES;
_collectionView.showsVerticalScrollIndicator = NO;
[_collectionView registerClass:KBMyKeyboardCell.class forCellWithReuseIdentifier:kKBMyKeyboardCellId];
}
return _collectionView;
}
- (UIButton *)saveButton {
if (!_saveButton) {
_saveButton = [UIButton buttonWithType:UIButtonTypeSystem];
[_saveButton setTitle:@"Save" forState:UIControlStateNormal];
_saveButton.titleLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightSemibold];
[_saveButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
_saveButton.backgroundColor = [UIColor colorWithHex:KBColorValue];
_saveButton.layer.cornerRadius = 25;
_saveButton.layer.masksToBounds = YES;
[_saveButton addTarget:self action:@selector(onSave) forControlEvents:UIControlEventTouchUpInside];
}
return _saveButton;
}
@end

View File

@@ -10,6 +10,7 @@
#import "KBSkinDetailVC.h"
#import "KBMyHeaderView.h" //
#import "KBMyListCell.h"
#import "KBMyKeyBoardVC.h"
@interface MyVC () <UITableViewDelegate, UITableViewDataSource>
@property (nonatomic, strong) BaseTableView *tableView; //
@@ -100,6 +101,8 @@
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
KBMyKeyBoardVC *vc = [[KBMyKeyBoardVC alloc] init];
[self.navigationController pushViewController:vc animated:true];
}
#pragma mark - Lazy

View File

@@ -0,0 +1,71 @@
// MIT License
//
// Copyright (c) 2019 https://liangdahong.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
////////////////////
/// v3.1.4
////////////////////
#import <UIKit/UIKit.h>
#import "BMLongPressDragCellCollectionViewDelegate.h"
#import "BMLongPressDragCellCollectionViewDataSource.h"
/*
- (NSArray<NSArray<id> *> *)dataSourceWithDragCellCollectionView:(__kindof BMLongPressDragCellCollectionView *)dragCellCollectionView {
return self.dataSourceArray;
// 此协议方法里返回数据源
}
- (void)dragCellCollectionView:(__kindof BMLongPressDragCellCollectionView *)dragCellCollectionView newDataArrayAfterMove:(nullable NSArray <NSArray <id> *> *)newDataArray {
self.dataSourceArray = [newDataArray mutableCopy];
// 此协议方法里保存数据源
}
*/
@interface BMLongPressDragCellCollectionView : UICollectionView
@property (nonatomic, weak) id<BMLongPressDragCellCollectionViewDelegate> delegate;
@property (nonatomic, weak) id<BMLongPressDragCellCollectionViewDataSource> dataSource;
/// 长按触发拖拽所需时间,默认是 0.5 秒。
@property (nonatomic, assign) NSTimeInterval minimumPressDuration;
/// 是否可以拖拽 默认为 YES。
@property (nonatomic, assign, getter=isCanDrag) IBInspectable BOOL canDrag;
/// 长按拖拽时拖拽中的的 Cell 缩放比例,默认是 1.2 倍。
@property (nonatomic, assign) IBInspectable CGFloat dragZoomScale;
/// 拖拽的 Cell 在拖拽移动时的透明度 默认是: 1.0 不透明。
@property (nonatomic, assign) IBInspectable CGFloat dragCellAlpha;
/// 拖拽到 UICollectionView 边缘时 UICollectionView 的滚动速度
/// 默认为: 8.0f ,建议设置 5.0f 到 15.0f 之间。
@property (nonatomic, assign) IBInspectable CGFloat dragSpeed;
/// 拖拽 View 的背景颜色,默认和被拖拽的 Cell 一样。
@property (nonatomic, strong) IBInspectable UIColor *dragSnapedViewBackgroundColor;
/// 移动到指定位置
/// @param indexPath 移动到的位置
/// 内部只会处理当前正在拖拽的情况,会把拖拽的 Cell 移动到指定位置,建议在停止手势时或者认为适当的时候使用。
- (void)dragMoveItemToIndexPath:(NSIndexPath *)indexPath;
@end

View File

@@ -0,0 +1,729 @@
// MIT License
//
// Copyright (c) 2019 https://liangdahong.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "BMLongPressDragCellCollectionView.h"
typedef NS_ENUM(NSUInteger, BMLongPressDragCellCollectionViewScrollDirection) {
BMLongPressDragCellCollectionViewScrollDirectionNone = 0,
BMLongPressDragCellCollectionViewScrollDirectionLeft = 1,
BMLongPressDragCellCollectionViewScrollDirectionRight = 2,
BMLongPressDragCellCollectionViewScrollDirectionUp = 3,
BMLongPressDragCellCollectionViewScrollDirectionDown = 4,
};
@interface BMLongPressDragCellCollectionView ()
///
@property (nonatomic, strong, nullable) UILongPressGestureRecognizer *longGesture;
///
@property (nonatomic, strong, nullable) UIView *snapedView;
///
@property (nonatomic, strong, nullable) CADisplayLink *edgeTimer;
/// IndexPath
@property (nonatomic, strong, nullable) NSIndexPath *oldIndexPath;
///
@property (nonatomic, strong, nullable) NSIndexPath *currentIndexPath;
@property (nonatomic, assign) BOOL isEndDrag; ///<
@property (nonatomic, assign) BOOL banReload; ///<
@property (nonatomic, assign) CGPoint oldPoint; ///<
@property (nonatomic, assign) CGPoint lastPoint; ///<
@end
@implementation BMLongPressDragCellCollectionView
@dynamic delegate, dataSource;
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout {
if (self = [super initWithFrame:frame collectionViewLayout:layout]) {
[self initConfiguration];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self initConfiguration];
}
return self;
}
- (void)dealloc {
[self _stopEdgeTimer];
}
#pragma mark - getters setters
- (void)setCanDrag:(BOOL)canDrag {
_canDrag = canDrag;
self.longGesture.enabled = _canDrag;
}
- (void)setMinimumPressDuration:(NSTimeInterval)minimumPressDuration {
_minimumPressDuration = minimumPressDuration;
self.longGesture.minimumPressDuration = minimumPressDuration;
}
- (void)setDragZoomScale:(CGFloat)dragZoomScale {
if (dragZoomScale < 0.1) {
dragZoomScale = 1.2;
}
_dragZoomScale = dragZoomScale;
}
- (void)setDragSpeed:(CGFloat)dragSpeed {
if (dragSpeed < 0.5) {
dragSpeed = 8.0;
}
_dragSpeed = dragSpeed;
}
- (void)setDragSnapedViewBackgroundColor:(UIColor *)dragSnapedViewBackgroundColor {
_dragSnapedViewBackgroundColor = dragSnapedViewBackgroundColor;
_snapedView.backgroundColor = dragSnapedViewBackgroundColor;
}
- (UILongPressGestureRecognizer *)longGesture {
if (!_longGesture) {
_longGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handlelongGesture:)];
_longGesture.minimumPressDuration = _minimumPressDuration;
}
return _longGesture;
}
// iOS 10 UICollectionView使bug,
// https://developer.apple.com/documentation/uikit/uicollectionview/1771771-prefetchingenabled
// https://sxgfxm.github.io/blog/2016/10/18/uicollectionview-ios10-new-features
- (void)setPrefetchingEnabled:(BOOL)prefetchingEnabled {
[super setPrefetchingEnabled:NO];
}
#pragma mark -
- (void)initConfiguration {
_canDrag = YES;
_minimumPressDuration = 0.5;
_dragZoomScale = 1.2;
_dragCellAlpha = 1.0;
_dragSpeed = 8.0;
[self addGestureRecognizer:self.longGesture];
// iOS 10 UICollectionView 使bug
// https://developer.apple.com/documentation/uikit/uicollectionview/1771771-prefetchingenabled
// https://sxgfxm.github.io/blog/2016/10/18/uicollectionview-ios10-new-features
if (@available(iOS 10.0, *)) {
self.prefetchingEnabled = NO;
}
}
- (void)reloadData {
if (_banReload) {
return;
}
[super reloadData];
}
- (void)insertSections:(NSIndexSet *)sections {
if (_banReload) {
return;
}
[super insertSections:sections];
}
- (void)deleteSections:(NSIndexSet *)sections {
if (_banReload) {
return;
}
[super deleteSections:sections];
}
- (void)reloadSections:(NSIndexSet *)sections {
if (_banReload) {
return;
}
[super reloadSections:sections];
}
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection {
if (_banReload) {
return;
}
[super moveSection:section toSection:newSection];
}
- (void)insertItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths {
if (_banReload) {
return;
}
[super insertItemsAtIndexPaths:indexPaths];
}
- (void)deleteItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths {
if (_banReload) {
return;
}
[super deleteItemsAtIndexPaths:indexPaths];
}
- (void)reloadItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths {
if (_banReload) {
return;
}
[super reloadItemsAtIndexPaths:indexPaths];
}
- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath {
if (_banReload) {
return;
}
[super moveItemAtIndexPath:indexPath toIndexPath:newIndexPath];
}
- (__kindof UICollectionViewCell *)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [super dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
//
if (_isEndDrag) {
cell.hidden = NO;
} else {
cell.hidden = (self.oldIndexPath
&& self.oldIndexPath.item == indexPath.item
&& self.oldIndexPath.section == indexPath.section);
}
return cell;
}
- (nullable NSIndexPath *)_getChangedNullIndexPath {
__block NSIndexPath *index = nil;
CGPoint point = [self.longGesture locationInView:self];
NSInteger number = self.numberOfSections;
while (number--) {
NSInteger row = [self.dataSource collectionView:self numberOfItemsInSection:number];
if (row == 0) {
//
CGRect headerFrame = [self layoutAttributesForSupplementaryElementOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:number]].frame;
CGRect footerFrame = [self layoutAttributesForSupplementaryElementOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:number]].frame;
CGRect frame = CGRectZero;
if (((UICollectionViewFlowLayout *)self.collectionViewLayout).scrollDirection == UICollectionViewScrollDirectionHorizontal) {
//
frame = CGRectMake(CGRectGetMinX(headerFrame),
CGRectGetMinY(headerFrame),
CGRectGetMaxX(footerFrame) - CGRectGetMidX(headerFrame),
CGRectGetWidth(footerFrame));
if (frame.size.width < 10.0) {
// 10.0 10.0
frame.size.width = 10.0;
frame.size.height = CGRectGetHeight(self.frame);
frame.origin.x -= 5.0;
}
} else {
//
frame = CGRectMake(CGRectGetMinX(headerFrame),
CGRectGetMinY(headerFrame),
CGRectGetWidth(footerFrame),
CGRectGetMaxY(footerFrame) - CGRectGetMidY(headerFrame));
if (frame.size.height < 10.0) {
// 10.0 10.0
frame.size.height = 10.0;
frame.size.width = CGRectGetWidth(self.frame);
frame.origin.y -= 5.0;
}
}
if (CGRectContainsPoint(frame, point)) {
//
return [NSIndexPath indexPathForItem:0 inSection:number];
}
}
}
return index;
}
- (nullable NSIndexPath *)_getChangedIndexPath {
__block NSIndexPath *index = nil;
CGPoint point = [self.longGesture locationInView:self];
// Cell Cell
[[self visibleCells] enumerateObjectsUsingBlock:^(__kindof UICollectionViewCell * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (CGRectContainsPoint(obj.frame, point)) {
index = [self indexPathForCell:obj];
*stop = YES;
}
}];
// Cell index
if (index) {
if ((index.section == self.oldIndexPath.section) && (index.item == self.oldIndexPath.item)) {
return nil;
}
return index;
}
// Cell
__block CGFloat width = MAXFLOAT;
__weak typeof(self) weakSelf = self;
[[self visibleCells] enumerateObjectsUsingBlock:^(__kindof UICollectionViewCell * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (CGRectContainsPoint(obj.frame, point)) {
index = [self indexPathForCell:obj];
*stop = YES;
}
__strong typeof(weakSelf) self = weakSelf;
CGPoint p1 = self.snapedView.center;
CGPoint p2 = obj.center;
//
CGFloat distance = sqrt(pow((p1.x - p2.x), 2) + pow((p1.y - p2.y), 2));
if (distance < width) {
width = distance;
index = [self indexPathForCell:obj];
}
}];
if (!index) {
return nil;
}
if ((index.section == self.oldIndexPath.section) && (index.item == self.oldIndexPath.item)) {
// Cell , return nil
return nil;
}
return index;
}
//
- (BMLongPressDragCellCollectionViewScrollDirection)_setScrollDirection {
if (((self.bounds.size.height + self.contentOffset.y - _snapedView.center.y) < (_snapedView.bounds.size.height / 2))
&& ((self.bounds.size.height + self.contentOffset.y) < self.contentSize.height)) {
return BMLongPressDragCellCollectionViewScrollDirectionDown;
} else if (((_snapedView.center.y - self.contentOffset.y) < (_snapedView.bounds.size.height / 2))
&& self.contentOffset.y > 0) {
return BMLongPressDragCellCollectionViewScrollDirectionUp;
} else if (((self.bounds.size.width + self.contentOffset.x) - (_snapedView.center.x)) < (_snapedView.bounds.size.width / 2)
&& (self.bounds.size.width + self.contentOffset.x) < self.contentSize.width) {
return BMLongPressDragCellCollectionViewScrollDirectionRight;
} else if (((_snapedView.center.x - self.contentOffset.x) < (_snapedView.bounds.size.width) / 2)
&& self.contentOffset.x > 0) {
return BMLongPressDragCellCollectionViewScrollDirectionLeft;
}
return BMLongPressDragCellCollectionViewScrollDirectionNone;
}
// UICollectionView
- (void)_updateSourceData {
//
NSMutableArray *array = [self.dataSource dataSourceWithDragCellCollectionView:self].mutableCopy;
//
BOOL dataTypeCheck = ([self numberOfSections] != 1 || ([self numberOfSections] == 1 && [array[0] isKindOfClass:NSArray.class]));
if (dataTypeCheck) {
for (int i = 0; i < array.count; i ++) {
// NSMutableArray
[array replaceObjectAtIndex:i withObject:[array[i] mutableCopy]];
}
}
if (_currentIndexPath.section == _oldIndexPath.section) {
NSMutableArray *orignalSection = dataTypeCheck ? array[_oldIndexPath.section] : array;
if (_currentIndexPath.item > _oldIndexPath.item) {
for (NSUInteger i = _oldIndexPath.item; i < _currentIndexPath.item ; i ++) {
[orignalSection exchangeObjectAtIndex:i withObjectAtIndex:i + 1];
}
} else {
for (NSUInteger i = _oldIndexPath.item; i > _currentIndexPath.item ; i --) {
[orignalSection exchangeObjectAtIndex:i withObjectAtIndex:i - 1];
}
}
} else {
NSMutableArray *orignalSection = array[_oldIndexPath.section];
NSMutableArray *currentSection = array[_currentIndexPath.section];
[currentSection insertObject:orignalSection[_oldIndexPath.item] atIndex:_currentIndexPath.item];
// https://github.com/liangdahong/BMLongPressDragCellCollectionView/issues/16
// [ , OC ]
// [orignalSection removeObject:orignalSection[_oldIndexPath.item]];
[orignalSection removeObjectAtIndex:_oldIndexPath.item];
}
// ==========
//
// 使使 reloadData
// https://github.com/liangdahong/BMLongPressDragCellCollectionView/issues/14
self.banReload = YES;
[self.delegate dragCellCollectionView:self newDataArrayAfterMove:array];
self.banReload = NO;
}
- (void)_setEdgeTimer{
if (!_edgeTimer) {
_edgeTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(_edgeScroll)];
[_edgeTimer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
}
- (void)_stopEdgeTimer{
if (_edgeTimer) {
[_edgeTimer invalidate];
_edgeTimer = nil;
}
}
- (void)_edgeScroll {
BMLongPressDragCellCollectionViewScrollDirection scrollDirection = [self _setScrollDirection];
if (BMLongPressDragCellCollectionViewScrollDirectionNone == scrollDirection) {
return;
}
switch (scrollDirection) {
case BMLongPressDragCellCollectionViewScrollDirectionLeft:{
// NO
[self setContentOffset:CGPointMake(self.contentOffset.x - _dragSpeed, self.contentOffset.y) animated:NO];
_snapedView.center = CGPointMake(_snapedView.center.x - _dragSpeed, _snapedView.center.y);
_lastPoint.x -= _dragSpeed;
}
break;
case BMLongPressDragCellCollectionViewScrollDirectionRight:{
[self setContentOffset:CGPointMake(self.contentOffset.x + _dragSpeed, self.contentOffset.y) animated:NO];
_snapedView.center = CGPointMake(_snapedView.center.x + _dragSpeed, _snapedView.center.y);
_lastPoint.x += _dragSpeed;
}
break;
case BMLongPressDragCellCollectionViewScrollDirectionUp:{
[self setContentOffset:CGPointMake(self.contentOffset.x, self.contentOffset.y - _dragSpeed) animated:NO];
_snapedView.center = CGPointMake(_snapedView.center.x, _snapedView.center.y - _dragSpeed);
_lastPoint.y -= _dragSpeed;
}
break;
case BMLongPressDragCellCollectionViewScrollDirectionDown:{
[self setContentOffset:CGPointMake(self.contentOffset.x, self.contentOffset.y + _dragSpeed) animated:NO];
_snapedView.center = CGPointMake(_snapedView.center.x, _snapedView.center.y + _dragSpeed);
_lastPoint.y += _dragSpeed;
}
break;
default:
break;
}
// Cell 沿
//
[UIView animateWithDuration:0.1 animations:^{
self.snapedView.center = self.lastPoint;
}];
// Cell
NSIndexPath *index1 = [self _getChangedNullIndexPath];
NSIndexPath *index = nil;
index = index1 ? : [self _getChangedIndexPath];
if (self.delegate && [self.delegate respondsToSelector:@selector(dragCellCollectionView:changedDragAtPoint:)]) {
[self.delegate dragCellCollectionView:self changedDragAtPoint:_lastPoint];
}
if (!index) {
return;
}
//
if (self.delegate && [self.delegate respondsToSelector:@selector(dragCellCollectionViewShouldBeginExchange:sourceIndexPath:toIndexPath:)]) {
if (![self.delegate dragCellCollectionViewShouldBeginExchange:self sourceIndexPath:_oldIndexPath toIndexPath:index]) {
return;
}
}
_currentIndexPath = index;
UICollectionViewCell *cell1 = [self cellForItemAtIndexPath:_currentIndexPath];
NSIndexPath *sourceIndexPath = _oldIndexPath;
NSIndexPath *toIndexPath = _currentIndexPath;
if (!index1) {
self.oldPoint = cell1.center;
//
[self _updateSourceData];
// cell
[self moveItemAtIndexPath:_oldIndexPath toIndexPath:_currentIndexPath];
// indexPath
_oldIndexPath = _currentIndexPath;
// Cell ,
//
[self reloadItemsAtIndexPaths:@[_oldIndexPath]];
} else {
//
[self _updateSourceData];
// cell
[self moveItemAtIndexPath:_oldIndexPath toIndexPath:_currentIndexPath];
self.oldPoint = [self cellForItemAtIndexPath:_currentIndexPath].center;
// indexPath
_oldIndexPath = _currentIndexPath;
[self reloadItemsAtIndexPaths:@[_oldIndexPath]];
}
//
if (self.delegate && [self.delegate respondsToSelector:@selector(dragCellCollectionViewShouldEndExchange:sourceIndexPath:toIndexPath:)]) {
[self.delegate dragCellCollectionViewShouldEndExchange:self sourceIndexPath:sourceIndexPath toIndexPath:toIndexPath];
}
}
#pragma mark -
- (void)handlelongGesture:(UILongPressGestureRecognizer *)longGesture {
CGPoint point = [longGesture locationInView:self];
NSIndexPath *indexPath = [self indexPathForItemAtPoint:point];
switch (longGesture.state) {
case UIGestureRecognizerStateBegan: {
//
self.userInteractionEnabled = NO;
_oldIndexPath = indexPath;
// Cell , cell break
if (_oldIndexPath == nil) {
self.longGesture.enabled = NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (self.canDrag) {
self.longGesture.enabled = YES;
}
});
break;
}
// Cell
if (self.delegate && [self.delegate respondsToSelector:@selector(dragCellCollectionViewShouldBeginMove:indexPath:)]) {
if (![self.delegate dragCellCollectionViewShouldBeginMove:self indexPath:_oldIndexPath]) {
_oldIndexPath = nil;
self.longGesture.enabled = NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (self.canDrag) {
self.longGesture.enabled = YES;
}
});
break;
}
}
self.isEndDrag = NO;
// cell
UICollectionViewCell *cell = [self cellForItemAtIndexPath:_oldIndexPath];
self.oldPoint = cell.center;
// nil
if (_snapedView) {
_snapedView = nil;
}
// View
if (self.delegate && [self.delegate respondsToSelector:@selector(dragCellCollectionView: startDragAtIndexPath:)]) {
_snapedView = [self.delegate dragCellCollectionView:self startDragAtIndexPath:indexPath];
}
if (!_snapedView) {
// 使 cell view
_snapedView = [cell snapshotViewAfterScreenUpdates:NO];
}
//
if (_dragSnapedViewBackgroundColor) {
_snapedView.backgroundColor = _dragSnapedViewBackgroundColor;
}
//
if (self.delegate && [self.delegate respondsToSelector:@selector(dragCellCollectionView: dragView:indexPath:)]) {
[self.delegate dragCellCollectionView:self dragView:_snapedView indexPath:indexPath];
}
// frame
_snapedView.frame = cell.frame;
// collectionView
[self addSubview:_snapedView];
// cell
cell.hidden = YES;
//
CGPoint currentPoint = point;
//
[UIView animateWithDuration:0.25 animations:^{
self.snapedView.transform = CGAffineTransformMakeScale(self.dragZoomScale, self.dragZoomScale);
self.snapedView.center = CGPointMake(currentPoint.x, currentPoint.y);
self.snapedView.alpha = self.dragCellAlpha;
}];
}
break;
case UIGestureRecognizerStateChanged: {
// collectionView
// https://github.com/liangdahong/BMLongPressDragCellCollectionView/issues/15
if (!_edgeTimer) {
[self _setEdgeTimer];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(dragCellCollectionView:changedDragAtPoint:)]) {
[self.delegate dragCellCollectionView:self changedDragAtPoint:point];
}
//
_lastPoint = point;
//
[UIView animateWithDuration:0.1 animations:^{
self.snapedView.center = self.lastPoint;
}];
NSIndexPath *index1 = [self _getChangedNullIndexPath];
NSIndexPath *index = nil;
if (index1) {
index = index1;
} else {
index = [self _getChangedIndexPath];
}
// || Cell
if (!index) {
break;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(dragCellCollectionViewShouldBeginExchange:sourceIndexPath:toIndexPath:)]) {
if (![self.delegate dragCellCollectionViewShouldBeginExchange:self sourceIndexPath:_oldIndexPath toIndexPath:index]) {
break;
}
}
_currentIndexPath = index;
UICollectionViewCell *cell1 = [self cellForItemAtIndexPath:_currentIndexPath];
NSIndexPath *sourceIndexPath = _oldIndexPath;
NSIndexPath *toIndexPath = _currentIndexPath;
if (!index1) {
self.oldPoint = cell1.center;
//
[self _updateSourceData];
// cell
[self moveItemAtIndexPath:_oldIndexPath toIndexPath:_currentIndexPath];
// indexPath
_oldIndexPath = _currentIndexPath;
[self reloadItemsAtIndexPaths:@[_oldIndexPath]];
} else {
//
[self _updateSourceData];
// cell
[self moveItemAtIndexPath:_oldIndexPath toIndexPath:_currentIndexPath];
self.oldPoint = [self cellForItemAtIndexPath:_currentIndexPath].center;
// indexPath
_oldIndexPath = _currentIndexPath;
[self reloadItemsAtIndexPaths:@[_oldIndexPath]];
}
//
if (self.delegate && [self.delegate respondsToSelector:@selector(dragCellCollectionViewShouldEndExchange:sourceIndexPath:toIndexPath:)]) {
[self.delegate dragCellCollectionViewShouldEndExchange:self sourceIndexPath:sourceIndexPath toIndexPath:toIndexPath];
}
break;
}
break;
default: {
self.userInteractionEnabled = YES;
if (self.delegate && [self.delegate respondsToSelector:@selector(dragCellCollectionView:endedDragAtPoint:)]) {
[self.delegate dragCellCollectionView:self endedDragAtPoint:point];
}
if (longGesture.isEnabled) {
if (!self.oldIndexPath) {
return;
}
UICollectionViewCell *cell = [self cellForItemAtIndexPath:_oldIndexPath];
//
self.userInteractionEnabled = NO;
//
self.isEndDrag = YES;
// cell
[UIView animateWithDuration:0.25 animations:^{
if (!cell) {
self.snapedView.center = self.oldPoint;
} else {
self.snapedView.center = cell.center;
}
self.snapedView.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
self.snapedView.alpha = 1.0;
} completion:^(BOOL finished) {
// cell
[self.snapedView removeFromSuperview];
self.snapedView = nil;
cell.hidden = NO;
self.userInteractionEnabled = YES;
}];
}
//
self.oldIndexPath = nil;
[self _stopEdgeTimer];
}
break;
}
}
- (void)dragMoveItemToIndexPath:(NSIndexPath *)newIndexPath {
if (self.isEndDrag) {
return;
}
self.isEndDrag = YES;
self.longGesture.enabled = NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (self.canDrag) {
self.longGesture.enabled = YES;
}
});
if (_oldIndexPath) {
_currentIndexPath = newIndexPath;
//
[self _updateSourceData];
// cell
[self moveItemAtIndexPath:_oldIndexPath toIndexPath:_currentIndexPath];
// indexPath
_oldIndexPath = newIndexPath;
[self reloadItemsAtIndexPaths:@[newIndexPath]];
UICollectionViewCell *cell = [self cellForItemAtIndexPath:_oldIndexPath];
cell.hidden = YES;
//
self.userInteractionEnabled = NO;
//
self.isEndDrag = YES;
// cell
[UIView animateWithDuration:0.25 animations:^{
if (!cell) {
self.snapedView.center = self.oldPoint;
} else {
self.snapedView.center = cell.center;
}
self.snapedView.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
self.snapedView.alpha = 1.0;
} completion:^(BOOL finished) {
// cell
[self.snapedView removeFromSuperview];
cell.hidden = NO;
self.userInteractionEnabled = YES;
}];
}
//
_oldIndexPath = nil;
[self _stopEdgeTimer];
}
@end

View File

@@ -0,0 +1,47 @@
// MIT License
//
// Copyright (c) 2019 https://liangdahong.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class BMLongPressDragCellCollectionView;
@protocol BMLongPressDragCellCollectionViewDataSource <UICollectionViewDataSource>
@required
/**
获取数据源,内部会在合适的时候获取最新的数据源,必须实现
@param dragCellCollectionView dragCellCollectionView
@return 最新的数据源
- (nullable NSArray *)dataSourceWithDragCellCollectionView:(BMLongPressDragCellCollectionView *)dragCellCollectionView {
return self.dataArray;
}
*/
- (nullable NSArray <NSArray <id> *> *)dataSourceWithDragCellCollectionView:(__kindof BMLongPressDragCellCollectionView *)dragCellCollectionView;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,98 @@
// MIT License
//
// Copyright (c) 2019 https://liangdahong.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class BMLongPressDragCellCollectionView;
@protocol BMLongPressDragCellCollectionViewDelegate <UICollectionViewDelegateFlowLayout>
@required
/**
Cell 有交换时调用
@param dragCellCollectionView dragCellCollectionView
@param newDataArray 最新的数据源,需要使用者保存,在拖拽的时候 Cell 已经移动,需保存最新的数据源
- (void)dragCellCollectionView:(__kindof BMLongPressDragCellCollectionView *)dragCellCollectionView newDataArrayAfterMove:(nullable NSArray <NSArray <id> *> *)newDataArray {
self.dataArray = [newDataArray copy];
}
*/
- (void)dragCellCollectionView:(__kindof BMLongPressDragCellCollectionView *)dragCellCollectionView newDataArrayAfterMove:(nullable NSArray <NSArray <id> *> *)newDataArray;
@optional
/**
将要开始拖拽时,询问此位置的 Cell 是否能拖拽
@param dragCellCollectionView dragCellCollectionView
@param indexPath indexPath
@return YES: 正常拖拽和移动 NO:此 Cell 不可拖拽,如:增加按钮等。
*/
- (BOOL)dragCellCollectionViewShouldBeginMove:(__kindof BMLongPressDragCellCollectionView *)dragCellCollectionView indexPath:(NSIndexPath *)indexPath;
/**
将要开始拖拽时,向外面获取需要拖拽的 View如果不实现就内部自动处理。
@param dragCellCollectionView dragCellCollectionView
@param indexPath indexPath
@return dragView
*/
- (UIView *)dragCellCollectionView:(__kindof BMLongPressDragCellCollectionView *)dragCellCollectionView startDragAtIndexPath:(NSIndexPath *)indexPath;
/// 开始拖拽时,让外面的使用者可以对拖拽的 View 做额外操作
/// @param dragCellCollectionView dragCellCollectionView
/// @param dragView dragView
/// @param indexPath indexPath
- (void)dragCellCollectionView:(__kindof BMLongPressDragCellCollectionView *)dragCellCollectionView dragView:(UIView *)dragView indexPath:(NSIndexPath *)indexPath;
/// 正在拖拽时
/// @param dragCellCollectionView dragCellCollectionView
/// @param point 手指触摸点对于 collectionView 的位置
/// 可以参考 https://github.com/liangdahong/ToutiaoDemo/blob/master/ToutiaoDemo/Classes/Edit/Controller/BMChannelEditVC.m 的使用
- (void)dragCellCollectionView:(__kindof BMLongPressDragCellCollectionView *)dragCellCollectionView changedDragAtPoint:(CGPoint)point;
/// cell 将要交换时,询问是否能交换
/// @param dragCellCollectionView dragCellCollectionView
/// @param sourceIndexPath 原来的 IndexPath
/// @param destinationIndexPath 将要交换的 IndexPath
/// YES: 正常拖拽和移动 NO:此Cell不可拖拽增加按钮等。
- (BOOL)dragCellCollectionViewShouldBeginExchange:(__kindof BMLongPressDragCellCollectionView *)dragCellCollectionView sourceIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath;
/// 结束交换时
/// @param dragCellCollectionView dragCellCollectionView
/// @param sourceIndexPath 原来的 IndexPath
/// @param destinationIndexPath 被交换的 IndexPath
- (void)dragCellCollectionViewShouldEndExchange:(__kindof BMLongPressDragCellCollectionView *)dragCellCollectionView sourceIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath;
/// 结束拖拽时
/// @param dragCellCollectionView dragCellCollectionView
/// @param point 手指触摸点对于 collectionView 的位置
/// 可以参考 https://github.com/liangdahong/ToutiaoDemo/blob/master/ToutiaoDemo/Classes/Edit/Controller/BMChannelEditVC.m 的使用
- (void)dragCellCollectionView:(__kindof BMLongPressDragCellCollectionView *)dragCellCollectionView endedDragAtPoint:(CGPoint)point;
@end
NS_ASSUME_NONNULL_END