diff --git a/QMUIKit.podspec b/QMUIKit.podspec
index 20a5aac6..39c55065 100644
--- a/QMUIKit.podspec
+++ b/QMUIKit.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "QMUIKit"
- s.version = "2.7.0"
+ s.version = "2.7.1"
s.summary = "致力于提高项目 UI 开发效率的解决方案"
s.description = <<-DESC
QMUI iOS 是一个致力于提高项目 UI 开发效率的解决方案,其设计目的是用于辅助快速搭建一个具备基本设计还原效果的 iOS 项目,同时利用自身提供的丰富控件及兼容处理, 让开发者能专注于业务需求而无需耗费精力在基础代码的设计上。不管是新项目的创建,或是已有项目的维护,均可使开发效率和项目质量得到大幅度提升。
diff --git a/QMUIKit/Info.plist b/QMUIKit/Info.plist
index 5c555d18..e46a403d 100644
--- a/QMUIKit/Info.plist
+++ b/QMUIKit/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.7.0
+ 2.7.1
CFBundleVersion
$(CURRENT_PROJECT_VERSION)
NSPrincipalClass
diff --git a/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerPreviewViewController.m b/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerPreviewViewController.m
index e274e33b..488dc552 100644
--- a/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerPreviewViewController.m
+++ b/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerPreviewViewController.m
@@ -125,6 +125,10 @@ - (void)viewDidLayoutSubviews {
}
}
+- (BOOL)prefersStatusBarHidden {
+ return YES;
+}
+
- (void)setToolBarBackgroundColor:(UIColor *)toolBarBackgroundColor {
_toolBarBackgroundColor = toolBarBackgroundColor;
self.topToolBarView.backgroundColor = self.toolBarBackgroundColor;
diff --git a/QMUIKit/QMUIComponents/QMUIBadge/UIBarItem+QMUIBadge.h b/QMUIKit/QMUIComponents/QMUIBadge/UIBarItem+QMUIBadge.h
index 4f658a56..0366fc06 100644
--- a/QMUIKit/QMUIComponents/QMUIBadge/UIBarItem+QMUIBadge.h
+++ b/QMUIKit/QMUIComponents/QMUIBadge/UIBarItem+QMUIBadge.h
@@ -21,8 +21,11 @@
#pragma mark - Badge
-/// 设置未读数,0 则隐藏未读数
-@property(nonatomic, assign) NSUInteger qmui_badgeValue;
+/// 用数字设置未读数,0表示不显示未读数
+@property(nonatomic, assign) NSUInteger qmui_badgeInteger;
+
+/// 用字符串设置未读数,nil 表示不显示未读数
+@property(nonatomic, copy) NSString *qmui_badgeString;
@property(nonatomic, strong, nullable) UIColor *qmui_badgeBackgroundColor;
@property(nonatomic, strong, nullable) UIColor *qmui_badgeTextColor;
@@ -44,6 +47,7 @@
#pragma mark - UpdatesIndicator
+/// 控制红点的显隐
@property(nonatomic, assign) BOOL qmui_shouldShowUpdatesIndicator;
@property(nonatomic, strong, nullable) UIColor *qmui_updatesIndicatorColor;
@property(nonatomic, assign) CGSize qmui_updatesIndicatorSize;
diff --git a/QMUIKit/QMUIComponents/QMUIBadge/UIBarItem+QMUIBadge.m b/QMUIKit/QMUIComponents/QMUIBadge/UIBarItem+QMUIBadge.m
index c9daa81d..0a97b929 100644
--- a/QMUIKit/QMUIComponents/QMUIBadge/UIBarItem+QMUIBadge.m
+++ b/QMUIKit/QMUIComponents/QMUIBadge/UIBarItem+QMUIBadge.m
@@ -46,7 +46,7 @@ + (void)load {
// 针对非 customView 的 UIBarButtonItem,负责将红点添加上去
ExtendImplementationOfVoidMethodWithSingleArgument([UIBarButtonItem class], @selector(setView:), UIView *, ^(UIBarButtonItem *selfObject, UIView *firstArgv) {
- if (selfObject.qmui_badgeValue > 0 && selfObject.qmui_badgeLabel) {
+ if (selfObject.qmui_badgeString.length && selfObject.qmui_badgeLabel) {
[firstArgv addSubview:selfObject.qmui_badgeLabel];
}
if (selfObject.qmui_shouldShowUpdatesIndicator && selfObject.qmui_updatesIndicatorView) {
@@ -56,7 +56,7 @@ + (void)load {
// 针对 UITabBarItem,负责将红点添加上去
ExtendImplementationOfVoidMethodWithSingleArgument([UITabBarItem class], @selector(setView:), UIView *, ^(UITabBarItem *selfObject, UIView *firstArgv) {
- if (selfObject.qmui_badgeValue > 0 && selfObject.qmui_badgeLabel) {
+ if (selfObject.qmui_badgeString.length && selfObject.qmui_badgeLabel) {
[firstArgv addSubview:selfObject.qmui_badgeLabel];
}
if (selfObject.qmui_shouldShowUpdatesIndicator && selfObject.qmui_updatesIndicatorView) {
@@ -113,10 +113,20 @@ - (void)didInitialize {
#pragma mark - Badge
-static char kAssociatedObjectKey_badgeValue;
-- (void)setQmui_badgeValue:(NSUInteger)qmui_badgeValue {
- objc_setAssociatedObject(self, &kAssociatedObjectKey_badgeValue, @(qmui_badgeValue), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- if (qmui_badgeValue > 0) {
+static char kAssociatedObjectKey_badgeInteger;
+- (void)setQmui_badgeInteger:(NSUInteger)qmui_badgeInteger {
+ objc_setAssociatedObject(self, &kAssociatedObjectKey_badgeInteger, @(qmui_badgeInteger), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ self.qmui_badgeString = qmui_badgeInteger > 0 ? [NSString stringWithFormat:@"%@", @(qmui_badgeInteger)] : nil;
+}
+
+- (NSUInteger)qmui_badgeInteger {
+ return [((NSNumber *)objc_getAssociatedObject(self, &kAssociatedObjectKey_badgeInteger)) unsignedIntegerValue];
+}
+
+static char kAssociatedObjectKey_badgeString;
+- (void)setQmui_badgeString:(NSString *)qmui_badgeString {
+ objc_setAssociatedObject(self, &kAssociatedObjectKey_badgeString, qmui_badgeString, OBJC_ASSOCIATION_COPY_NONATOMIC);
+ if (qmui_badgeString.length) {
if (!self.qmui_badgeLabel) {
self.qmui_badgeLabel = [[_QMUIBadgeLabel alloc] init];
self.qmui_badgeLabel.clipsToBounds = YES;
@@ -129,7 +139,7 @@ - (void)setQmui_badgeValue:(NSUInteger)qmui_badgeValue {
self.qmui_badgeLabel.centerOffsetLandscape = self.qmui_badgeCenterOffsetLandscape;
[self.qmui_view addSubview:self.qmui_badgeLabel];
}
- self.qmui_badgeLabel.text = [NSString stringWithFormat:@"%@", @(qmui_badgeValue)];
+ self.qmui_badgeLabel.text = qmui_badgeString;
self.qmui_badgeLabel.hidden = NO;
[self setNeedsUpdateBadgeLabelLayout];
} else {
@@ -137,8 +147,8 @@ - (void)setQmui_badgeValue:(NSUInteger)qmui_badgeValue {
}
}
-- (NSUInteger)qmui_badgeValue {
- return [((NSNumber *)objc_getAssociatedObject(self, &kAssociatedObjectKey_badgeValue)) unsignedIntegerValue];
+- (NSString *)qmui_badgeString {
+ return (NSString *)objc_getAssociatedObject(self, &kAssociatedObjectKey_badgeString);
}
static char kAssociatedObjectKey_badgeBackgroundColor;
@@ -225,7 +235,7 @@ - (_QMUIBadgeLabel *)qmui_badgeLabel {
}
- (void)setNeedsUpdateBadgeLabelLayout {
- if (self.qmui_badgeValue > 0) {
+ if (self.qmui_badgeString.length) {
if ([self isKindOfClass:[UIBarButtonItem class]] && ((UIBarButtonItem *)self).customView) {
// 如果是 customView,由于无法重写它的 layoutSubviews,所以认为它目前的 frame 已经是最终的 frame,直接按照当前 frame 来布局即可
[self.qmui_badgeLabel updateLayout];
diff --git a/QMUIKit/QMUIComponents/QMUICellHeightCache.h b/QMUIKit/QMUIComponents/QMUICellHeightCache.h
index 081b000a..5b33896f 100644
--- a/QMUIKit/QMUIComponents/QMUICellHeightCache.h
+++ b/QMUIKit/QMUIComponents/QMUICellHeightCache.h
@@ -40,7 +40,7 @@
- (void)invalidateAllHeightCache;
// 给 tableview 和 collectionview 调用的方法
-- (void)enumerateAllOrientationsUsingBlock:(void (^)(NSMutableArray *heightsBySection))block;
+- (void)enumerateAllOrientationsUsingBlock:(void (^)(NSMutableArray *> *heightsBySection))block;
- (void)buildSectionsIfNeeded:(NSInteger)targetSection;
- (void)buildCachesAtIndexPathsIfNeeded:(NSArray *)indexPaths;
diff --git a/QMUIKit/QMUIComponents/QMUICellHeightCache.m b/QMUIKit/QMUIComponents/QMUICellHeightCache.m
index 22b78c6d..3af64739 100644
--- a/QMUIKit/QMUIComponents/QMUICellHeightCache.m
+++ b/QMUIKit/QMUIComponents/QMUICellHeightCache.m
@@ -11,10 +11,11 @@
#import "QMUICore.h"
#import "UIScrollView+QMUI.h"
#import "UIView+QMUI.h"
+#import "NSNumber+QMUI.h"
@implementation QMUICellHeightCache {
- NSMutableDictionary *_mutableHeightsByKeyForPortrait;
- NSMutableDictionary *_mutableHeightsByKeyForLandscape;
+ NSMutableDictionary, NSNumber *> *_mutableHeightsByKeyForPortrait;
+ NSMutableDictionary, NSNumber *> *_mutableHeightsByKeyForLandscape;
}
- (instancetype)init {
@@ -26,7 +27,7 @@ - (instancetype)init {
return self;
}
-- (NSMutableDictionary *)mutableHeightsByKeyForCurrentOrientation {
+- (NSMutableDictionary, NSNumber *> *)mutableHeightsByKeyForCurrentOrientation {
return UIDeviceOrientationIsPortrait([UIDevice currentDevice].orientation) ? _mutableHeightsByKeyForPortrait : _mutableHeightsByKeyForLandscape;
}
@@ -40,11 +41,7 @@ - (void)cacheHeight:(CGFloat)height byKey:(id)key {
}
- (CGFloat)heightForKey:(id)key {
-#if CGFLOAT_IS_DOUBLE
- return [[self mutableHeightsByKeyForCurrentOrientation][key] doubleValue];
-#else
- return [[self mutableHeightsByKeyForCurrentOrientation][key] floatValue];
-#endif
+ return [self mutableHeightsByKeyForCurrentOrientation][key].qmui_CGFloatValue;
}
- (void)invalidateHeightForKey:(id)key {
@@ -64,8 +61,8 @@ - (NSString *)description {
@end
@implementation QMUICellHeightIndexPathCache {
- NSMutableArray *_heightsBySectionForPortrait;
- NSMutableArray *_heightsBySectionForLandscape;
+ NSMutableArray *> *_heightsBySectionForPortrait;
+ NSMutableArray *> *_heightsBySectionForLandscape;
}
- (instancetype)init {
@@ -77,11 +74,11 @@ - (instancetype)init {
return self;
}
-- (NSMutableArray *)heightsBySectionForCurrentOrientation {
+- (NSMutableArray *> *)heightsBySectionForCurrentOrientation {
return UIDeviceOrientationIsPortrait([UIDevice currentDevice].orientation) ? _heightsBySectionForPortrait : _heightsBySectionForLandscape;
}
-- (void)enumerateAllOrientationsUsingBlock:(void (^)(NSMutableArray *heightsBySection))block {
+- (void)enumerateAllOrientationsUsingBlock:(void (^)(NSMutableArray *> *heightsBySection))block {
if (block) {
block(_heightsBySectionForPortrait);
block(_heightsBySectionForLandscape);
@@ -102,28 +99,23 @@ - (void)cacheHeight:(CGFloat)height byIndexPath:(NSIndexPath *)indexPath {
- (CGFloat)heightForIndexPath:(NSIndexPath *)indexPath {
[self buildCachesAtIndexPathsIfNeeded:@[indexPath]];
- NSNumber *number = self.heightsBySectionForCurrentOrientation[indexPath.section][indexPath.row];
-#if CGFLOAT_IS_DOUBLE
- return number.doubleValue;
-#else
- return number.floatValue;
-#endif
+ return self.heightsBySectionForCurrentOrientation[indexPath.section][indexPath.row].qmui_CGFloatValue;
}
- (void)invalidateHeightAtIndexPath:(NSIndexPath *)indexPath {
[self buildCachesAtIndexPathsIfNeeded:@[indexPath]];
- [self enumerateAllOrientationsUsingBlock:^(NSMutableArray *heightsBySection) {
+ [self enumerateAllOrientationsUsingBlock:^(NSMutableArray *> *heightsBySection) {
heightsBySection[indexPath.section][indexPath.row] = @-1;
}];
}
- (void)invalidateAllHeightCache {
- [self enumerateAllOrientationsUsingBlock:^(NSMutableArray *heightsBySection) {
+ [self enumerateAllOrientationsUsingBlock:^(NSMutableArray *> *heightsBySection) {
[heightsBySection removeAllObjects];
}];
}
-- (void)buildCachesAtIndexPathsIfNeeded:(NSArray *)indexPaths {
+- (void)buildCachesAtIndexPathsIfNeeded:(NSArray *)indexPaths {
[indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
[self buildSectionsIfNeeded:indexPath.section];
[self buildRowsIfNeeded:indexPath.row inExistSection:indexPath.section];
@@ -131,7 +123,7 @@ - (void)buildCachesAtIndexPathsIfNeeded:(NSArray *)indexPaths {
}
- (void)buildSectionsIfNeeded:(NSInteger)targetSection {
- [self enumerateAllOrientationsUsingBlock:^(NSMutableArray *heightsBySection) {
+ [self enumerateAllOrientationsUsingBlock:^(NSMutableArray *> *heightsBySection) {
for (NSInteger section = 0; section <= targetSection; ++section) {
if (section >= heightsBySection.count) {
heightsBySection[section] = [NSMutableArray array];
@@ -141,7 +133,7 @@ - (void)buildSectionsIfNeeded:(NSInteger)targetSection {
}
- (void)buildRowsIfNeeded:(NSInteger)targetRow inExistSection:(NSInteger)section {
- [self enumerateAllOrientationsUsingBlock:^(NSMutableArray *heightsBySection) {
+ [self enumerateAllOrientationsUsingBlock:^(NSMutableArray *> *heightsBySection) {
NSMutableArray *heightsByRow = heightsBySection[section];
for (NSInteger row = 0; row <= targetRow; ++row) {
if (row >= heightsByRow.count) {
diff --git a/QMUIKit/QMUIComponents/QMUICellHeightKeyCache/UITableView+QMUICellHeightKeyCache.h b/QMUIKit/QMUIComponents/QMUICellHeightKeyCache/UITableView+QMUICellHeightKeyCache.h
index d3c1b60f..9883df83 100644
--- a/QMUIKit/QMUIComponents/QMUICellHeightKeyCache/UITableView+QMUICellHeightKeyCache.h
+++ b/QMUIKit/QMUIComponents/QMUICellHeightKeyCache/UITableView+QMUICellHeightKeyCache.h
@@ -16,7 +16,8 @@
* 1. 将 tableView.qmui_cacheCellHeightByKeyAutomatically = YES
* 2. 实现 tableView 的 delegate 方法 qmui_tableView:cacheKeyForRowAtIndexPath: 返回一个 key。建议 key 由所有可能影响高度的字段拼起来,这样当数据发生变化时不需要手动更新缓存。
*
- * @note 注意这里的高度缓存仅适合于使用 self-sizing 机制的 tableView(也即 tableView.rowHeight = UITableViewAutomaticDimension),QMUICellHeightKeyCache 会自动在 willDisplayCell 里将 cell 的当前高度缓存起来,然后在 heightForRow 里从缓存中读取高度后使用。而如果你的 tableView 并没有使用 self-sizing 机制(也即自己重写了 heightForRow),则请勿使用本控件的功能。
+ * @note 注意这里的高度缓存仅适合于使用 self-sizing 机制的 tableView(也即 tableView.rowHeight = UITableViewAutomaticDimension),QMUICellHeightKeyCache 会自动在 willDisplayCell 里将 cell 的当前高度缓存起来,然后在 heightForRow 里从缓存中读取高度后使用。
+ * @note 如果 tableView.delegate 指向的类既想使用 QMUICellHeightKeyCache 的功能,又需要在 tableView:heightForRowAtIndexPath: 里写业务逻辑,则可通过 tableView:heightForRowAtIndexPath: 返回 -1 来使用 QMUICellHeightKeyCache 的计算结果,返回大于等于 0 的值将不会触发 QMUICellHeightKeyCache 的计算,具体请看 QMUI Demo。
*
* @note 在 UITableView 的宽度和 contentInset 发生变化时(例如横竖屏旋转、iPad 分屏),高度缓存会自动刷新,所以无需为这种情况做保护。
*/
diff --git a/QMUIKit/QMUIComponents/QMUICellHeightKeyCache/UITableView+QMUICellHeightKeyCache.m b/QMUIKit/QMUIComponents/QMUICellHeightKeyCache/UITableView+QMUICellHeightKeyCache.m
index 95cfe31b..0a0ca483 100644
--- a/QMUIKit/QMUIComponents/QMUICellHeightKeyCache/UITableView+QMUICellHeightKeyCache.m
+++ b/QMUIKit/QMUIComponents/QMUICellHeightKeyCache/UITableView+QMUICellHeightKeyCache.m
@@ -44,13 +44,13 @@ - (void)setQmui_cacheCellHeightByKeyAutomatically:(BOOL)qmui_cacheCellHeightByKe
NSAssert(!self.delegate || [self.delegate respondsToSelector:@selector(qmui_tableView:cacheKeyForRowAtIndexPath:)], @"%@ 需要实现 %@ 方法才能自动缓存 cell 高度", self.delegate, NSStringFromSelector(@selector(qmui_tableView:cacheKeyForRowAtIndexPath:)));
NSAssert(self.estimatedRowHeight != 0, @"estimatedRowHeight 不能为 0,否则无法开启 self-sizing cells 功能");
- [self replaceMethodForDelegateIfNeeded:self.delegate];
+ [self replaceMethodForDelegateIfNeeded:(id)self.delegate];
// 在上面那一句 replaceMethodForDelegateIfNeeded 里可能修改了 delegate 里的一些方法,所以需要通过重新设置 delegate 来触发 tableView 读取新的方法。iOS 8 要先置空再设置才能生效。
if (@available(iOS 9.0, *)) {
self.delegate = self.delegate;
} else {
- id tempDelegate = self.delegate;
+ id tempDelegate = (id)self.delegate;
self.delegate = nil;
self.delegate = tempDelegate;
}
@@ -93,35 +93,33 @@ - (CGFloat)widthForCacheKey {
}
- (void)qmui_tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
- [tableView qmui_tableView:tableView willDisplayCell:cell forRowAtIndexPath:indexPath];
if (tableView.qmui_cacheCellHeightByKeyAutomatically) {
- id cachedKey = [((id)self) qmui_tableView:tableView cacheKeyForRowAtIndexPath:indexPath];
+ id cachedKey = [((id)tableView.delegate) qmui_tableView:tableView cacheKeyForRowAtIndexPath:indexPath];
[tableView.qmui_currentCellHeightKeyCache cacheHeight:CGRectGetHeight(cell.frame) forKey:cachedKey];
}
}
- (CGFloat)qmui_tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView.qmui_cacheCellHeightByKeyAutomatically) {
- id cachedKey = [((id)self) qmui_tableView:tableView cacheKeyForRowAtIndexPath:indexPath];
+ id cachedKey = [((id)tableView.delegate) qmui_tableView:tableView cacheKeyForRowAtIndexPath:indexPath];
if ([tableView.qmui_currentCellHeightKeyCache existsHeightForKey:cachedKey]) {
return [tableView.qmui_currentCellHeightKeyCache heightForKey:cachedKey];
}
// 由于 QMUICellHeightKeyCache 只对 self-sizing 的 cell 生效,所以这里返回这个值,以使用 self-sizing 效果
return UITableViewAutomaticDimension;
} else {
- // 对于开启过 qmui_cacheCellHeightByKeyAutomatically 然后又关闭的 class 就会走到这里,此时已经无法调用回之前被替换的方法的实现,所以直接使用 tableView.rowHeight
- // TODO: molice 最好应该在 replaceMethodForDelegateIfNeeded: 里判断在替换方法之前 delegate 是否已经有实现 heightForRow,如果有,则在这里调用回它自己的实现,如果没有,再使用 tableView.rowHeight,不然现在的做法会导致 delegate 里关闭了自动缓存的情况下就算实现了 heightForRow,也无法被调用。
+ // 对于开启过 qmui_cacheCellHeightByKeyAutomatically 然后又关闭的 class 就会走到这里,做个保护而已。理论上走到这个分支本身就是没有意义的。
return tableView.rowHeight;
}
}
-- (void)qmui_setDelegate:(id)delegate {
+- (void)qmui_setDelegate:(id)delegate {
[self replaceMethodForDelegateIfNeeded:delegate];
[self qmui_setDelegate:delegate];
}
static NSMutableSet *qmui_methodsReplacedClasses;
-- (void)replaceMethodForDelegateIfNeeded:(id)delegate {
+- (void)replaceMethodForDelegateIfNeeded:(id)delegate {
if (self.qmui_cacheCellHeightByKeyAutomatically && delegate) {
if (!qmui_methodsReplacedClasses) {
qmui_methodsReplacedClasses = [NSMutableSet set];
@@ -131,8 +129,71 @@ - (void)replaceMethodForDelegateIfNeeded:(id)delegate {
}
[qmui_methodsReplacedClasses addObject:NSStringFromClass(delegate.class)];
- ExchangeImplementationsInTwoClasses(delegate.class, @selector(tableView:willDisplayCell:forRowAtIndexPath:), self.class, @selector(qmui_tableView:willDisplayCell:forRowAtIndexPath:));
- ExchangeImplementationsInTwoClasses(delegate.class, @selector(tableView:heightForRowAtIndexPath:), self.class, @selector(qmui_tableView:heightForRowAtIndexPath:));
+ [self handleWillDisplayCellMethodForDelegate:delegate];
+ [self handleHeightForRowMethodForDelegate:delegate];
+
+ }
+}
+
+- (void)handleWillDisplayCellMethodForDelegate:(id)delegate {
+ // 如果 delegate 本身没有实现 tableView:willDisplayCell:forRowAtIndexPath:,则为它添加一个。
+ // 如果 delegate 已经有实现,则在调用完 delegate 自身的实现后,再调用我们自己的实现去存储计算后的 cell 高度
+ SEL willDisplayCellSelector = @selector(tableView:willDisplayCell:forRowAtIndexPath:);
+ Method willDisplayCellMethod = class_getInstanceMethod([self class], @selector(qmui_tableView:willDisplayCell:forRowAtIndexPath:));
+ IMP willDisplayCellIMP = method_getImplementation(willDisplayCellMethod);
+ void (*willDisplayCellFunction)(id, SEL, UITableView *, UITableViewCell *, NSIndexPath *);
+ willDisplayCellFunction = (void (*)(id, SEL, UITableView *, UITableViewCell *, NSIndexPath *))willDisplayCellIMP;
+
+ BOOL addedSuccessfully = class_addMethod(delegate.class, willDisplayCellSelector, willDisplayCellIMP, method_getTypeEncoding(willDisplayCellMethod));
+ if (!addedSuccessfully) {
+ OverrideImplementation([delegate class], willDisplayCellSelector, ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP originIMP) {
+ return ^(id delegateSelf, UITableView *tableView, UITableViewCell *cell, NSIndexPath *indexPath) {
+
+ // call super
+ void (*originSelectorIMP)(id, SEL, UITableView *, UITableViewCell *, NSIndexPath *);
+ originSelectorIMP = (void (*)(id, SEL, UITableView *, UITableViewCell *, NSIndexPath *))originIMP;
+ originSelectorIMP(delegateSelf, originCMD, tableView, cell, indexPath);
+
+ // avoid superclass
+ if (![delegateSelf isKindOfClass:originClass]) return;
+
+ // call QMUI
+ willDisplayCellFunction(delegateSelf, willDisplayCellSelector, tableView, cell, indexPath);
+ };
+ });
+ }
+}
+
+- (void)handleHeightForRowMethodForDelegate:(id)delegate {
+ // 如果 delegate 本身没有实现 tableView:heightForRowAtIndexPath:,则为它添加一个。
+ // 如果 delegate 已经有实现,则优先拿它的实现的值来 return,如果它的值小于0(例如-1),则认为它想用 QMUICellHeightKeyCache 的计算,此时再 return 我们自己的计算结果
+ SEL heightForRowSelector = @selector(tableView:heightForRowAtIndexPath:);
+ Method heightForRowMethod = class_getInstanceMethod([self class], @selector(qmui_tableView:heightForRowAtIndexPath:));
+ IMP heightForRowIMP = method_getImplementation(heightForRowMethod);
+ CGFloat (*heightForRowFunction)(id, SEL, UITableView *, NSIndexPath *);
+ heightForRowFunction = (CGFloat (*)(id, SEL, UITableView *, NSIndexPath *))heightForRowIMP;
+
+ BOOL addedSuccessfully = class_addMethod([delegate class], heightForRowSelector, heightForRowIMP, method_getTypeEncoding(heightForRowMethod));
+ if (!addedSuccessfully) {
+ OverrideImplementation([delegate class], heightForRowSelector, ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP originIMP) {
+ return ^CGFloat(id delegateSelf, UITableView *tableView, NSIndexPath *indexPath) {
+
+ // call super
+ CGFloat (*originSelectorIMP)(id, SEL, UITableView *, NSIndexPath *);
+ originSelectorIMP = (CGFloat (*)(id, SEL, UITableView *, NSIndexPath *))originIMP;
+ CGFloat result = originSelectorIMP(delegateSelf, originCMD, tableView, indexPath);
+
+ // avoid superclass
+ if (![delegateSelf isKindOfClass:originClass]) return result;
+
+ if (result >= 0) {
+ return result;
+ }
+
+ // call QMUI
+ return heightForRowFunction(delegateSelf, heightForRowSelector, tableView, indexPath);
+ };
+ });
}
}
diff --git a/QMUIKit/QMUIComponents/QMUICellSizeKeyCache/UICollectionView+QMUICellSizeKeyCache.h b/QMUIKit/QMUIComponents/QMUICellSizeKeyCache/UICollectionView+QMUICellSizeKeyCache.h
index 85d93627..cab072c7 100644
--- a/QMUIKit/QMUIComponents/QMUICellSizeKeyCache/UICollectionView+QMUICellSizeKeyCache.h
+++ b/QMUIKit/QMUIComponents/QMUICellSizeKeyCache/UICollectionView+QMUICellSizeKeyCache.h
@@ -17,6 +17,7 @@
- (nonnull id)qmui_collectionView:(nonnull UICollectionView *)collectionView cacheKeyForItemAtIndexPath:(nonnull NSIndexPath *)indexPath;
@end
+/// 注意,这个类的功能暂无法使用
@interface UICollectionView (QMUICellSizeKeyCache)
/// 控制是否要自动缓存 cell 的高度,默认为 NO
diff --git a/QMUIKit/QMUIComponents/QMUICellSizeKeyCache/UICollectionView+QMUICellSizeKeyCache.m b/QMUIKit/QMUIComponents/QMUICellSizeKeyCache/UICollectionView+QMUICellSizeKeyCache.m
index 4730ea2b..4a12a1eb 100644
--- a/QMUIKit/QMUIComponents/QMUICellSizeKeyCache/UICollectionView+QMUICellSizeKeyCache.m
+++ b/QMUIKit/QMUIComponents/QMUICellSizeKeyCache/UICollectionView+QMUICellSizeKeyCache.m
@@ -182,9 +182,6 @@ - (void)replaceMethodForDelegateIfNeeded:(id)delegate
return;
}
[qmui_methodsReplacedClasses addObject:NSStringFromClass(delegate.class)];
-
- ExchangeImplementationsInTwoClasses(delegate.class, @selector(collectionView:willDisplayCell:forItemAtIndexPath:), self.class, @selector(qmui_collectionView:willDisplayCell:forItemAtIndexPath:));
-// ExchangeImplementationsInTwoClasses(delegate.class, @selector(collectionView:layout:sizeForItemAtIndexPath:), self.class, @selector(qmui_collectionView:layout:sizeForItemAtIndexPath:));
}
}
diff --git a/QMUIKit/QMUIComponents/QMUIDialogViewController.m b/QMUIKit/QMUIComponents/QMUIDialogViewController.m
index a16c887a..45bcb421 100644
--- a/QMUIKit/QMUIComponents/QMUIDialogViewController.m
+++ b/QMUIKit/QMUIComponents/QMUIDialogViewController.m
@@ -401,7 +401,7 @@ - (void)hideWithAnimated:(BOOL)animated completion:(void (^)(BOOL))completion {
#pragma mark -
-- (CGSize)preferredContentSizeInModalPresentationViewController:(QMUIModalPresentationViewController *)controller limitSize:(CGSize)limitSize {
+- (CGSize)preferredContentSizeInModalPresentationViewController:(QMUIModalPresentationViewController *)controller keyboardHeight:(CGFloat)keyboardHeight limitSize:(CGSize)limitSize {
if (!self.hasCustomContentView) {
return limitSize;
}
@@ -601,7 +601,7 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
#pragma mark -
-- (CGSize)preferredContentSizeInModalPresentationViewController:(QMUIModalPresentationViewController *)controller limitSize:(CGSize)limitSize {
+- (CGSize)preferredContentSizeInModalPresentationViewController:(QMUIModalPresentationViewController *)controller keyboardHeight:(CGFloat)keyboardHeight limitSize:(CGSize)limitSize {
CGFloat contentViewVerticalMargin = UIEdgeInsetsGetVerticalValue(self.contentViewMargins);
CGFloat footerHeight = !self.footerView.hidden ? CGRectGetHeight(self.footerView.frame) : 0;
CGFloat tableViewLimitHeight = limitSize.height - CGRectGetHeight(self.headerView.frame) - footerHeight - contentViewVerticalMargin;
@@ -745,7 +745,7 @@ - (void)addSubmitButtonWithText:(NSString *)buttonText block:(void (^)(__kindof
#pragma mark -
-- (CGSize)preferredContentSizeInModalPresentationViewController:(QMUIModalPresentationViewController *)controller limitSize:(CGSize)limitSize {
+- (CGSize)preferredContentSizeInModalPresentationViewController:(QMUIModalPresentationViewController *)controller keyboardHeight:(CGFloat)keyboardHeight limitSize:(CGSize)limitSize {
CGFloat textFieldHeight = self.textFieldLabel.hidden ? 56.0 : 25.0; // 25.0 考虑了行高导致的 offsetoffset
CGFloat textFieldTitleHeight = 29.0;
diff --git a/QMUIKit/QMUIComponents/QMUIEmptyView.m b/QMUIKit/QMUIComponents/QMUIEmptyView.m
index 440b6ca9..a71bcd7d 100644
--- a/QMUIKit/QMUIComponents/QMUIEmptyView.m
+++ b/QMUIKit/QMUIComponents/QMUIEmptyView.m
@@ -57,7 +57,7 @@ - (void)didInitialize {
self.scrollView.showsVerticalScrollIndicator = NO;
self.scrollView.showsHorizontalScrollIndicator = NO;
self.scrollView.scrollsToTop = NO;
- self.scrollView.contentInset = UIEdgeInsetsMake(0, 10, 0, 10); // 避免 label 直接撑满到屏幕两边,不好看
+ self.scrollView.contentInset = UIEdgeInsetsMake(0, 16, 0, 16);
[self addSubview:self.scrollView];
_contentView = [[UIView alloc] init];
diff --git a/QMUIKit/QMUIComponents/QMUIKeyboardManager.h b/QMUIKit/QMUIComponents/QMUIKeyboardManager.h
index 515f4446..b6747d1f 100644
--- a/QMUIKit/QMUIComponents/QMUIKeyboardManager.h
+++ b/QMUIKit/QMUIComponents/QMUIKeyboardManager.h
@@ -68,7 +68,7 @@
+ (CGRect)convertKeyboardRect:(CGRect)rect toView:(UIView *)view;
/**
- * 获取键盘到顶部到相对于view底部的距离,这个值在某些情况下会等于endFrame.size.height或者visiableKeyboardHeight,不过在iPad浮动键盘的时候就包括了底部的空隙。所以建议使用这个方法。
+ * 获取键盘到顶部到相对于view底部的距离,这个值在某些情况下会等于endFrame.size.height或者visibleKeyboardHeight,不过在iPad浮动键盘的时候就包括了底部的空隙。所以建议使用这个方法。
*/
+ (CGFloat)distanceFromMinYToBottomInView:(UIView *)view keyboardRect:(CGRect)rect;
@@ -107,7 +107,7 @@
/**
* 当前键盘高度键盘的可见高度
*/
-+ (CGFloat)visiableKeyboardHeight;
++ (CGFloat)visibleKeyboardHeight;
@end
diff --git a/QMUIKit/QMUIComponents/QMUIKeyboardManager.m b/QMUIKit/QMUIComponents/QMUIKeyboardManager.m
index 6bbf968f..f4e51797 100644
--- a/QMUIKit/QMUIComponents/QMUIKeyboardManager.m
+++ b/QMUIKit/QMUIComponents/QMUIKeyboardManager.m
@@ -10,13 +10,15 @@
#import "QMUICore.h"
#import "QMUILog.h"
+// iOS 8 下当键盘已经升起的时候再聚焦另一个输入框,此时系统不会再发出键盘通知,导致一些逻辑不准确,这里修复系统这个 bug。iOS 9 及以后没问题。
+// 对应的 issue:https://github.com/QMUI/QMUI_iOS/issues/348
+static QMUIKeyboardManager *kKeyboardManagerInstance;
@interface QMUIKeyboardManager ()
@property(nonatomic, strong) NSMutableArray *targetResponderValues;
-@property(nonatomic, strong) QMUIKeyboardUserInfo *keyboardMoveUserInfo;
-@property(nonatomic, assign) CGRect keyboardMoveBeginRect;
+@property(nonatomic, strong) QMUIKeyboardUserInfo *lastUserInfo;
@property(nonatomic, weak) UIResponder *currentResponder;
@property(nonatomic, weak) UIResponder *currentResponderWhenResign;
@@ -60,6 +62,26 @@ + (void)load {
- (BOOL)keyboardManager_becomeFirstResponder {
self.keyboardManager_isFirstResponder = YES;
+ if (@available(iOS 9.0, *)) {
+ return [self keyboardManager_becomeFirstResponder];
+ }
+
+ // iOS 8 下如果键盘已经在显示的时候,另一个输入框被聚焦,升起键盘,此时系统不会再发键盘事件给你,但 iOS 9 及以后会发送,所以这里主动给输入框发送键盘事件
+ // 对应这个 issue:https://github.com/QMUI/QMUI_iOS/issues/348
+ BOOL isTextInputComponents = [self isKindOfClass:[UITextField class]] || [self isKindOfClass:[UITextView class]];
+ BOOL isAlreadyFirstResponder = self.isFirstResponder;
+ BOOL isKeyboardVisible = [QMUIKeyboardManager isKeyboardVisible];
+ if (isTextInputComponents && !isAlreadyFirstResponder && isKeyboardVisible) {
+ BOOL result = [self keyboardManager_becomeFirstResponder];
+ if (result) {
+ NSDictionary *userInfo = kKeyboardManagerInstance.lastUserInfo.notification.userInfo;
+ [[NSNotificationCenter defaultCenter] postNotificationName:UIKeyboardWillChangeFrameNotification object:self userInfo:userInfo];
+ [[NSNotificationCenter defaultCenter] postNotificationName:UIKeyboardWillShowNotification object:self userInfo:userInfo];
+ [[NSNotificationCenter defaultCenter] postNotificationName:UIKeyboardDidChangeFrameNotification object:self userInfo:userInfo];
+ [[NSNotificationCenter defaultCenter] postNotificationName:UIKeyboardDidShowNotification object:self userInfo:userInfo];
+ }
+ return result;
+ }
return [self keyboardManager_becomeFirstResponder];
}
@@ -148,11 +170,11 @@ - (CGFloat)heightInView:(UIView *)view {
return [self height];
}
CGRect keyboardRect = [QMUIKeyboardManager convertKeyboardRect:_endFrame toView:view];
- CGRect visiableRect = CGRectIntersection(view.bounds, keyboardRect);
- if (CGRectIsNull(visiableRect)) {
+ CGRect visibleRect = CGRectIntersection(view.bounds, keyboardRect);
+ if (CGRectIsNull(visibleRect)) {
return 0;
}
- return visiableRect.size.height;
+ return visibleRect.size.height;
}
- (CGRect)beginFrame {
@@ -178,105 +200,33 @@ - (UIViewAnimationOptions)animationOptions {
@end
-@interface QMUIKeyboardViewFrameObserver : NSObject
-
-@property(nonatomic, copy) void (^keyboardViewChangeFrameBlock)(UIView *keyboardView);
+/**
+ 1. 系统键盘app启动第一次使用键盘的时候,会调用两轮键盘通知事件,之后就只会调用一次。而搜狗等第三方输入法的键盘,目前发现每次都会调用三次键盘通知事件。总之,键盘的通知事件是不确定的。
-- (void)addToKeyboardView:(UIView *)keyboardView;
-+ (instancetype)observerForView:(UIView *)keyboardView;
+ 2. 搜狗键盘可以修改键盘的高度,在修改键盘高度之后,会调用键盘的keyboardWillChangeFrameNotification和keyboardWillShowNotification通知。
-@end
+ 3. 如果从一个聚焦的输入框直接聚焦到另一个输入框,会调用前一个输入框的keyboardWillChangeFrameNotification,在调用后一个输入框的keyboardWillChangeFrameNotification,最后调用后一个输入框的keyboardWillShowNotification(如果此时是浮动键盘,那么后一个输入框的keyboardWillShowNotification不会被调用;)。
-static char kAssociatedObjectKey_KeyboardViewFrameObserver;
+ 4. iPad可以变成浮动键盘,固定->浮动:会调用keyboardWillChangeFrameNotification和keyboardWillHideNotification;浮动->固定:会调用keyboardWillChangeFrameNotification和keyboardWillShowNotification;浮动键盘在移动的时候只会调用keyboardWillChangeFrameNotification通知,并且endFrame为zero,fromFrame不为zero,而是移动前键盘的frame。浮动键盘在聚焦和失焦的时候只会调用keyboardWillChangeFrameNotification,不会调用show和hide的notification。
-@implementation QMUIKeyboardViewFrameObserver {
- __unsafe_unretained UIView *_keyboardView;
-}
+ 5. iPad可以拆分为左右的小键盘,小键盘的通知具体基本跟浮动键盘一样。
-- (void)addToKeyboardView:(UIView *)keyboardView {
- if (_keyboardView == keyboardView) {
- return;
- }
- if (_keyboardView) {
- [self removeFrameObserver];
- objc_setAssociatedObject(_keyboardView, &kAssociatedObjectKey_KeyboardViewFrameObserver, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- _keyboardView = keyboardView;
- if (keyboardView) {
- [self addFrameObserver];
- }
- objc_setAssociatedObject(keyboardView, &kAssociatedObjectKey_KeyboardViewFrameObserver, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
-}
-
-- (void)addFrameObserver {
- if (!_keyboardView) {
- return;
- }
- [_keyboardView addObserver:self forKeyPath:@"frame" options:kNilOptions context:NULL];
- [_keyboardView addObserver:self forKeyPath:@"center" options:kNilOptions context:NULL];
- [_keyboardView addObserver:self forKeyPath:@"bounds" options:kNilOptions context:NULL];
- [_keyboardView addObserver:self forKeyPath:@"transform" options:kNilOptions context:NULL];
-}
-
-- (void)removeFrameObserver {
- [_keyboardView removeObserver:self forKeyPath:@"frame"];
- [_keyboardView removeObserver:self forKeyPath:@"center"];
- [_keyboardView removeObserver:self forKeyPath:@"bounds"];
- [_keyboardView removeObserver:self forKeyPath:@"transform"];
- _keyboardView = nil;
-}
-
-- (void)dealloc {
- [self removeFrameObserver];
-}
-
-+ (instancetype)observerForView:(UIView *)keyboardView {
- if (!keyboardView) {
- return nil;
- }
- return objc_getAssociatedObject(keyboardView, &kAssociatedObjectKey_KeyboardViewFrameObserver);
-}
-
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
- if (![keyPath isEqualToString:@"frame"] &&
- ![keyPath isEqualToString:@"center"] &&
- ![keyPath isEqualToString:@"bounds"] &&
- ![keyPath isEqualToString:@"transform"]) {
- return;
- }
- if ([[change objectForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue]) {
- return;
- }
- if ([[change objectForKey:NSKeyValueChangeKindKey] integerValue] != NSKeyValueChangeSetting) {
- return;
- }
- id newValue = [change objectForKey:NSKeyValueChangeNewKey];
- if (newValue == [NSNull null]) { newValue = nil; }
- if (self.keyboardViewChangeFrameBlock) {
- self.keyboardViewChangeFrameBlock(_keyboardView);
- }
-}
-
-@end
+ 6. iPad可以外接键盘,外接键盘之后屏幕上就没有虚拟键盘了,但是当我们输入文字的时候,发现底部还是有一条灰色的候选词,条东西也是键盘,它也会触发跟虚拟键盘一样的通知事件。如果点击这条候选词右边的向下箭头,则可以完全隐藏虚拟键盘,这个时候如果失焦再聚焦发现还是没有这条候选词,也就是键盘完全不出来了,如果输入文字,候选词才会重新出来。总结来说就是这条候选词是可以关闭的,关闭之后只有当下次输入才会重新出现。(聚焦和失焦都只调用keyboardWillChangeFrameNotification和keyboardWillHideNotification通知,而且frame始终不变,都是在屏幕下面)
+ 7. iOS8 hide 之后高度变成0了,keyboardWillHideNotification还是正常的,所以建议不要使用键盘高度来做动画,而是用键盘的y值;在show和hide的时候endFrame会出现一些奇怪的中间值,但最终值是对的;两个输入框切换聚焦,iOS8不会触发任何键盘通知;iOS8的浮动切换正常;
+ 8. iOS8在 固定->浮动 的过程中,后面的keyboardWillChangeFrameNotification和keyboardWillHideNotification里面的endFrame是正确的,而iOS10和iOS9是错的,iOS9的y值是键盘的MaxY,而iOS10的y值是隐藏状态下的y,也就是屏幕高度。所以iOS9和iOS10需要在keyboardDidChangeFrameNotification里面重新刷新一下。
+ */
@implementation QMUIKeyboardManager
-// 1、系统键盘app启动第一次使用键盘的时候,会调用两轮键盘通知事件,之后就只会调用一次。而搜狗等第三方输入法的键盘,目前发现每次都会调用三次键盘通知事件。总之,键盘的通知事件是不确定的。
-
-// 2、搜狗键盘可以修改键盘的高度,在修改键盘高度之后,会调用键盘的keyboardWillChangeFrameNotification和keyboardWillShowNotification通知。
-
-// 3、如果从一个聚焦的输入框直接聚焦到另一个输入框,会调用前一个输入框的keyboardWillChangeFrameNotification,在调用后一个输入框的keyboardWillChangeFrameNotification,最后调用后一个输入框的keyboardWillShowNotification(如果此时是浮动键盘,那么后一个输入框的keyboardWillShowNotification不会被调用;)。
-
-// 4、iPad可以变成浮动键盘,固定->浮动:会调用keyboardWillChangeFrameNotification和keyboardWillHideNotification;浮动->固定:会调用keyboardWillChangeFrameNotification和keyboardWillShowNotification;浮动键盘在移动的时候只会调用keyboardWillChangeFrameNotification通知,并且endFrame为zero,fromFrame不为zero,而是移动前键盘的frame。浮动键盘在聚焦和失焦的时候只会调用keyboardWillChangeFrameNotification,不会调用show和hide的notification。
-
-// 5、iPad可以拆分为左右的小键盘,小键盘的通知具体基本跟浮动键盘一样。
-
-// 6、iPad可以外接键盘,外接键盘之后屏幕上就没有虚拟键盘了,但是当我们输入文字的时候,发现底部还是有一条灰色的候选词,条东西也是键盘,它也会触发跟虚拟键盘一样的通知事件。如果点击这条候选词右边的向下箭头,则可以完全隐藏虚拟键盘,这个时候如果失焦再聚焦发现还是没有这条候选词,也就是键盘完全不出来了,如果输入文字,候选词才会重新出来。总结来说就是这条候选词是可以关闭的,关闭之后只有当下次输入才会重新出现。(聚焦和失焦都只调用keyboardWillChangeFrameNotification和keyboardWillHideNotification通知,而且frame始终不变,都是在屏幕下面)
-
-// 7、iOS8 hide 之后高度变成0了,keyboardWillHideNotification还是正常的,所以建议不要使用键盘高度来做动画,而是用键盘的y值;在show和hide的时候endFrame会出现一些奇怪的中间值,最终值是对的;两个输入框切换聚焦,iOS8不会触发任何键盘通知;iOS8的浮动切换正常;
-
-// 8、iOS8在 固定->浮动 的过程中,后面的keyboardWillChangeFrameNotification和keyboardWillHideNotification里面的endFrame是正确的,而iOS10和iOS9是错的,iOS9的y值是键盘的MaxY,而iOS10的y值是隐藏状态下的y,也就是屏幕高度。所以iOS9和iOS10需要在keyboardDidChangeFrameNotification里面重新刷新一下。
++ (void)initialize {
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ if (!kKeyboardManagerInstance) {
+ kKeyboardManagerInstance = [[QMUIKeyboardManager alloc] initWithDelegate:nil];
+ }
+ });
+}
- (instancetype)init {
NSAssert(NO, @"请使用initWithDelegate:初始化");
@@ -382,6 +332,7 @@ - (void)keyboardWillShowNotification:(NSNotification *)notification {
}
QMUIKeyboardUserInfo *userInfo = [self newUserInfoWithNotification:notification];
+ self.lastUserInfo = userInfo;
userInfo.targetResponder = self.currentResponder ?: nil;
if (self.delegateEnabled && [self.delegate respondsToSelector:@selector(keyboardWillShowWithUserInfo:)]) {
@@ -401,6 +352,7 @@ - (void)keyboardDidShowNotification:(NSNotification *)notification {
}
QMUIKeyboardUserInfo *userInfo = [self newUserInfoWithNotification:notification];
+ self.lastUserInfo = userInfo;
userInfo.targetResponder = self.currentResponder ?: nil;
id firstResponder = [[UIApplication sharedApplication].keyWindow qmui_findFirstResponder];
@@ -429,6 +381,7 @@ - (void)keyboardWillHideNotification:(NSNotification *)notification {
}
QMUIKeyboardUserInfo *userInfo = [self newUserInfoWithNotification:notification];
+ self.lastUserInfo = userInfo;
userInfo.targetResponder = self.currentResponder ?: nil;
if (self.delegateEnabled && [self.delegate respondsToSelector:@selector(keyboardWillHideWithUserInfo:)]) {
@@ -448,6 +401,7 @@ - (void)keyboardDidHideNotification:(NSNotification *)notification {
}
QMUIKeyboardUserInfo *userInfo = [self newUserInfoWithNotification:notification];
+ self.lastUserInfo = userInfo;
userInfo.targetResponder = self.currentResponder ?: nil;
if ([self shouldReceiveHideNotification]) {
@@ -473,6 +427,7 @@ - (void)keyboardWillChangeFrameNotification:(NSNotification *)notification {
}
QMUIKeyboardUserInfo *userInfo = [self newUserInfoWithNotification:notification];
+ self.lastUserInfo = userInfo;
if ([self shouldReceiveShowNotification]) {
userInfo.targetResponder = self.currentResponder ?: nil;
@@ -499,6 +454,7 @@ - (void)keyboardDidChangeFrameNotification:(NSNotification *)notification {
}
QMUIKeyboardUserInfo *userInfo = [self newUserInfoWithNotification:notification];
+ self.lastUserInfo = userInfo;
if ([self shouldReceiveShowNotification]) {
userInfo.targetResponder = self.currentResponder ?: nil;
@@ -543,92 +499,6 @@ - (BOOL)shouldReceiveHideNotification {
}
}
-#pragma mark - iPad浮动键盘
-
-- (void)addFrameObserverIfNeeded {
- if (![self.class keyboardView]) {
- return;
- }
- __weak __typeof(self)weakSelf = self;
- QMUIKeyboardViewFrameObserver *observer = [QMUIKeyboardViewFrameObserver observerForView:[self.class keyboardView]];
- if (!observer) {
- observer = [[QMUIKeyboardViewFrameObserver alloc] init];
- observer.keyboardViewChangeFrameBlock = ^(UIView *keyboardView) {
- [weakSelf keyboardDidChangedFrame:keyboardView];
- };
- [observer addToKeyboardView:[self.class keyboardView]];
- // 手动调用第一次
- [self keyboardDidChangedFrame:[self.class keyboardView]];
- }
-}
-
-- (void)keyboardDidChangedFrame:(UIView *)keyboardView {
-
- if (keyboardView != [self.class keyboardView]) {
- return;
- }
-
- // 也需要判断targetResponder
- if (![self shouldReceiveShowNotification] && ![self shouldReceiveHideNotification]) {
- return;
- }
-
- if (self.delegateEnabled && [self.delegate respondsToSelector:@selector(keyboardWillChangeFrameWithUserInfo:)]) {
-
- UIWindow *keyboardWindow = keyboardView.window;
-
- if (self.keyboardMoveBeginRect.size.width == 0 && self.keyboardMoveBeginRect.size.height == 0) {
- // 第一次需要初始化
- self.keyboardMoveBeginRect = CGRectMake(0, keyboardWindow.bounds.size.height, keyboardWindow.bounds.size.width, 0);
- }
-
- CGRect endFrame = CGRectZero;
- if (keyboardWindow) {
- endFrame = [keyboardWindow convertRect:keyboardView.frame toWindow:nil];
- } else {
- endFrame = keyboardView.frame;
- }
-
- // 自己构造一个QMUIKeyboardUserInfo,一些属性使用之前最后一个keyboardUserInfo的值
- QMUIKeyboardUserInfo *keyboardMoveUserInfo = [[QMUIKeyboardUserInfo alloc] init];
- keyboardMoveUserInfo.keyboardManager = self;
- keyboardMoveUserInfo.targetResponder = self.keyboardMoveUserInfo ? self.keyboardMoveUserInfo.targetResponder : nil;
- keyboardMoveUserInfo.animationDuration = self.keyboardMoveUserInfo ? self.keyboardMoveUserInfo.animationDuration : 0.25;
- keyboardMoveUserInfo.animationCurve = self.keyboardMoveUserInfo ? self.keyboardMoveUserInfo.animationCurve : 7;
- keyboardMoveUserInfo.animationOptions = self.keyboardMoveUserInfo ? self.keyboardMoveUserInfo.animationOptions : keyboardMoveUserInfo.animationCurve<<16;
- keyboardMoveUserInfo.beginFrame = self.keyboardMoveBeginRect;
- keyboardMoveUserInfo.endFrame = endFrame;
-
- if (self.debug) {
- QMUILog(NSStringFromClass(self.class), @"keyboardDidMoveNotification - %@", self);
- }
-
- [self.delegate keyboardWillChangeFrameWithUserInfo:keyboardMoveUserInfo];
-
- self.keyboardMoveBeginRect = endFrame;
-
- if (self.currentResponder) {
- UIWindow *mainWindow = [UIApplication sharedApplication].keyWindow ?: [[UIApplication sharedApplication] windows].firstObject;
- if (mainWindow) {
- CGRect keyboardRect = keyboardMoveUserInfo.endFrame;
- CGFloat distanceFromBottom = [QMUIKeyboardManager distanceFromMinYToBottomInView:mainWindow keyboardRect:keyboardRect];
- if (distanceFromBottom < keyboardRect.size.height) {
- if (!self.currentResponder.keyboardManager_isFirstResponder) {
- // willHide
- self.currentResponder = nil;
- }
- } else if (distanceFromBottom > keyboardRect.size.height && !self.currentResponder.isFirstResponder) {
- if (!self.currentResponder.keyboardManager_isFirstResponder) {
- // 浮动
- self.currentResponder = nil;
- }
- }
- }
- }
-
- }
-}
-
#pragma mark - 工具方法
+ (void)animateWithAnimated:(BOOL)animated keyboardUserInfo:(QMUIKeyboardUserInfo *)keyboardUserInfo animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion {
@@ -654,7 +524,7 @@ + (void)animateWithAnimated:(BOOL)animated keyboardUserInfo:(QMUIKeyboardUserInf
+ (void)handleKeyboardNotificationWithUserInfo:(QMUIKeyboardUserInfo *)keyboardUserInfo showBlock:(void (^)(QMUIKeyboardUserInfo *keyboardUserInfo))showBlock hideBlock:(void (^)(QMUIKeyboardUserInfo *keyboardUserInfo))hideBlock {
// 专门处理 iPad Pro 在键盘完全不显示的情况(不会调用willShow,所以通过是否focus来判断)
- if ([QMUIKeyboardManager visiableKeyboardHeight] <= 0 && !keyboardUserInfo.isTargetResponderFocused) {
+ if ([QMUIKeyboardManager visibleKeyboardHeight] <= 0 && !keyboardUserInfo.isTargetResponderFocused) {
if (hideBlock) {
hideBlock(keyboardUserInfo);
}
@@ -797,10 +667,10 @@ + (BOOL)isKeyboardVisible {
return NO;
}
CGRect rect = CGRectIntersection(keyboardWindow.bounds, keyboardView.frame);
- if (CGRectIsNull(rect) || CGRectIsInfinite(rect)) {
- return NO;
+ if (CGRectIsValidated(rect) && !CGRectIsEmpty(rect)) {
+ return YES;
}
- return rect.size.width > 0 && rect.size.height > 0;
+ return NO;
}
+ (CGRect)currentKeyboardFrame {
@@ -816,17 +686,17 @@ + (CGRect)currentKeyboardFrame {
}
}
-+ (CGFloat)visiableKeyboardHeight {
++ (CGFloat)visibleKeyboardHeight {
UIView *keyboardView = [self keyboardView];
UIWindow *keyboardWindow = keyboardView.window;
if (!keyboardView || !keyboardWindow) {
return 0;
} else {
- CGRect visiableRect = CGRectIntersection(keyboardWindow.bounds, keyboardView.frame);
- if (CGRectIsNull(visiableRect)) {
- return 0;
+ CGRect visibleRect = CGRectIntersection(keyboardWindow.bounds, keyboardView.frame);
+ if (CGRectIsValidated(visibleRect)) {
+ return CGRectGetHeight(visibleRect);
}
- return visiableRect.size.height;
+ return 0;
}
}
@@ -906,14 +776,6 @@ - (void)setQmui_keyboardDidChangeFrameNotificationBlock:(void (^)(QMUIKeyboardUs
return objc_getAssociatedObject(self, _cmd);
}
-//- (void)setQmui_keyboardManager:(QMUIKeyboardManager *)keyboardManager {
-// objc_setAssociatedObject(self, @selector(qmui_keyboardManager), keyboardManager, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
-//}
-//
-//- (QMUIKeyboardManager *)qmui_keyboardManager {
-// return objc_getAssociatedObject(self, _cmd);
-//}
-
- (void)initKeyboardManagerIfNeeded {
if (!self.qmui_keyboardManager) {
self.qmui_keyboardManager = [[QMUIKeyboardManager alloc] initWithDelegate:self];
@@ -1035,14 +897,6 @@ - (void)setQmui_keyboardDidChangeFrameNotificationBlock:(void (^)(QMUIKeyboardUs
return objc_getAssociatedObject(self, _cmd);
}
-//- (void)setQmui_keyboardManager:(QMUIKeyboardManager *)keyboardManager {
-// objc_setAssociatedObject(self, @selector(qmui_keyboardManager), keyboardManager, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
-//}
-//
-//- (QMUIKeyboardManager *)qmui_keyboardManager {
-// return objc_getAssociatedObject(self, _cmd);
-//}
-
- (void)initKeyboardManagerIfNeeded {
if (!self.qmui_keyboardManager) {
self.qmui_keyboardManager = [[QMUIKeyboardManager alloc] initWithDelegate:self];
diff --git a/QMUIKit/QMUIComponents/QMUILogManagerViewController.m b/QMUIKit/QMUIComponents/QMUILogManagerViewController.m
index 1b1dda71..b01037d6 100644
--- a/QMUIKit/QMUIComponents/QMUILogManagerViewController.m
+++ b/QMUIKit/QMUIComponents/QMUILogManagerViewController.m
@@ -153,24 +153,24 @@ - (void)handleMenuItemEvent {
menuView.maximumWidth = 124;
menuView.safetyMarginsOfSuperview = UIEdgeInsetsSetRight(menuView.safetyMarginsOfSuperview, 6);
menuView.items = @[
- [QMUIPopupMenuItem itemWithImage:nil title:@"开启全部" handler:^{
+ [QMUIPopupMenuItem itemWithImage:nil title:@"开启全部" handler:^(QMUIPopupMenuView *aMenuView, QMUIPopupMenuItem *aItem) {
for (NSString *logName in self.allNames) {
[[QMUILogger sharedInstance].logNameManager setEnabled:YES forLogName:logName];
}
[self reloadData];
- [menuView hideWithAnimated:YES];
+ [aMenuView hideWithAnimated:YES];
}],
- [QMUIPopupMenuItem itemWithImage:nil title:@"禁用全部" handler:^{
+ [QMUIPopupMenuItem itemWithImage:nil title:@"禁用全部" handler:^(QMUIPopupMenuView *aMenuView, QMUIPopupMenuItem *aItem) {
for (NSString *logName in self.allNames) {
[[QMUILogger sharedInstance].logNameManager setEnabled:NO forLogName:logName];
}
[self reloadData];
- [menuView hideWithAnimated:YES];
+ [aMenuView hideWithAnimated:YES];
}],
- [QMUIPopupMenuItem itemWithImage:nil title:@"清空全部" handler:^{
+ [QMUIPopupMenuItem itemWithImage:nil title:@"清空全部" handler:^(QMUIPopupMenuView *aMenuView, QMUIPopupMenuItem *aItem) {
[[QMUILogger sharedInstance].logNameManager removeAllNames];
[self reloadData];
- [menuView hideWithAnimated:YES];
+ [aMenuView hideWithAnimated:YES];
}]];
[menuView layoutWithTargetView:self.navigationItem.rightBarButtonItem.qmui_view];
[menuView showWithAnimated:YES];
diff --git a/QMUIKit/QMUIComponents/QMUIModalPresentationViewController.h b/QMUIKit/QMUIComponents/QMUIModalPresentationViewController.h
index b2c725c0..14168c4d 100644
--- a/QMUIKit/QMUIComponents/QMUIModalPresentationViewController.h
+++ b/QMUIKit/QMUIComponents/QMUIModalPresentationViewController.h
@@ -24,10 +24,11 @@ typedef NS_ENUM(NSUInteger, QMUIModalPresentationAnimationStyle) {
/**
* 当浮层以 UIViewController 的形式展示(而非 UIView),并且使用 modalController 提供的默认布局时,则可通过这个方法告诉 modalController 当前浮层期望的大小
* @param controller 当前的modalController
- * @param limitSize 浮层最大的宽高,由当前 modalController 的大小及 `contentViewMargins`、`maximumContentViewWidth` 决定
+ * @param keyboardHeight 当前的键盘高度,如果键盘降下,则为0
+ * @param limitSize 浮层最大的宽高,由当前 modalController 的大小及 `contentViewMargins`、`maximumContentViewWidth` 和键盘高度决定
* @return 返回浮层在 `limitSize` 限定内的大小,如果业务自身不需要限制宽度/高度,则为 width/height 返回 `CGFLOAT_MAX` 即可
*/
-- (CGSize)preferredContentSizeInModalPresentationViewController:(QMUIModalPresentationViewController *)controller limitSize:(CGSize)limitSize;
+- (CGSize)preferredContentSizeInModalPresentationViewController:(QMUIModalPresentationViewController *)controller keyboardHeight:(CGFloat)keyboardHeight limitSize:(CGSize)limitSize;
@end
@@ -82,8 +83,8 @@ typedef NS_ENUM(NSUInteger, QMUIModalPresentationAnimationStyle) {
* @endcode
*
* 默认的布局会将浮层居中显示,浮层的大小可通过接口控制:
- * 1. 如果是用 `contentViewController`,则可通过 `preferredContentSizeInModalPresentationViewController:limitSize:` 来设置
- * 2. 如果使用 `contentView`,或者使用 `contentViewController` 但没实现 `preferredContentSizeInModalPresentationViewController:limitSize:`,则调用`contentView`的`sizeThatFits:`方法获取大小。
+ * 1. 如果是用 `contentViewController`,则可通过 `preferredContentSizeInModalPresentationViewController:keyboardHeight:limitSize:` 来设置
+ * 2. 如果使用 `contentView`,或者使用 `contentViewController` 但没实现 `preferredContentSizeInModalPresentationViewController:keyboardHeight:limitSize:`,则调用`contentView`的`sizeThatFits:`方法获取大小。
* 3. 浮层大小会受 `maximumContentViewWidth` 属性的限制,以及 `contentViewMargins` 属性的影响。
*
* 通过`layoutBlock`、`showingAnimation`、`hidingAnimation`可设置自定义的布局、打开及隐藏的动画,并允许你适配键盘升起时的场景。
diff --git a/QMUIKit/QMUIComponents/QMUIModalPresentationViewController.m b/QMUIKit/QMUIComponents/QMUIModalPresentationViewController.m
index 147accf2..db7b9eb1 100644
--- a/QMUIKit/QMUIComponents/QMUIModalPresentationViewController.m
+++ b/QMUIKit/QMUIComponents/QMUIModalPresentationViewController.m
@@ -303,6 +303,7 @@ - (void)viewWillDisappear:(BOOL)animated {
- (void)updateLayout {
if ([self isViewLoaded]) {
[self.view setNeedsLayout];
+ [self.view layoutIfNeeded];
}
}
@@ -511,8 +512,8 @@ - (CGRect)contentViewFrameForShowing {
CGSize contentViewContainerSize = CGSizeMake(CGRectGetWidth(self.view.bounds) - UIEdgeInsetsGetHorizontalValue(self.contentViewMargins), CGRectGetHeight(self.view.bounds) - self.keyboardHeight - UIEdgeInsetsGetVerticalValue(self.contentViewMargins));
CGSize contentViewLimitSize = CGSizeMake(fmin(self.maximumContentViewWidth, contentViewContainerSize.width), contentViewContainerSize.height);
CGSize contentViewSize = CGSizeZero;
- if ([self.contentViewController respondsToSelector:@selector(preferredContentSizeInModalPresentationViewController:limitSize:)]) {
- contentViewSize = [self.contentViewController preferredContentSizeInModalPresentationViewController:self limitSize:contentViewLimitSize];
+ if ([self.contentViewController respondsToSelector:@selector(preferredContentSizeInModalPresentationViewController:keyboardHeight:limitSize:)]) {
+ contentViewSize = [self.contentViewController preferredContentSizeInModalPresentationViewController:self keyboardHeight:self.keyboardHeight limitSize:contentViewLimitSize];
} else {
contentViewSize = [self.contentView sizeThatFits:contentViewLimitSize];
}
@@ -543,18 +544,12 @@ - (BOOL)isShowingPresentedViewController {
#pragma mark -
-- (void)keyboardWillShowWithUserInfo:(QMUIKeyboardUserInfo *)keyboardUserInfo {
+- (void)keyboardWillChangeFrameWithUserInfo:(QMUIKeyboardUserInfo *)keyboardUserInfo {
CGRect keyboardRect = [QMUIKeyboardManager convertKeyboardRect:[keyboardUserInfo endFrame] toView:self.view];
- CGFloat keyboardHeight = keyboardRect.size.height;
- if (keyboardHeight <= 0) return;
-
+ CGFloat keyboardHeight = CGRectIntersection(self.view.bounds, keyboardRect).size.height;
self.keyboardHeight = keyboardHeight;
- [self.view setNeedsLayout];
-}
-
-- (void)keyboardWillHideWithUserInfo:(QMUIKeyboardUserInfo *)keyboardUserInfo {
- self.keyboardHeight = 0;
- [self.view setNeedsLayout];
+
+ [self updateLayout];
}
#pragma mark - 屏幕旋转
diff --git a/QMUIKit/QMUIComponents/QMUIMoreOperationController.m b/QMUIKit/QMUIComponents/QMUIMoreOperationController.m
index 442995dd..cb3dfc6f 100644
--- a/QMUIKit/QMUIComponents/QMUIMoreOperationController.m
+++ b/QMUIKit/QMUIComponents/QMUIMoreOperationController.m
@@ -612,7 +612,7 @@ - (void)updateCornerRadius {
#pragma mark -
-- (CGSize)preferredContentSizeInModalPresentationViewController:(QMUIModalPresentationViewController *)controller limitSize:(CGSize)limitSize {
+- (CGSize)preferredContentSizeInModalPresentationViewController:(QMUIModalPresentationViewController *)controller keyboardHeight:(CGFloat)keyboardHeight limitSize:(CGSize)limitSize {
__block CGFloat contentHeight = (self.cancelButton.hidden ? 0 : self.cancelButtonHeight + self.cancelButtonMarginTop);
[self.mutableScrollViews enumerateObjectsUsingBlock:^(UIScrollView * _Nonnull scrollView, NSUInteger idx, BOOL * _Nonnull stop) {
NSArray *itemSection = self.mutableItems[idx];
diff --git a/QMUIKit/QMUIComponents/QMUIPopupMenuView.h b/QMUIKit/QMUIComponents/QMUIPopupMenuView.h
index 7442dc81..da22943a 100644
--- a/QMUIKit/QMUIComponents/QMUIPopupMenuView.h
+++ b/QMUIKit/QMUIComponents/QMUIPopupMenuView.h
@@ -50,7 +50,8 @@
@property(nonatomic, strong) UIImage *image;
@property(nonatomic, copy) NSString *title;
@property(nonatomic, strong, readonly) QMUIButton *button;
-@property(nonatomic, copy) void (^handler)(void);
+@property(nonatomic, copy) void (^handler)(QMUIPopupMenuView *aMenuView, QMUIPopupMenuItem *aItem);
+@property(nonatomic, weak) QMUIPopupMenuView *menuView;
-+ (instancetype)itemWithImage:(UIImage *)image title:(NSString *)title handler:(void (^)(void))handler;
++ (instancetype)itemWithImage:(UIImage *)image title:(NSString *)title handler:(void (^)(QMUIPopupMenuView *aMenuView, QMUIPopupMenuItem *aItem))handler;
@end
diff --git a/QMUIKit/QMUIComponents/QMUIPopupMenuView.m b/QMUIKit/QMUIComponents/QMUIPopupMenuView.m
index 284bfe54..534f4709 100644
--- a/QMUIKit/QMUIComponents/QMUIPopupMenuView.m
+++ b/QMUIKit/QMUIComponents/QMUIPopupMenuView.m
@@ -11,6 +11,7 @@
#import "UIView+QMUI.h"
#import "CALayer+QMUI.h"
#import "UIButton+QMUI.h"
+#import "NSArray+QMUI.h"
#import "QMUICore.h"
@interface QMUIPopupMenuItem ()
@@ -32,11 +33,17 @@ - (void)updateAppearanceForPopupMenuView;
@implementation QMUIPopupMenuView
- (void)setItems:(NSArray *)items {
+ [_items enumerateObjectsUsingBlock:^(QMUIPopupMenuItem * _Nonnull item, NSUInteger idx, BOOL * _Nonnull stop) {
+ item.menuView = nil;
+ }];
_items = items;
self.itemSections = @[_items];
}
- (void)setItemSections:(NSArray *> *)itemSections {
+ [_itemSections qmui_enumerateNestedArrayWithBlock:^(QMUIPopupMenuItem *item, BOOL *stop) {
+ item.menuView = nil;
+ }];
_itemSections = itemSections;
[self configureItems];
}
@@ -60,6 +67,7 @@ - (void)configureItems {
item.button.imageEdgeInsets = UIEdgeInsetsMake(0, -self.imageMarginRight, 0, self.imageMarginRight);
item.button.contentEdgeInsets = UIEdgeInsetsMake(0, self.padding.left - item.button.imageEdgeInsets.left, 0, self.padding.right);
[self.scrollView addSubview:item.button];
+ item.menuView = self;
// 配置分隔线,注意每一个 section 里的最后一行是不显示分隔线的
BOOL shouldShowSeparatorAtRow = [self shouldShowSeparatorAtRow:row rowCount:rowCount inSection:section sectionCount:sectionCount];
@@ -175,7 +183,7 @@ - (void)updateAppearanceForPopupMenuView {
@implementation QMUIPopupMenuItem
-+ (instancetype)itemWithImage:(UIImage *)image title:(NSString *)title handler:(void (^)(void))handler {
++ (instancetype)itemWithImage:(UIImage *)image title:(NSString *)title handler:(void (^)(QMUIPopupMenuView *, QMUIPopupMenuItem *))handler {
QMUIPopupMenuItem *item = [[QMUIPopupMenuItem alloc] init];
item.image = image;
item.title = title;
@@ -201,7 +209,7 @@ - (void)setImage:(UIImage *)image {
- (void)handleButtonEvent:(id)sender {
if (self.handler) {
- self.handler();
+ self.handler(self.menuView, self);
}
}
diff --git a/QMUIKit/QMUICore/QMUICommonDefines.h b/QMUIKit/QMUICore/QMUICommonDefines.h
index 50bcb78a..eee686a3 100644
--- a/QMUIKit/QMUICore/QMUICommonDefines.h
+++ b/QMUIKit/QMUICore/QMUICommonDefines.h
@@ -221,12 +221,6 @@ ExchangeImplementationsInTwoClasses(Class _fromClass, SEL _originSelector, Class
return NO;
}
- Class superclass = class_getSuperclass(_fromClass);
- BOOL tryToExchangeSuperclassMethod = [superclass instancesRespondToSelector:_originSelector] && (class_getInstanceMethod(superclass, _originSelector) == class_getInstanceMethod(_fromClass, _originSelector));
- if (tryToExchangeSuperclassMethod) {
- NSLog(@"注意,%@ 准备替换方法 %@, 但这个方法来自于父类 %@", NSStringFromClass(_fromClass), NSStringFromSelector(_originSelector), NSStringFromClass(superclass));
- }
-
BOOL isAddedMethod = class_addMethod(_fromClass, _originSelector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod));
if (isAddedMethod) {
// 如果 class_addMethod 成功了,说明之前 fromClass 里并不存在 originSelector,所以要用一个空的方法代替它,以避免 class_replaceMethod 后,后续 toClass 的这个方法被调用时可能会 crash
diff --git a/QMUIKit/QMUICore/QMUIHelper.m b/QMUIKit/QMUICore/QMUIHelper.m
index 89028ce4..3cc84efa 100644
--- a/QMUIKit/QMUICore/QMUIHelper.m
+++ b/QMUIKit/QMUICore/QMUIHelper.m
@@ -8,6 +8,7 @@
#import "QMUIHelper.h"
#import "QMUICore.h"
+#import "NSNumber+QMUI.h"
#import
#import
@@ -102,7 +103,7 @@ + (NSNumber *)preferredContentSizeLevel {
+ (CGFloat)heightForDynamicTypeCell:(NSArray *)heights {
NSNumber *index = [QMUIHelper preferredContentSizeLevel];
- return [((NSNumber *)[heights objectAtIndex:[index intValue]]) floatValue];
+ return [((NSNumber *)[heights objectAtIndex:[index intValue]]) qmui_CGFloatValue];
}
@end
@@ -137,7 +138,7 @@ - (void)setLastKeyboardHeight:(CGFloat)argv {
}
- (CGFloat)lastKeyboardHeight {
- return [((NSNumber *)objc_getAssociatedObject(self, &kAssociatedObjectKey_LastKeyboardHeight)) floatValue];
+ return [((NSNumber *)objc_getAssociatedObject(self, &kAssociatedObjectKey_LastKeyboardHeight)) qmui_CGFloatValue];
}
+ (CGFloat)lastKeyboardHeightInApplicationWindowWhenVisible {
diff --git a/QMUIKit/UIKitExtensions/UILabel+QMUI.m b/QMUIKit/UIKitExtensions/UILabel+QMUI.m
index 26cdfc09..7958fa01 100644
--- a/QMUIKit/UIKitExtensions/UILabel+QMUI.m
+++ b/QMUIKit/UIKitExtensions/UILabel+QMUI.m
@@ -10,6 +10,7 @@
#import "QMUICore.h"
#import "NSParagraphStyle+QMUI.h"
#import "NSObject+QMUI.h"
+#import "NSNumber+QMUI.h"
@implementation UILabel (QMUI)
@@ -148,7 +149,7 @@ - (void)setQmui_lineHeight:(CGFloat)qmui_lineHeight {
}
- (CGFloat)qmui_lineHeight {
- return [objc_getAssociatedObject(self, &kAssociatedObjectKey_lineHeight) floatValue];
+ return [(NSNumber *)objc_getAssociatedObject(self, &kAssociatedObjectKey_lineHeight) qmui_CGFloatValue];
}
- (instancetype)qmui_initWithFont:(UIFont *)font textColor:(UIColor *)textColor {
diff --git a/QMUIKit/UIKitExtensions/UIScrollView+QMUI.m b/QMUIKit/UIKitExtensions/UIScrollView+QMUI.m
index 20b325c7..8791f6b2 100644
--- a/QMUIKit/UIKitExtensions/UIScrollView+QMUI.m
+++ b/QMUIKit/UIKitExtensions/UIScrollView+QMUI.m
@@ -23,10 +23,6 @@ - (NSString *)qmui_description {
}
- (BOOL)qmui_alreadyAtTop {
- if (!self.qmui_canScroll) {
- return YES;
- }
-
if (self.contentOffset.y == -self.qmui_contentInset.top) {
return YES;
}
diff --git a/QMUIKit/UIKitExtensions/UITextView+QMUI.m b/QMUIKit/UIKitExtensions/UITextView+QMUI.m
index 2af1cc5e..383ab309 100644
--- a/QMUIKit/UIKitExtensions/UITextView+QMUI.m
+++ b/QMUIKit/UIKitExtensions/UITextView+QMUI.m
@@ -37,7 +37,7 @@ - (void)qmui_scrollCaretVisibleAnimated:(BOOL)animated {
CGRect caretRect = [self caretRectForPosition:self.selectedTextRange.end];
// scrollEnabled 为 NO 时可能产生不合法的 rect 值 https://github.com/QMUI/QMUI_iOS/issues/205
- if (isinf(CGRectGetMinX(caretRect)) || isinf(CGRectGetMinY(caretRect))) {
+ if (!CGRectIsValidated(caretRect)) {
return;
}
diff --git a/QMUIKit/UIKitExtensions/UIView+QMUI.m b/QMUIKit/UIKitExtensions/UIView+QMUI.m
index 407a0c4a..e472190d 100644
--- a/QMUIKit/UIKitExtensions/UIView+QMUI.m
+++ b/QMUIKit/UIKitExtensions/UIView+QMUI.m
@@ -12,6 +12,7 @@
#import "UIColor+QMUI.h"
#import "NSObject+QMUI.h"
#import "UIImage+QMUI.h"
+#import "NSNumber+QMUI.h"
#import
@interface UIView ()
@@ -414,7 +415,7 @@ - (void)setQmui_borderWidth:(CGFloat)qmui_borderWidth {
}
- (CGFloat)qmui_borderWidth {
- return (CGFloat)[objc_getAssociatedObject(self, &kAssociatedObjectKey_borderWidth) floatValue];
+ return [((NSNumber *)objc_getAssociatedObject(self, &kAssociatedObjectKey_borderWidth)) qmui_CGFloatValue];
}
static char kAssociatedObjectKey_borderColor;
@@ -434,7 +435,7 @@ - (void)setQmui_dashPhase:(CGFloat)qmui_dashPhase {
}
- (CGFloat)qmui_dashPhase {
- return (CGFloat)[objc_getAssociatedObject(self, &kAssociatedObjectKey_dashPhase) floatValue];
+ return [(NSNumber *)objc_getAssociatedObject(self, &kAssociatedObjectKey_dashPhase) qmui_CGFloatValue];
}
static char kAssociatedObjectKey_dashPattern;
diff --git a/qmui.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/qmui.xcodeproj/project.xcworkspace/contents.xcworkspacedata
deleted file mode 100644
index 919434a6..00000000
--- a/qmui.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
diff --git a/qmui.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/qmui.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
deleted file mode 100644
index 18d98100..00000000
--- a/qmui.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
- IDEDidComputeMac32BitWarning
-
-
-