From 074a65ea9d94022af505b7a2cebdd3514d762cab Mon Sep 17 00:00:00 2001 From: MoLice Date: Fri, 19 Apr 2019 16:41:35 +0800 Subject: [PATCH] 3.1.5 --- QMUIKit/Info.plist | 2 +- .../QMUIImagePickerPreviewViewController.h | 4 +- .../QMUIImagePickerViewController.m | 2 +- QMUIKit/QMUIComponents/QMUITextView.m | 12 ++++ QMUIKit/QMUIKit.h | 2 +- .../QMUICommonTableViewController.m | 4 +- QMUIKit/UIKitExtensions/UILabel+QMUI.h | 8 ++- QMUIKit/UIKitExtensions/UILabel+QMUI.m | 4 +- QMUIKit/UIKitExtensions/UITableView+QMUI.h | 12 ++++ QMUIKit/UIKitExtensions/UITableView+QMUI.m | 14 +++++ .../UIKitExtensions/UIViewController+QMUI.m | 60 ++++++++++++++++++- 11 files changed, 111 insertions(+), 13 deletions(-) diff --git a/QMUIKit/Info.plist b/QMUIKit/Info.plist index fa644949..ba3791d6 100644 --- a/QMUIKit/Info.plist +++ b/QMUIKit/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 3.1.4 + 3.1.5 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerPreviewViewController.h b/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerPreviewViewController.h index d98fa028..a3995b38 100644 --- a/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerPreviewViewController.h +++ b/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerPreviewViewController.h @@ -75,8 +75,8 @@ NS_ASSUME_NONNULL_BEGIN * @param currentImageIndex 当前展示的图片在 imageAssetArray 的索引 * @param singleCheckMode 是否为单选模式,如果是单选模式,则不显示 checkbox */ -- (void)updateImagePickerPreviewViewWithImagesAssetArray:(NSArray * _Nullable)imageAssetArray - selectedImageAssetArray:(NSArray * _Nullable)selectedImageAssetArray +- (void)updateImagePickerPreviewViewWithImagesAssetArray:(NSMutableArray * _Nullable)imageAssetArray + selectedImageAssetArray:(NSMutableArray * _Nullable)selectedImageAssetArray currentImageIndex:(NSInteger)currentImageIndex singleCheckMode:(BOOL)singleCheckMode; diff --git a/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerViewController.m b/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerViewController.m index e2651b53..553d9bb9 100644 --- a/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerViewController.m +++ b/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerViewController.m @@ -362,7 +362,7 @@ - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPa [self initPreviewViewControllerIfNeeded]; if (!self.allowsMultipleSelection) { // 单选的情况下 - [self.imagePickerPreviewViewController updateImagePickerPreviewViewWithImagesAssetArray:@[imageAsset] + [self.imagePickerPreviewViewController updateImagePickerPreviewViewWithImagesAssetArray:@[imageAsset].mutableCopy selectedImageAssetArray:nil currentImageIndex:0 singleCheckMode:YES]; diff --git a/QMUIKit/QMUIComponents/QMUITextView.m b/QMUIKit/QMUIComponents/QMUITextView.m index 74a4202a..642763a2 100644 --- a/QMUIKit/QMUIComponents/QMUITextView.m +++ b/QMUIKit/QMUIComponents/QMUITextView.m @@ -298,9 +298,21 @@ - (void)setFrame:(CGRect)frame { // 如果没走完 didInitialize,说明 self.maximumHeight 尚未被赋初始值 CGFLOAT_MAX,此时的值为 0,就会导致调用 initWithFrame: 时高度无效,必定被指定为 0 frame = CGRectSetHeight(frame, MIN(CGRectGetHeight(frame), self.maximumHeight)); } + + // 重写了 UITextView 的 drawRect: 后,对于带小数点的 frame 会导致文本框右边多出一条黑线,原因未明,暂时这样处理 + // https://github.com/Tencent/QMUI_iOS/issues/557 + frame = CGRectFlatted(frame); + [super setFrame:frame]; } +- (void)setBounds:(CGRect)bounds { + // 重写了 UITextView 的 drawRect: 后,对于带小数点的 frame 会导致文本框右边多出一条黑线,原因未明,暂时这样处理 + // https://github.com/Tencent/QMUI_iOS/issues/557 + bounds = CGRectFlatted(bounds); + [super setBounds:bounds]; +} + - (void)layoutSubviews { [super layoutSubviews]; if (self.placeholder.length > 0) { diff --git a/QMUIKit/QMUIKit.h b/QMUIKit/QMUIKit.h index 0030e341..bc96e63d 100644 --- a/QMUIKit/QMUIKit.h +++ b/QMUIKit/QMUIKit.h @@ -13,7 +13,7 @@ #ifndef QMUIKit_h #define QMUIKit_h -static NSString * const QMUI_VERSION = @"3.1.4"; +static NSString * const QMUI_VERSION = @"3.1.5"; #if __has_include("CAAnimation+QMUI.h") #import "CAAnimation+QMUI.h" diff --git a/QMUIKit/QMUIMainFrame/QMUICommonTableViewController.m b/QMUIKit/QMUIMainFrame/QMUICommonTableViewController.m index 317abd58..9339bf9b 100644 --- a/QMUIKit/QMUIMainFrame/QMUICommonTableViewController.m +++ b/QMUIKit/QMUIMainFrame/QMUICommonTableViewController.m @@ -119,7 +119,9 @@ - (void)initSubviews { - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; - [self.tableView qmui_clearsSelection]; + if (!self.tableView.allowsMultipleSelection) { + [self.tableView qmui_clearsSelection]; + } } - (void)viewDidLayoutSubviews { diff --git a/QMUIKit/UIKitExtensions/UILabel+QMUI.h b/QMUIKit/UIKitExtensions/UILabel+QMUI.h index ec4aa708..1bf1deca 100644 --- a/QMUIKit/UIKitExtensions/UILabel+QMUI.h +++ b/QMUIKit/UIKitExtensions/UILabel+QMUI.h @@ -15,9 +15,11 @@ #import +NS_ASSUME_NONNULL_BEGIN + @interface UILabel (QMUI) -- (instancetype)qmui_initWithFont:(UIFont *)font textColor:(UIColor *)textColor; +- (instancetype)qmui_initWithFont:(nullable UIFont *)font textColor:(nullable UIColor *)textColor; /** * @brief 在需要特殊样式时,可通过此属性直接给整个 label 添加 NSAttributeName 系列样式,然后 setText 即可,无需使用繁琐的 attributedText @@ -35,7 +37,7 @@ * 唯一例外的极端情况是:先用方法2将文字设成红色,再用方法1将文字设成蓝色,最后再 setText,这时虽然代码执行顺序靠后的是方法1,但最终生效的会是方法2,为了避免这种极端情况的困扰,建议不要同时使用方法1和方法2去设置同一种样式。 * */ -@property(nonatomic, copy) NSDictionary *qmui_textAttributes; +@property(nullable, nonatomic, copy) NSDictionary *qmui_textAttributes; /** * Setter 设置当前整段文字的行高 @@ -78,3 +80,5 @@ - (void)qmui_avoidBlendedLayersIfShowingChineseWithBackgroundColor:(UIColor *)color; @end + +NS_ASSUME_NONNULL_END diff --git a/QMUIKit/UIKitExtensions/UILabel+QMUI.m b/QMUIKit/UIKitExtensions/UILabel+QMUI.m index f1477fc7..55e2deec 100644 --- a/QMUIKit/UIKitExtensions/UILabel+QMUI.m +++ b/QMUIKit/UIKitExtensions/UILabel+QMUI.m @@ -44,7 +44,7 @@ - (void)qmui_setText:(NSString *)text { // 在 qmui_textAttributes 样式基础上添加用户传入的 attributedString 中包含的新样式。换句话说,如果这个方法里有样式冲突,则以 attributedText 为准 - (void)qmui_setAttributedText:(NSAttributedString *)text { - if (!text) { + if (!text || (!self.qmui_textAttributes.count && ![self _hasSetQmuiLineHeight])) { [self qmui_setAttributedText:text]; return; } @@ -110,7 +110,7 @@ - (NSDictionary *)qmui_textAttributes { // 去除最后一个字的 kern 效果,并且在有必要的情况下应用 qmui_setLineHeight: 设置的行高 - (NSAttributedString *)attributedStringWithKernAndLineHeightAdjusted:(NSAttributedString *)string { - if (!string || !string.length) { + if (!string.length) { return string; } NSMutableAttributedString *attributedString = nil; diff --git a/QMUIKit/UIKitExtensions/UITableView+QMUI.h b/QMUIKit/UIKitExtensions/UITableView+QMUI.h index 113d8e83..7d175e4d 100644 --- a/QMUIKit/UIKitExtensions/UITableView+QMUI.h +++ b/QMUIKit/UIKitExtensions/UITableView+QMUI.h @@ -16,6 +16,8 @@ #import #import +NS_ASSUME_NONNULL_BEGIN + /// cell 在当前 section 里的位置,注意判断时要用 (var & xxx) == xxx 的方式 typedef NS_OPTIONS(NSInteger, QMUITableViewCellPosition) { QMUITableViewCellPositionNone = 0, // 默认 @@ -114,4 +116,14 @@ typedef NS_OPTIONS(NSInteger, QMUITableViewCellPosition) { */ - (BOOL)qmui_canScroll; +/** + 等同于 UITableView 自 iOS 11 开始新增的同名方法,但兼容 iOS 11 以下的系统使用。 + + @param updates insert/delete/reload/move calls + @param completion completion callback + */ +- (void)qmui_performBatchUpdates:(void (NS_NOESCAPE ^ _Nullable)(void))updates completion:(void (^ _Nullable)(BOOL finished))completion; + @end + +NS_ASSUME_NONNULL_END diff --git a/QMUIKit/UIKitExtensions/UITableView+QMUI.m b/QMUIKit/UIKitExtensions/UITableView+QMUI.m index 546e5b3f..8fc4ad5f 100644 --- a/QMUIKit/UIKitExtensions/UITableView+QMUI.m +++ b/QMUIKit/UIKitExtensions/UITableView+QMUI.m @@ -20,6 +20,12 @@ const NSUInteger kFloatValuePrecision = 4;// 统一一个小数点运算精度 +@interface UITableView () + +// iOS 9、10 的 UITableView 都有这个私有方法,因此把它显示声明一次以便调用 +- (void)_performBatchUpdates:(void (NS_NOESCAPE ^ _Nullable)(void))updates completion:(void (^ _Nullable)(BOOL finished))completion; +@end + @implementation UITableView (QMUI) + (void)load { @@ -314,4 +320,12 @@ - (void)QMUISymbolicUsingTableViewEstimatedHeightMakeWarning { QMUILog(@"UITableView 的 estimatedRow(SectionHeader / SectionFooter)Height 属性会影响 contentSize、sizeThatFits:、rectForXxx 等方法的计算,导致计算结果不准确,建议重新考虑是否要使用 estimated。可添加 '%@' 的 Symbolic Breakpoint 以捕捉此类信息\n%@", NSStringFromSelector(_cmd), [NSThread callStackSymbols]); } +- (void)qmui_performBatchUpdates:(void (NS_NOESCAPE ^ _Nullable)(void))updates completion:(void (^ _Nullable)(BOOL finished))completion { + if (@available(iOS 11.0, *)) { + [self performBatchUpdates:updates completion:completion]; + } else { + [self _performBatchUpdates:updates completion:completion]; + } +} + @end diff --git a/QMUIKit/UIKitExtensions/UIViewController+QMUI.m b/QMUIKit/UIKitExtensions/UIViewController+QMUI.m index 366846b5..ba8d9010 100644 --- a/QMUIKit/UIKitExtensions/UIViewController+QMUI.m +++ b/QMUIKit/UIKitExtensions/UIViewController+QMUI.m @@ -15,6 +15,7 @@ #import "UIViewController+QMUI.h" #import "UINavigationController+QMUI.h" +#import "QMUINavigationController.h" #import "QMUICore.h" #import "UIInterface+QMUI.h" #import "NSObject+QMUI.h" @@ -244,12 +245,65 @@ - (CGFloat)qmui_navigationBarMaxYInViewCoordinator { } } - UINavigationBar *navigationBar = (!navigationController.navigationBarHidden && navigationController.navigationBar) ? navigationController.navigationBar : nil; - - if (!navigationBar) { + if (!navigationController) { return 0; } + UINavigationBar *navigationBar = navigationController.navigationBar; + CGFloat barMinX = CGRectGetMinX(navigationBar.frame); + CGFloat barPresentationMinX = CGRectGetMinX(navigationBar.layer.presentationLayer.frame); + CGFloat superviewX = CGRectGetMinX(self.view.superview.frame); + CGFloat superviewX2 = CGRectGetMinX(self.view.superview.superview.frame); + + if (self.qmui_navigationControllerPoppingInteracted) { + if (barMinX != 0 && barMinX == barPresentationMinX) { + // 返回到无 bar 的界面 + return 0; + } else if (barMinX > 0) { + if (self.qmui_willAppearByInteractivePopGestureRecognizer) { + // 要手势返回去的那个界面隐藏了 bar + return 0; + } + } else if (barMinX < 0) { + // 正在手势返回的这个界面隐藏了 bar + if (!self.qmui_willAppearByInteractivePopGestureRecognizer) { + return 0; + } + } else { + // 正在手势返回的这个界面隐藏了 bar + if (barPresentationMinX != 0 && !self.qmui_willAppearByInteractivePopGestureRecognizer) { + return 0; + } + } + } else { + if (barMinX > 0) { + // 正在 pop 回无 bar 的界面 + if (superviewX2 <= 0) { + // 即将回到的那个无 bar 的界面 + return 0; + } + } else if (barMinX < 0) { + if (barPresentationMinX < 0) { + // 从无 bar push 进无 bar 的界面 + return 0; + } + // 正在从有 bar 的界面 push 到无 bar 的界面(bar 被推到左边屏幕外,所以是负数) + if (superviewX >= 0) { + // 即将进入的那个无 bar 的界面 + return 0; + } + } else { + if (superviewX < 0 && barPresentationMinX != 0) { + // 无 bar push 进有 bar 的界面时,背后的那个无 bar 的界面 + return 0; + } + if (superviewX2 > 0) { + // 无 bar pop 回有 bar 的界面时,被 pop 掉的那个无 bar 的界面 + return 0; + } + } + } + CGRect navigationBarFrameInView = [self.view convertRect:navigationBar.frame fromView:navigationBar.superview]; CGRect navigationBarFrame = CGRectIntersection(self.view.bounds, navigationBarFrameInView);