Skip to content

Commit

Permalink
3.1.3
Browse files Browse the repository at this point in the history
  • Loading branch information
MoLice committed Feb 19, 2019
1 parent 8f6f438 commit c18b960
Show file tree
Hide file tree
Showing 35 changed files with 184 additions and 79 deletions.
2 changes: 1 addition & 1 deletion QMUIKit.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "QMUIKit"
s.version = "3.1.2"
s.version = "3.1.3"
s.summary = "致力于提高项目 UI 开发效率的解决方案"
s.description = <<-DESC
QMUI iOS 是一个致力于提高项目 UI 开发效率的解决方案,其设计目的是用于辅助快速搭建一个具备基本设计还原效果的 iOS 项目,同时利用自身提供的丰富控件及兼容处理, 让开发者能专注于业务需求而无需耗费精力在基础代码的设计上。不管是新项目的创建,或是已有项目的维护,均可使开发效率和项目质量得到大幅度提升。
Expand Down
2 changes: 1 addition & 1 deletion QMUIKit/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>3.1.2</string>
<string>3.1.3</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ + (nonnull instancetype)appearance {
@interface QMUIImagePickerViewController ()

@property(nonatomic, strong) QMUIImagePickerPreviewViewController *imagePickerPreviewViewController;
@property(nonatomic, assign) BOOL isImagesAssetLoaded;// 这个属性的作用描述:https://github.com/QMUI/QMUI_iOS/issues/219
@property(nonatomic, assign) BOOL isImagesAssetLoaded;// 这个属性的作用描述:https://github.com/Tencent/QMUI_iOS/issues/219
@property(nonatomic, assign) BOOL hasScrollToInitialPosition;
@property(nonatomic, assign) BOOL canScrollToInitialPosition;// 要等数据加载完才允许滚动
@end
Expand Down Expand Up @@ -258,7 +258,7 @@ - (void)refreshWithAssetsGroup:(QMUIAssetsGroup *)assetsGroup {
[self.imagesAssetArray addObject:resultAsset];
} else {
// result 为 nil,即遍历相片或视频完毕
self.isImagesAssetLoaded = YES;// 这个属性的作用描述: https://github.com/QMUI/QMUI_iOS/issues/219
self.isImagesAssetLoaded = YES;// 这个属性的作用描述: https://github.com/Tencent/QMUI_iOS/issues/219
[self.collectionView reloadData];
[self.collectionView performBatchUpdates:NULL completion:^(BOOL finished) {
[self scrollToInitialPositionIfNeeded];
Expand Down
7 changes: 7 additions & 0 deletions QMUIKit/QMUIComponents/QMUIBadge/UIBarItem+QMUIBadge.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#import "UIBarItem+QMUIBadge.h"
#import "QMUICore.h"
#import "QMUILog.h"
#import "QMUILabel.h"
#import "UIView+QMUI.h"
#import "UIBarItem+QMUI.h"
Expand Down Expand Up @@ -55,6 +56,12 @@ + (void)load {
return;
}

if (selfObject == firstArgv) {
NSString *log = [NSString stringWithFormat:@"UITabBarButton addSubview:, 把自己作为 subview 添加到自己身上!\n%@", [NSThread callStackSymbols]];
NSAssert(NO, log);
QMUILogWarn(@"UIBarItem (QMUIBadge)", @"%@", log);
}

// call super
void (*originSelectorIMP)(id, SEL, UIView *);
originSelectorIMP = (void (*)(id, SEL, UIView *))originIMP;
Expand Down
2 changes: 1 addition & 1 deletion QMUIKit/QMUIComponents/QMUIButton/QMUIButton.m
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ - (void)setImage:(UIImage *)image forState:(UIControlState)state {

[super setImage:image forState:state];

// 注意,这句必须。因 UIButton 系统特性问题,如果在设置图片前就访问过 imageView,那么之后设置图片时 imageView.image 在布局的时候还会是 nil(self.currentImage 有值),这样会导致一些布局问题。解决方法是再触发一下 self.imageView。详情请看 https://github.com/QMUI/QMUI_iOS/issues/439
// 注意,这句必须。因 UIButton 系统特性问题,如果在设置图片前就访问过 imageView,那么之后设置图片时 imageView.image 在布局的时候还会是 nil(self.currentImage 有值),这样会导致一些布局问题。解决方法是再触发一下 self.imageView。详情请看 https://github.com/Tencent/QMUI_iOS/issues/439
[self imageView];
}

Expand Down
18 changes: 9 additions & 9 deletions QMUIKit/QMUIComponents/QMUIButton/QMUINavigationButton.m
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ + (void)load {
@selector(setRightBarButtonItem:animated:),
@selector(setRightBarButtonItems:animated:),

// 如果被拦截,则 getter 也要返回被缓存的 item,否则会出现这个 bug:https://github.com/QMUI/QMUI_iOS/issues/362
// 如果被拦截,则 getter 也要返回被缓存的 item,否则会出现这个 bug:https://github.com/Tencent/QMUI_iOS/issues/362
@selector(leftBarButtonItem),
@selector(leftBarButtonItems),
@selector(rightBarButtonItem),
Expand All @@ -362,7 +362,7 @@ + (void)load {
});
}

// 监控是否在 iOS 10 及以下,手势返回的过程中,手势返回背后的那个界面修改了 navigationItem,这可能导致 bug:https://github.com/QMUI/QMUI_iOS/issues/302
// 监控是否在 iOS 10 及以下,手势返回的过程中,手势返回背后的那个界面修改了 navigationItem,这可能导致 bug:https://github.com/Tencent/QMUI_iOS/issues/302
- (BOOL)detectSetItemsWhenPopping {
if (@available(iOS 11, *)) {
} else {
Expand Down Expand Up @@ -392,12 +392,12 @@ - (void)qmui_setLeftBarButtonItem:(UIBarButtonItem *)item animated:(BOOL)animate
// 自动给 position 赋值
item.qmui_navigationButton.buttonPosition = QMUINavigationButtonPositionLeft;

// iOS 11,调整自定义返回按钮的位置 https://github.com/QMUI/QMUI_iOS/issues/279
// iOS 11,调整自定义返回按钮的位置 https://github.com/Tencent/QMUI_iOS/issues/279
if (@available(iOS 11, *)) {
UINavigationBar *navigationBar = self.qmui_navigationBar;
if (!navigationBar) return;

// 这个保护对应这个 issue:https://github.com/QMUI/QMUI_iOS/issues/335
// 这个保护对应这个 issue:https://github.com/Tencent/QMUI_iOS/issues/335
if ([navigationBar.items containsObject:self] && navigationBar.topItem != self) return;

navigationBar.qmui_customizingBackBarButtonItem = item.qmui_isCustomizedBackBarButtonItem;
Expand All @@ -421,13 +421,13 @@ - (void)qmui_setLeftBarButtonItems:(NSArray<UIBarButtonItem *> *)items animated:
}
}

// iOS 11,调整自定义返回按钮的位置 https://github.com/QMUI/QMUI_iOS/issues/279
// iOS 11,调整自定义返回按钮的位置 https://github.com/Tencent/QMUI_iOS/issues/279
if (@available(iOS 11, *)) {

UINavigationBar *navigationBar = self.qmui_navigationBar;
if (!navigationBar) return;

// 这个保护对应这个 issue:https://github.com/QMUI/QMUI_iOS/issues/335
// 这个保护对应这个 issue:https://github.com/Tencent/QMUI_iOS/issues/335
if ([navigationBar.items containsObject:self] && navigationBar.topItem != self) return;

BOOL customizingBackBarButtonItem = NO;
Expand Down Expand Up @@ -543,7 +543,7 @@ + (void)load {
OverrideImplementation([UINavigationBar class], @selector(pushNavigationItem:animated:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP originIMP) {
return ^(UINavigationBar *selfObject, UINavigationItem *item, BOOL animated) {

// iOS 11,调整自定义返回按钮的位置 https://github.com/QMUI/QMUI_iOS/issues/279
// iOS 11,调整自定义返回按钮的位置 https://github.com/Tencent/QMUI_iOS/issues/279
BOOL shouldSetTagBeforeCallingSuper = NO;// 如果要 push 进的新 item 本身就是自定义返回按钮,那么要先打好标记再调用 super,否则先调用 super 再打标记,实测只有这样才不会导致跳动
if (@available(iOS 11, *)) {
shouldSetTagBeforeCallingSuper = [selfObject isKindOfClass:originClass] && item.leftBarButtonItem.qmui_isCustomizedBackBarButtonItem;
Expand All @@ -569,7 +569,7 @@ + (void)load {
return ^(UINavigationBar *selfObject, NSArray<UINavigationItem *> *items, BOOL animated) {

if ([selfObject isKindOfClass:originClass]) {
// iOS 11,调整自定义返回按钮的位置 https://github.com/QMUI/QMUI_iOS/issues/279
// iOS 11,调整自定义返回按钮的位置 https://github.com/Tencent/QMUI_iOS/issues/279
if (@available(iOS 11, *)) {
selfObject.qmui_customizingBackBarButtonItem = items.lastObject.leftBarButtonItem.qmui_isCustomizedBackBarButtonItem;
}
Expand Down Expand Up @@ -632,7 +632,7 @@ + (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{

// iOS 11,调整自定义返回按钮的位置 https://github.com/QMUI/QMUI_iOS/issues/279
// iOS 11,调整自定义返回按钮的位置 https://github.com/Tencent/QMUI_iOS/issues/279
if (@available(iOS 11, *)) {
ExchangeImplementations([UINavigationController class], @selector(navigationBar:shouldPopItem:), @selector(navigationButton_navigationBar:shouldPopItem:));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@
* 2. 实现 tableView 的 delegate 方法 qmui_tableView:cacheKeyForRowAtIndexPath: 返回一个 key。建议 key 由所有可能影响高度的字段拼起来,这样当数据发生变化时不需要手动更新缓存。
*
* @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 如果 tableView 开启了 qmui_cacheCellHeightByKeyAutomatically 并且 tableView.delegate 实现了 tableView:heightForRowAtIndexPath:,如果返回值 >= 0则使用这个返回值当成最终的高度,如果 < 0 则交给 QMUICellHeightKeyCache 自己处理。
* @note 如果 tableView 开启了 qmui_cacheCellHeightByKeyAutomatically 并且 tableView.delegate 实现了 tableView:estimatedHeightForRowAtIndexPath:,则当该 indexPath 所在的 cell 的高度已经被计算过的情况下,业务自己的 tableView:estimatedHeightForRowAtIndexPath: 不会被调用,只有当高度缓存里找不到该 indexPath 对应的 key 的缓存时,才会调用业务的这个方法。
*
* @note 在 UITableView 的宽度和 contentInset 发生变化时(例如横竖屏旋转、iPad 分屏),高度缓存会自动刷新,所以无需为这种情况做保护。
* @note 在 UITableView 的宽度和 contentInset、safeAreaInsets 发生变化时(例如横竖屏旋转、iPad 分屏),高度缓存会自动刷新,所以无需为这种情况做保护。
*/
@interface UITableView (QMUICellHeightKeyCache)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ - (void)setQmui_cacheCellHeightByKeyAutomatically:(BOOL)qmui_cacheCellHeightByKe
if (qmui_cacheCellHeightByKeyAutomatically) {

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 功能");
NSAssert(self.estimatedRowHeight != 0 || [self.delegate respondsToSelector:@selector(tableView:estimatedHeightForRowAtIndexPath:)], @"必须为 estimatedRowHeight 赋一个不为0的值,或者实现 tableView:estimatedHeightForRowAtIndexPath: 方法,否则无法开启 self-sizing cells 功能");

[self replaceMethodForDelegateIfNeeded:(id<QMUITableViewDelegate>)self.delegate];

Expand Down Expand Up @@ -114,6 +114,16 @@ - (CGFloat)qmui_tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIn
}
}

- (CGFloat)qmui_tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView.qmui_cacheCellHeightByKeyAutomatically) {
id<NSCopying> cachedKey = [((id<QMUITableViewDelegate>)tableView.delegate) qmui_tableView:tableView cacheKeyForRowAtIndexPath:indexPath];
if ([tableView.qmui_currentCellHeightKeyCache existsHeightForKey:cachedKey]) {
return [tableView.qmui_currentCellHeightKeyCache heightForKey:cachedKey];
}
}
return UITableViewAutomaticDimension;// 表示 QMUICellHeightKeyCache 无法决定一个合适的高度,交给业务,或者交给系统默认值决定。
}

- (void)qmui_setDelegate:(id<QMUITableViewDelegate>)delegate {
[self replaceMethodForDelegateIfNeeded:delegate];
[self qmui_setDelegate:delegate];
Expand All @@ -134,6 +144,7 @@ - (void)replaceMethodForDelegateIfNeeded:(id<QMUITableViewDelegate>)delegate {

[self handleWillDisplayCellMethodForDelegate:aDelegate];
[self handleHeightForRowMethodForDelegate:aDelegate];
[self handleEstimatedHeightForRowMethodForDelegate:aDelegate];
};

if ([delegate isKindOfClass:[QMUIMultipleDelegates class]]) {
Expand Down Expand Up @@ -211,8 +222,40 @@ - (void)handleHeightForRowMethodForDelegate:(id<QMUITableViewDelegate>)delegate
}
}

- (void)handleEstimatedHeightForRowMethodForDelegate:(id<QMUITableViewDelegate>)delegate {
// 如果 delegate 本身没有实现 tableView:estimatedHeightForRowAtIndexPath:,则为它添加一个。
// 如果 delegate 已经有实现,会优先拿 QMUICellHeightKeyCache 的结果,如果 QMUICellHeightKeyCache 在 cache 里找不到值,才会返回业务在 tableView:estimatedHeightForRowAtIndexPath: 里的返回值
SEL heightForRowSelector = @selector(tableView:estimatedHeightForRowAtIndexPath:);
Method heightForRowMethod = class_getInstanceMethod([self class], @selector(qmui_tableView:estimatedHeightForRowAtIndexPath:));
IMP heightForRowIMP = method_getImplementation(heightForRowMethod);
CGFloat (*heightForRowFunction)(id<QMUITableViewDelegate>, SEL, UITableView *, NSIndexPath *);
heightForRowFunction = (CGFloat (*)(id<QMUITableViewDelegate>, 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<QMUITableViewDelegate> delegateSelf, UITableView *tableView, NSIndexPath *indexPath) {

CGFloat result = 0;
if ([delegateSelf isKindOfClass:originClass]) {
result = heightForRowFunction(delegateSelf, heightForRowSelector, tableView, indexPath);
if (result != UITableViewAutomaticDimension) {
return result;
}
}

// call super
CGFloat (*originSelectorIMP)(id<QMUITableViewDelegate>, SEL, UITableView *, NSIndexPath *);
originSelectorIMP = (CGFloat (*)(id<QMUITableViewDelegate>, SEL, UITableView *, NSIndexPath *))originIMP;
result = originSelectorIMP(delegateSelf, originCMD, tableView, indexPath);
return result;
};
});
}
}

- (void)qmui_invalidateCellHeightCachedForKey:(id<NSCopying>)key {
[self.qmui_allKeyCaches enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull key, QMUICellHeightKeyCache * _Nonnull obj, BOOL * _Nonnull stop) {
[self.qmui_allKeyCaches enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull widthKey, QMUICellHeightKeyCache * _Nonnull obj, BOOL * _Nonnull stop) {
[obj invalidateHeightForKey:key];
}];
}
Expand Down
10 changes: 10 additions & 0 deletions QMUIKit/QMUIComponents/QMUICollectionViewPagingLayout.m
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,11 @@ - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentO

// 最顶/最底
if (proposedContentOffset.y < -contentInset.top || proposedContentOffset.y >= contentSize.height + contentInset.bottom - frameSize.height) {
if (IOS_VERSION_NUMBER < 100000) {
// iOS 10 及以上的版本,直接返回当前的 contentOffset,系统会自动帮你调整到边界状态,而 iOS 9 及以下的版本需要自己计算
// https://github.com/Tencent/QMUI_iOS/issues/499
proposedContentOffset.y = MIN(MAX(proposedContentOffset.y, -contentInset.top), contentSize.height + contentInset.bottom - frameSize.height);
}
return proposedContentOffset;
}

Expand Down Expand Up @@ -282,6 +287,11 @@ - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentO

// 最左/最右
if (proposedContentOffset.x < -contentInset.left || proposedContentOffset.x >= contentSize.width + contentInset.right - frameSize.width) {
if (IOS_VERSION_NUMBER < 100000) {
// iOS 10 及以上的版本,直接返回当前的 contentOffset,系统会自动帮你调整到边界状态,而 iOS 9 及以下的版本需要自己计算
// https://github.com/Tencent/QMUI_iOS/issues/499
proposedContentOffset.x = MIN(MAX(proposedContentOffset.x, -contentInset.left), contentSize.width + contentInset.right - frameSize.width);
}
return proposedContentOffset;
}

Expand Down
6 changes: 3 additions & 3 deletions QMUIKit/QMUIComponents/QMUIModalPresentationViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -271,10 +271,10 @@ - (void)viewWillDisappear:(BOOL)animated {
void (^didHiddenCompletion)(BOOL finished) = ^(BOOL finished) {

if (self.shownInWindowMode) {
// 恢复 keyWindow 之前做一下检查,避免这个问题 https://github.com/QMUI/QMUI_iOS/issues/90
// 恢复 keyWindow 之前做一下检查,避免这个问题 https://github.com/Tencent/QMUI_iOS/issues/90
if ([[UIApplication sharedApplication] keyWindow] == self.containerWindow) {
if (self.previousKeyWindow.hidden) {
// 保护了这个 issue 记录的情况,避免主 window 丢失 keyWindow https://github.com/QMUI/QMUI_iOS/issues/315
// 保护了这个 issue 记录的情况,避免主 window 丢失 keyWindow https://github.com/Tencent/QMUI_iOS/issues/315
[[UIApplication sharedApplication].delegate.window makeKeyWindow];
} else {
[self.previousKeyWindow makeKeyWindow];
Expand Down Expand Up @@ -694,7 +694,7 @@ @implementation QMUIModalPresentationWindow
- (void)layoutSubviews {
[super layoutSubviews];
if (self.rootViewController) {
// https://github.com/QMUI/QMUI_iOS/issues/375
// https://github.com/Tencent/QMUI_iOS/issues/375
UIView *rootView = self.rootViewController.view;
if (CGRectGetMinY(rootView.frame) > 0 && ![UIApplication sharedApplication].statusBarHidden && StatusBarHeight > CGRectGetMinY(rootView.frame)) {
rootView.frame = self.bounds;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@

@interface NSObject (QMUIMultipleDelegates_Private)

/// 内部使用,用于标志“xxx.delegate = xxx”的情况,具体请看 https://github.com/QMUI/QMUI_iOS/issues/346
/// 内部使用,用于标志“xxx.delegate = xxx”的情况,具体请看 https://github.com/Tencent/QMUI_iOS/issues/346
@property(nonatomic, assign) BOOL qmui_delegatesSelf;

@end
Loading

0 comments on commit c18b960

Please sign in to comment.