/**
|
* Tencent is pleased to support the open source community by making QMUI_iOS available.
|
* Copyright (C) 2016-2021 THL A29 Limited, a Tencent company. All rights reserved.
|
* Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
|
* http://opensource.org/licenses/MIT
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
*/
|
|
//
|
// QMUIButton.m
|
// qmui
|
//
|
// Created by QMUI Team on 14-7-7.
|
//
|
|
#import "QMUIButton.h"
|
#import "QMUICore.h"
|
#import "CALayer+QMUI.h"
|
#import "UIButton+QMUI.h"
|
|
const CGFloat QMUIButtonCornerRadiusAdjustsBounds = -1;
|
|
@interface QMUIButton ()
|
|
@property(nonatomic, strong) CALayer *highlightedBackgroundLayer;
|
@property(nonatomic, strong) UIColor *originBorderColor;
|
@end
|
|
@implementation QMUIButton
|
|
- (instancetype)initWithFrame:(CGRect)frame {
|
if (self = [super initWithFrame:frame]) {
|
self.tintColor = ButtonTintColor;
|
[self setTitleColor:self.tintColor forState:UIControlStateNormal];// 初始化时 adjustsTitleTintColorAutomatically 还是 NO,所以这里手动把 titleColor 设置为 tintColor 的值
|
|
// iOS7以后的button,sizeToFit后默认会自带一个上下的contentInsets,为了保证按钮大小即为内容大小,这里直接去掉,改为一个最小的值。
|
self.contentEdgeInsets = UIEdgeInsetsMake(CGFLOAT_MIN, 0, CGFLOAT_MIN, 0);
|
|
// 放在后面,让前面的默认值可以被子类重写的 didInitialize 覆盖
|
[self didInitialize];
|
}
|
return self;
|
}
|
|
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
if (self = [super initWithCoder:aDecoder]) {
|
[self didInitialize];
|
}
|
return self;
|
}
|
|
- (void)didInitialize {
|
// 默认接管highlighted和disabled的表现,去掉系统默认的表现
|
self.adjustsImageWhenHighlighted = NO;
|
self.adjustsImageWhenDisabled = NO;
|
self.adjustsButtonWhenHighlighted = YES;
|
self.adjustsButtonWhenDisabled = YES;
|
|
// 图片默认在按钮左边,与系统UIButton保持一致
|
self.imagePosition = QMUIButtonImagePositionLeft;
|
}
|
|
// 系统访问 self.imageView 会触发 layout,而私有方法 _imageView 则是简单地访问 imageView,所以在 QMUIButton layoutSubviews 里应该用这个方法
|
// https://github.com/Tencent/QMUI_iOS/issues/1051
|
- (UIImageView *)_qmui_imageView {
|
BeginIgnorePerformSelectorLeaksWarning
|
return [self performSelector:NSSelectorFromString(@"_imageView")];
|
EndIgnorePerformSelectorLeaksWarning
|
}
|
|
- (CGSize)sizeThatFits:(CGSize)size {
|
// 如果调用 sizeToFit,那么传进来的 size 就是当前按钮的 size,此时的计算不要去限制宽高
|
// 系统 UIButton 不管任何时候,对 sizeThatFits:CGSizeZero 都会返回真实的内容大小,这里对齐
|
if (CGSizeEqualToSize(self.bounds.size, size) || CGSizeIsEmpty(size)) {
|
size = CGSizeMax;
|
}
|
|
BOOL isImageViewShowing = !!self.currentImage;
|
BOOL isTitleLabelShowing = !!self.currentTitle || self.currentAttributedTitle;
|
CGSize imageTotalSize = CGSizeZero;// 包含 imageEdgeInsets 那些空间
|
CGSize titleTotalSize = CGSizeZero;// 包含 titleEdgeInsets 那些空间
|
CGFloat spacingBetweenImageAndTitle = flat(isImageViewShowing && isTitleLabelShowing ? self.spacingBetweenImageAndTitle : 0);// 如果图片或文字某一者没显示,则这个 spacing 不考虑进布局
|
UIEdgeInsets contentEdgeInsets = UIEdgeInsetsRemoveFloatMin(self.contentEdgeInsets);
|
CGSize resultSize = CGSizeZero;
|
CGSize contentLimitSize = CGSizeMake(size.width - UIEdgeInsetsGetHorizontalValue(contentEdgeInsets), size.height - UIEdgeInsetsGetVerticalValue(contentEdgeInsets));
|
|
switch (self.imagePosition) {
|
case QMUIButtonImagePositionTop:
|
case QMUIButtonImagePositionBottom: {
|
// 图片和文字上下排版时,宽度以文字或图片的最大宽度为最终宽度
|
if (isImageViewShowing) {
|
CGFloat imageLimitWidth = contentLimitSize.width - UIEdgeInsetsGetHorizontalValue(self.imageEdgeInsets);
|
CGSize imageSize = self.imageView.image ? [self.imageView sizeThatFits:CGSizeMake(imageLimitWidth, CGFLOAT_MAX)] : self.currentImage.size;
|
imageSize.width = MIN(imageSize.width, imageLimitWidth);// QMUIButton sizeThatFits 时 self._imageView 为 nil 但 self.imageView 有值,而开启了 Bold Text 时,系统的 self.imageView sizeThatFits 返回值会比没开启 BoldText 时多 1pt(不知道为什么文字加粗与否会影响 imageView...),从而保证开启 Bold Text 后文字依然能完整展示出来,所以这里应该用 self.imageView 而不是 self._imageView
|
imageTotalSize = CGSizeMake(imageSize.width + UIEdgeInsetsGetHorizontalValue(self.imageEdgeInsets), imageSize.height + UIEdgeInsetsGetVerticalValue(self.imageEdgeInsets));
|
}
|
|
if (isTitleLabelShowing) {
|
CGSize titleLimitSize = CGSizeMake(contentLimitSize.width - UIEdgeInsetsGetHorizontalValue(self.titleEdgeInsets), contentLimitSize.height - imageTotalSize.height - spacingBetweenImageAndTitle - UIEdgeInsetsGetVerticalValue(self.titleEdgeInsets));
|
CGSize titleSize = [self.titleLabel sizeThatFits:titleLimitSize];
|
titleSize.height = MIN(titleSize.height, titleLimitSize.height);
|
titleTotalSize = CGSizeMake(titleSize.width + UIEdgeInsetsGetHorizontalValue(self.titleEdgeInsets), titleSize.height + UIEdgeInsetsGetVerticalValue(self.titleEdgeInsets));
|
}
|
|
resultSize.width = UIEdgeInsetsGetHorizontalValue(contentEdgeInsets);
|
resultSize.width += MAX(imageTotalSize.width, titleTotalSize.width);
|
resultSize.height = UIEdgeInsetsGetVerticalValue(contentEdgeInsets) + imageTotalSize.height + spacingBetweenImageAndTitle + titleTotalSize.height;
|
}
|
break;
|
|
case QMUIButtonImagePositionLeft:
|
case QMUIButtonImagePositionRight: {
|
// 图片和文字水平排版时,高度以文字或图片的最大高度为最终高度
|
// 注意这里有一个和系统不一致的行为:当 titleLabel 为多行时,系统的 sizeThatFits: 计算结果固定是单行的,所以当 QMUIButtonImagePositionLeft 并且titleLabel 多行的情况下,QMUIButton 计算的结果与系统不一致
|
|
if (isImageViewShowing) {
|
CGFloat imageLimitHeight = contentLimitSize.height - UIEdgeInsetsGetVerticalValue(self.imageEdgeInsets);
|
CGSize imageSize = self.imageView.image ? [self.imageView sizeThatFits:CGSizeMake(CGFLOAT_MAX, imageLimitHeight)] : self.currentImage.size;
|
imageSize.height = MIN(imageSize.height, imageLimitHeight);// QMUIButton sizeThatFits 时 self._imageView 为 nil 但 self.imageView 有值,而开启了 Bold Text 时,系统的 self.imageView sizeThatFits 返回值会比没开启 BoldText 时多 1pt(不知道为什么文字加粗与否会影响 imageView...),从而保证开启 Bold Text 后文字依然能完整展示出来,所以这里应该用 self.imageView 而不是 self._imageView
|
imageTotalSize = CGSizeMake(imageSize.width + UIEdgeInsetsGetHorizontalValue(self.imageEdgeInsets), imageSize.height + UIEdgeInsetsGetVerticalValue(self.imageEdgeInsets));
|
}
|
|
if (isTitleLabelShowing) {
|
CGSize titleLimitSize = CGSizeMake(contentLimitSize.width - UIEdgeInsetsGetHorizontalValue(self.titleEdgeInsets) - imageTotalSize.width - spacingBetweenImageAndTitle, contentLimitSize.height - UIEdgeInsetsGetVerticalValue(self.titleEdgeInsets));
|
CGSize titleSize = [self.titleLabel sizeThatFits:titleLimitSize];
|
titleSize.height = MIN(titleSize.height, titleLimitSize.height);
|
titleTotalSize = CGSizeMake(titleSize.width + UIEdgeInsetsGetHorizontalValue(self.titleEdgeInsets), titleSize.height + UIEdgeInsetsGetVerticalValue(self.titleEdgeInsets));
|
}
|
|
resultSize.width = UIEdgeInsetsGetHorizontalValue(contentEdgeInsets) + imageTotalSize.width + spacingBetweenImageAndTitle + titleTotalSize.width;
|
resultSize.height = UIEdgeInsetsGetVerticalValue(contentEdgeInsets);
|
resultSize.height += MAX(imageTotalSize.height, titleTotalSize.height);
|
}
|
break;
|
}
|
return resultSize;
|
}
|
|
- (CGSize)intrinsicContentSize {
|
return [self sizeThatFits:CGSizeMax];
|
}
|
|
- (void)layoutSubviews {
|
[super layoutSubviews];
|
|
if (CGRectIsEmpty(self.bounds)) {
|
return;
|
}
|
|
if (self.cornerRadius == QMUIButtonCornerRadiusAdjustsBounds) {
|
self.layer.cornerRadius = CGRectGetHeight(self.bounds) / 2;
|
}
|
|
BOOL isImageViewShowing = !!self.currentImage;
|
BOOL isTitleLabelShowing = !!self.currentTitle || !!self.currentAttributedTitle;
|
CGSize imageLimitSize = CGSizeZero;
|
CGSize titleLimitSize = CGSizeZero;
|
CGSize imageTotalSize = CGSizeZero;// 包含 imageEdgeInsets 那些空间
|
CGSize titleTotalSize = CGSizeZero;// 包含 titleEdgeInsets 那些空间
|
CGFloat spacingBetweenImageAndTitle = flat(isImageViewShowing && isTitleLabelShowing ? self.spacingBetweenImageAndTitle : 0);// 如果图片或文字某一者没显示,则这个 spacing 不考虑进布局
|
CGRect imageFrame = CGRectZero;
|
CGRect titleFrame = CGRectZero;
|
UIEdgeInsets contentEdgeInsets = UIEdgeInsetsRemoveFloatMin(self.contentEdgeInsets);
|
CGSize contentSize = CGSizeMake(CGRectGetWidth(self.bounds) - UIEdgeInsetsGetHorizontalValue(contentEdgeInsets), CGRectGetHeight(self.bounds) - UIEdgeInsetsGetVerticalValue(contentEdgeInsets));
|
|
// 图片的布局原则都是尽量完整展示,所以不管 imagePosition 的值是什么,这个计算过程都是相同的
|
if (isImageViewShowing) {
|
imageLimitSize = CGSizeMake(contentSize.width - UIEdgeInsetsGetHorizontalValue(self.imageEdgeInsets), contentSize.height - UIEdgeInsetsGetVerticalValue(self.imageEdgeInsets));
|
CGSize imageSize = self._qmui_imageView.image ? [self._qmui_imageView sizeThatFits:imageLimitSize] : self.currentImage.size;
|
imageSize.width = MIN(imageLimitSize.width, imageSize.width);
|
imageSize.height = MIN(imageLimitSize.height, imageSize.height);
|
imageFrame = CGRectMakeWithSize(imageSize);
|
imageTotalSize = CGSizeMake(imageSize.width + UIEdgeInsetsGetHorizontalValue(self.imageEdgeInsets), imageSize.height + UIEdgeInsetsGetVerticalValue(self.imageEdgeInsets));
|
}
|
|
// UIButton 如果本身大小为 (0,0),此时设置一个 imageEdgeInsets 会让 imageView 的 bounds 错误,导致后续 imageView 的 subviews 布局时会产生偏移,因此这里做一次保护
|
// https://github.com/Tencent/QMUI_iOS/issues/1012
|
void (^makesureBoundsPositive)(UIView *) = ^void(UIView *view) {
|
CGRect bounds = view.bounds;
|
if (CGRectGetMinX(bounds) < 0 || CGRectGetMinY(bounds) < 0) {
|
bounds = CGRectMakeWithSize(bounds.size);
|
view.bounds = bounds;
|
}
|
};
|
if (isImageViewShowing) {
|
makesureBoundsPositive(self._qmui_imageView);
|
}
|
if (isTitleLabelShowing) {
|
makesureBoundsPositive(self.titleLabel);
|
}
|
|
if (self.imagePosition == QMUIButtonImagePositionTop || self.imagePosition == QMUIButtonImagePositionBottom) {
|
|
if (isTitleLabelShowing) {
|
titleLimitSize = CGSizeMake(contentSize.width - UIEdgeInsetsGetHorizontalValue(self.titleEdgeInsets), contentSize.height - imageTotalSize.height - spacingBetweenImageAndTitle - UIEdgeInsetsGetVerticalValue(self.titleEdgeInsets));
|
CGSize titleSize = [self.titleLabel sizeThatFits:titleLimitSize];
|
titleSize.width = MIN(titleLimitSize.width, titleSize.width);
|
titleSize.height = MIN(titleLimitSize.height, titleSize.height);
|
titleFrame = CGRectMakeWithSize(titleSize);
|
titleTotalSize = CGSizeMake(titleSize.width + UIEdgeInsetsGetHorizontalValue(self.titleEdgeInsets), titleSize.height + UIEdgeInsetsGetVerticalValue(self.titleEdgeInsets));
|
}
|
|
switch (self.contentHorizontalAlignment) {
|
case UIControlContentHorizontalAlignmentLeft:
|
imageFrame = isImageViewShowing ? CGRectSetX(imageFrame, contentEdgeInsets.left + self.imageEdgeInsets.left) : imageFrame;
|
titleFrame = isTitleLabelShowing ? CGRectSetX(titleFrame, contentEdgeInsets.left + self.titleEdgeInsets.left) : titleFrame;
|
break;
|
case UIControlContentHorizontalAlignmentCenter:
|
imageFrame = isImageViewShowing ? CGRectSetX(imageFrame, contentEdgeInsets.left + self.imageEdgeInsets.left + CGFloatGetCenter(imageLimitSize.width, CGRectGetWidth(imageFrame))) : imageFrame;
|
titleFrame = isTitleLabelShowing ? CGRectSetX(titleFrame, contentEdgeInsets.left + self.titleEdgeInsets.left + CGFloatGetCenter(titleLimitSize.width, CGRectGetWidth(titleFrame))) : titleFrame;
|
break;
|
case UIControlContentHorizontalAlignmentRight:
|
imageFrame = isImageViewShowing ? CGRectSetX(imageFrame, CGRectGetWidth(self.bounds) - contentEdgeInsets.right - self.imageEdgeInsets.right - CGRectGetWidth(imageFrame)) : imageFrame;
|
titleFrame = isTitleLabelShowing ? CGRectSetX(titleFrame, CGRectGetWidth(self.bounds) - contentEdgeInsets.right - self.titleEdgeInsets.right - CGRectGetWidth(titleFrame)) : titleFrame;
|
break;
|
case UIControlContentHorizontalAlignmentFill:
|
if (isImageViewShowing) {
|
imageFrame = CGRectSetX(imageFrame, contentEdgeInsets.left + self.imageEdgeInsets.left);
|
imageFrame = CGRectSetWidth(imageFrame, imageLimitSize.width);
|
}
|
if (isTitleLabelShowing) {
|
titleFrame = CGRectSetX(titleFrame, contentEdgeInsets.left + self.titleEdgeInsets.left);
|
titleFrame = CGRectSetWidth(titleFrame, titleLimitSize.width);
|
}
|
break;
|
default:
|
break;
|
}
|
|
if (self.imagePosition == QMUIButtonImagePositionTop) {
|
switch (self.contentVerticalAlignment) {
|
case UIControlContentVerticalAlignmentTop:
|
imageFrame = isImageViewShowing ? CGRectSetY(imageFrame, contentEdgeInsets.top + self.imageEdgeInsets.top) : imageFrame;
|
titleFrame = isTitleLabelShowing ? CGRectSetY(titleFrame, contentEdgeInsets.top + imageTotalSize.height + spacingBetweenImageAndTitle + self.titleEdgeInsets.top) : titleFrame;
|
break;
|
case UIControlContentVerticalAlignmentCenter: {
|
CGFloat contentHeight = imageTotalSize.height + spacingBetweenImageAndTitle + titleTotalSize.height;
|
CGFloat minY = CGFloatGetCenter(contentSize.height, contentHeight) + contentEdgeInsets.top;
|
imageFrame = isImageViewShowing ? CGRectSetY(imageFrame, minY + self.imageEdgeInsets.top) : imageFrame;
|
titleFrame = isTitleLabelShowing ? CGRectSetY(titleFrame, minY + imageTotalSize.height + spacingBetweenImageAndTitle + self.titleEdgeInsets.top) : titleFrame;
|
}
|
break;
|
case UIControlContentVerticalAlignmentBottom:
|
titleFrame = isTitleLabelShowing ? CGRectSetY(titleFrame, CGRectGetHeight(self.bounds) - contentEdgeInsets.bottom - self.titleEdgeInsets.bottom - CGRectGetHeight(titleFrame)) : titleFrame;
|
imageFrame = isImageViewShowing ? CGRectSetY(imageFrame, CGRectGetHeight(self.bounds) - contentEdgeInsets.bottom - titleTotalSize.height - spacingBetweenImageAndTitle - self.imageEdgeInsets.bottom - CGRectGetHeight(imageFrame)) : imageFrame;
|
break;
|
case UIControlContentVerticalAlignmentFill: {
|
if (isImageViewShowing && isTitleLabelShowing) {
|
|
// 同时显示图片和 label 的情况下,图片高度按本身大小显示,剩余空间留给 label
|
imageFrame = isImageViewShowing ? CGRectSetY(imageFrame, contentEdgeInsets.top + self.imageEdgeInsets.top) : imageFrame;
|
titleFrame = isTitleLabelShowing ? CGRectSetY(titleFrame, contentEdgeInsets.top + imageTotalSize.height + spacingBetweenImageAndTitle + self.titleEdgeInsets.top) : titleFrame;
|
titleFrame = isTitleLabelShowing ? CGRectSetHeight(titleFrame, CGRectGetHeight(self.bounds) - contentEdgeInsets.bottom - self.titleEdgeInsets.bottom - CGRectGetMinY(titleFrame)) : titleFrame;
|
|
} else if (isImageViewShowing) {
|
imageFrame = CGRectSetY(imageFrame, contentEdgeInsets.top + self.imageEdgeInsets.top);
|
imageFrame = CGRectSetHeight(imageFrame, contentSize.height - UIEdgeInsetsGetVerticalValue(self.imageEdgeInsets));
|
} else {
|
titleFrame = CGRectSetY(titleFrame, contentEdgeInsets.top + self.titleEdgeInsets.top);
|
titleFrame = CGRectSetHeight(titleFrame, contentSize.height - UIEdgeInsetsGetVerticalValue(self.titleEdgeInsets));
|
}
|
}
|
break;
|
}
|
} else {
|
switch (self.contentVerticalAlignment) {
|
case UIControlContentVerticalAlignmentTop:
|
titleFrame = isTitleLabelShowing ? CGRectSetY(titleFrame, contentEdgeInsets.top + self.titleEdgeInsets.top) : titleFrame;
|
imageFrame = isImageViewShowing ? CGRectSetY(imageFrame, contentEdgeInsets.top + titleTotalSize.height + spacingBetweenImageAndTitle + self.imageEdgeInsets.top) : imageFrame;
|
break;
|
case UIControlContentVerticalAlignmentCenter: {
|
CGFloat contentHeight = imageTotalSize.height + titleTotalSize.height + spacingBetweenImageAndTitle;
|
CGFloat minY = CGFloatGetCenter(contentSize.height, contentHeight) + contentEdgeInsets.top;
|
titleFrame = isTitleLabelShowing ? CGRectSetY(titleFrame, minY + self.titleEdgeInsets.top) : titleFrame;
|
imageFrame = isImageViewShowing ? CGRectSetY(imageFrame, minY + titleTotalSize.height + spacingBetweenImageAndTitle + self.imageEdgeInsets.top) : imageFrame;
|
}
|
break;
|
case UIControlContentVerticalAlignmentBottom:
|
imageFrame = isImageViewShowing ? CGRectSetY(imageFrame, CGRectGetHeight(self.bounds) - contentEdgeInsets.bottom - self.imageEdgeInsets.bottom - CGRectGetHeight(imageFrame)) : imageFrame;
|
titleFrame = isTitleLabelShowing ? CGRectSetY(titleFrame, CGRectGetHeight(self.bounds) - contentEdgeInsets.bottom - imageTotalSize.height - spacingBetweenImageAndTitle - self.titleEdgeInsets.bottom - CGRectGetHeight(titleFrame)) : titleFrame;
|
break;
|
case UIControlContentVerticalAlignmentFill: {
|
if (isImageViewShowing && isTitleLabelShowing) {
|
|
// 同时显示图片和 label 的情况下,图片高度按本身大小显示,剩余空间留给 label
|
imageFrame = CGRectSetY(imageFrame, CGRectGetHeight(self.bounds) - contentEdgeInsets.bottom - self.imageEdgeInsets.bottom - CGRectGetHeight(imageFrame));
|
titleFrame = CGRectSetY(titleFrame, contentEdgeInsets.top + self.titleEdgeInsets.top);
|
titleFrame = CGRectSetHeight(titleFrame, CGRectGetHeight(self.bounds) - contentEdgeInsets.bottom - imageTotalSize.height - spacingBetweenImageAndTitle - self.titleEdgeInsets.bottom - CGRectGetMinY(titleFrame));
|
|
} else if (isImageViewShowing) {
|
imageFrame = CGRectSetY(imageFrame, contentEdgeInsets.top + self.imageEdgeInsets.top);
|
imageFrame = CGRectSetHeight(imageFrame, contentSize.height - UIEdgeInsetsGetVerticalValue(self.imageEdgeInsets));
|
} else {
|
titleFrame = CGRectSetY(titleFrame, contentEdgeInsets.top + self.titleEdgeInsets.top);
|
titleFrame = CGRectSetHeight(titleFrame, contentSize.height - UIEdgeInsetsGetVerticalValue(self.titleEdgeInsets));
|
}
|
}
|
break;
|
}
|
}
|
|
if (isImageViewShowing) {
|
imageFrame = CGRectFlatted(imageFrame);
|
self._qmui_imageView.frame = imageFrame;
|
}
|
if (isTitleLabelShowing) {
|
titleFrame = CGRectFlatted(titleFrame);
|
self.titleLabel.frame = titleFrame;
|
}
|
|
} else if (self.imagePosition == QMUIButtonImagePositionLeft || self.imagePosition == QMUIButtonImagePositionRight) {
|
|
if (isTitleLabelShowing) {
|
titleLimitSize = CGSizeMake(contentSize.width - UIEdgeInsetsGetHorizontalValue(self.titleEdgeInsets) - imageTotalSize.width - spacingBetweenImageAndTitle, contentSize.height - UIEdgeInsetsGetVerticalValue(self.titleEdgeInsets));
|
CGSize titleSize = [self.titleLabel sizeThatFits:titleLimitSize];
|
titleSize.width = MIN(titleLimitSize.width, titleSize.width);
|
titleSize.height = MIN(titleLimitSize.height, titleSize.height);
|
titleFrame = CGRectMakeWithSize(titleSize);
|
titleTotalSize = CGSizeMake(titleSize.width + UIEdgeInsetsGetHorizontalValue(self.titleEdgeInsets), titleSize.height + UIEdgeInsetsGetVerticalValue(self.titleEdgeInsets));
|
}
|
|
switch (self.contentVerticalAlignment) {
|
case UIControlContentVerticalAlignmentTop:
|
imageFrame = isImageViewShowing ? CGRectSetY(imageFrame, contentEdgeInsets.top + self.imageEdgeInsets.top) : imageFrame;
|
titleFrame = isTitleLabelShowing ? CGRectSetY(titleFrame, contentEdgeInsets.top + self.titleEdgeInsets.top) : titleFrame;
|
|
break;
|
case UIControlContentVerticalAlignmentCenter:
|
imageFrame = isImageViewShowing ? CGRectSetY(imageFrame, contentEdgeInsets.top + CGFloatGetCenter(contentSize.height, CGRectGetHeight(imageFrame)) + self.imageEdgeInsets.top) : imageFrame;
|
titleFrame = isTitleLabelShowing ? CGRectSetY(titleFrame, contentEdgeInsets.top + CGFloatGetCenter(contentSize.height, CGRectGetHeight(titleFrame)) + self.titleEdgeInsets.top) : titleFrame;
|
break;
|
case UIControlContentVerticalAlignmentBottom:
|
imageFrame = isImageViewShowing ? CGRectSetY(imageFrame, CGRectGetHeight(self.bounds) - contentEdgeInsets.bottom - self.imageEdgeInsets.bottom - CGRectGetHeight(imageFrame)) : imageFrame;
|
titleFrame = isTitleLabelShowing ? CGRectSetY(titleFrame, CGRectGetHeight(self.bounds) - contentEdgeInsets.bottom - self.titleEdgeInsets.bottom - CGRectGetHeight(titleFrame)) : titleFrame;
|
break;
|
case UIControlContentVerticalAlignmentFill:
|
if (isImageViewShowing) {
|
imageFrame = CGRectSetY(imageFrame, contentEdgeInsets.top + self.imageEdgeInsets.top);
|
imageFrame = CGRectSetHeight(imageFrame, contentSize.height - UIEdgeInsetsGetVerticalValue(self.imageEdgeInsets));
|
}
|
if (isTitleLabelShowing) {
|
titleFrame = CGRectSetY(titleFrame, contentEdgeInsets.top + self.titleEdgeInsets.top);
|
titleFrame = CGRectSetHeight(titleFrame, contentSize.height - UIEdgeInsetsGetVerticalValue(self.titleEdgeInsets));
|
}
|
break;
|
}
|
|
if (self.imagePosition == QMUIButtonImagePositionLeft) {
|
switch (self.contentHorizontalAlignment) {
|
case UIControlContentHorizontalAlignmentLeft:
|
imageFrame = isImageViewShowing ? CGRectSetX(imageFrame, contentEdgeInsets.left + self.imageEdgeInsets.left) : imageFrame;
|
titleFrame = isTitleLabelShowing ? CGRectSetX(titleFrame, contentEdgeInsets.left + imageTotalSize.width + spacingBetweenImageAndTitle + self.titleEdgeInsets.left) : titleFrame;
|
break;
|
case UIControlContentHorizontalAlignmentCenter: {
|
CGFloat contentWidth = imageTotalSize.width + spacingBetweenImageAndTitle + titleTotalSize.width;
|
CGFloat minX = contentEdgeInsets.left + CGFloatGetCenter(contentSize.width, contentWidth);
|
imageFrame = isImageViewShowing ? CGRectSetX(imageFrame, minX + self.imageEdgeInsets.left) : imageFrame;
|
titleFrame = isTitleLabelShowing ? CGRectSetX(titleFrame, minX + imageTotalSize.width + spacingBetweenImageAndTitle + self.titleEdgeInsets.left) : titleFrame;
|
}
|
break;
|
case UIControlContentHorizontalAlignmentRight: {
|
if (imageTotalSize.width + spacingBetweenImageAndTitle + titleTotalSize.width > contentSize.width) {
|
// 图片和文字总宽超过按钮宽度,则优先完整显示图片
|
imageFrame = isImageViewShowing ? CGRectSetX(imageFrame, contentEdgeInsets.left + self.imageEdgeInsets.left) : imageFrame;
|
titleFrame = isTitleLabelShowing ? CGRectSetX(titleFrame, contentEdgeInsets.left + imageTotalSize.width + spacingBetweenImageAndTitle + self.titleEdgeInsets.left) : titleFrame;
|
} else {
|
// 内容不超过按钮宽度,则靠右布局即可
|
titleFrame = isTitleLabelShowing ? CGRectSetX(titleFrame, CGRectGetWidth(self.bounds) - contentEdgeInsets.right - self.titleEdgeInsets.right - CGRectGetWidth(titleFrame)) : titleFrame;
|
imageFrame = isImageViewShowing ? CGRectSetX(imageFrame, CGRectGetWidth(self.bounds) - contentEdgeInsets.right - titleTotalSize.width - spacingBetweenImageAndTitle - imageTotalSize.width + self.imageEdgeInsets.left) : imageFrame;
|
}
|
}
|
break;
|
case UIControlContentHorizontalAlignmentFill: {
|
if (isImageViewShowing && isTitleLabelShowing) {
|
// 同时显示图片和 label 的情况下,图片按本身宽度显示,剩余空间留给 label
|
imageFrame = CGRectSetX(imageFrame, contentEdgeInsets.left + self.imageEdgeInsets.left);
|
titleFrame = CGRectSetX(titleFrame, contentEdgeInsets.left + imageTotalSize.width + spacingBetweenImageAndTitle + self.titleEdgeInsets.left);
|
titleFrame = CGRectSetWidth(titleFrame, CGRectGetWidth(self.bounds) - contentEdgeInsets.right - self.titleEdgeInsets.right - CGRectGetMinX(titleFrame));
|
} else if (isImageViewShowing) {
|
imageFrame = CGRectSetX(imageFrame, contentEdgeInsets.left + self.imageEdgeInsets.left);
|
imageFrame = CGRectSetWidth(imageFrame, contentSize.width - UIEdgeInsetsGetHorizontalValue(self.imageEdgeInsets));
|
} else {
|
titleFrame = CGRectSetX(titleFrame, contentEdgeInsets.left + self.titleEdgeInsets.left);
|
titleFrame = CGRectSetWidth(titleFrame, contentSize.width - UIEdgeInsetsGetHorizontalValue(self.titleEdgeInsets));
|
}
|
}
|
break;
|
default:
|
break;
|
}
|
} else {
|
switch (self.contentHorizontalAlignment) {
|
case UIControlContentHorizontalAlignmentLeft: {
|
if (imageTotalSize.width + spacingBetweenImageAndTitle + titleTotalSize.width > contentSize.width) {
|
// 图片和文字总宽超过按钮宽度,则优先完整显示图片
|
imageFrame = isImageViewShowing ? CGRectSetX(imageFrame, CGRectGetWidth(self.bounds) - contentEdgeInsets.right - self.imageEdgeInsets.right - CGRectGetWidth(imageFrame)) : imageFrame;
|
titleFrame = isTitleLabelShowing ? CGRectSetX(titleFrame, CGRectGetWidth(self.bounds) - contentEdgeInsets.right - imageTotalSize.width - spacingBetweenImageAndTitle - titleTotalSize.width + self.titleEdgeInsets.left) : titleFrame;
|
} else {
|
// 内容不超过按钮宽度,则靠左布局即可
|
titleFrame = isTitleLabelShowing ? CGRectSetX(titleFrame, contentEdgeInsets.left + self.titleEdgeInsets.left) : titleFrame;
|
imageFrame = isImageViewShowing ? CGRectSetX(imageFrame, contentEdgeInsets.left + titleTotalSize.width + spacingBetweenImageAndTitle + self.imageEdgeInsets.left) : imageFrame;
|
}
|
}
|
break;
|
case UIControlContentHorizontalAlignmentCenter: {
|
CGFloat contentWidth = imageTotalSize.width + spacingBetweenImageAndTitle + titleTotalSize.width;
|
CGFloat minX = contentEdgeInsets.left + CGFloatGetCenter(contentSize.width, contentWidth);
|
titleFrame = isTitleLabelShowing ? CGRectSetX(titleFrame, minX + self.titleEdgeInsets.left) : titleFrame;
|
imageFrame = isImageViewShowing ? CGRectSetX(imageFrame, minX + titleTotalSize.width + spacingBetweenImageAndTitle + self.imageEdgeInsets.left) : imageFrame;
|
}
|
break;
|
case UIControlContentHorizontalAlignmentRight:
|
imageFrame = isImageViewShowing ? CGRectSetX(imageFrame, CGRectGetWidth(self.bounds) - contentEdgeInsets.right - self.imageEdgeInsets.right - CGRectGetWidth(imageFrame)) : imageFrame;
|
titleFrame = isTitleLabelShowing ? CGRectSetX(titleFrame, CGRectGetWidth(self.bounds) - contentEdgeInsets.right - imageTotalSize.width - spacingBetweenImageAndTitle - self.titleEdgeInsets.right - CGRectGetWidth(titleFrame)) : titleFrame;
|
break;
|
case UIControlContentHorizontalAlignmentFill: {
|
if (isImageViewShowing && isTitleLabelShowing) {
|
// 图片按自身大小显示,剩余空间由标题占满
|
imageFrame = CGRectSetX(imageFrame, CGRectGetWidth(self.bounds) - contentEdgeInsets.right - self.imageEdgeInsets.right - CGRectGetWidth(imageFrame));
|
titleFrame = CGRectSetX(titleFrame, contentEdgeInsets.left + self.titleEdgeInsets.left);
|
titleFrame = CGRectSetWidth(titleFrame, CGRectGetMinX(imageFrame) - self.imageEdgeInsets.left - spacingBetweenImageAndTitle - self.titleEdgeInsets.right - CGRectGetMinX(titleFrame));
|
|
} else if (isImageViewShowing) {
|
imageFrame = CGRectSetX(imageFrame, contentEdgeInsets.left + self.imageEdgeInsets.left);
|
imageFrame = CGRectSetWidth(imageFrame, contentSize.width - UIEdgeInsetsGetHorizontalValue(self.imageEdgeInsets));
|
} else {
|
titleFrame = CGRectSetX(titleFrame, contentEdgeInsets.left + self.titleEdgeInsets.left);
|
titleFrame = CGRectSetWidth(titleFrame, contentSize.width - UIEdgeInsetsGetHorizontalValue(self.titleEdgeInsets));
|
}
|
}
|
break;
|
default:
|
break;
|
}
|
}
|
|
if (isImageViewShowing) {
|
imageFrame = CGRectFlatted(imageFrame);
|
self._qmui_imageView.frame = imageFrame;
|
}
|
if (isTitleLabelShowing) {
|
titleFrame = CGRectFlatted(titleFrame);
|
self.titleLabel.frame = titleFrame;
|
}
|
}
|
}
|
|
- (void)setSpacingBetweenImageAndTitle:(CGFloat)spacingBetweenImageAndTitle {
|
_spacingBetweenImageAndTitle = spacingBetweenImageAndTitle;
|
|
[self setNeedsLayout];
|
}
|
|
- (void)setImagePosition:(QMUIButtonImagePosition)imagePosition {
|
_imagePosition = imagePosition;
|
|
[self setNeedsLayout];
|
}
|
|
- (void)setHighlightedBackgroundColor:(UIColor *)highlightedBackgroundColor {
|
_highlightedBackgroundColor = highlightedBackgroundColor;
|
if (_highlightedBackgroundColor) {
|
// 只要开启了highlightedBackgroundColor,就默认不需要alpha的高亮
|
self.adjustsButtonWhenHighlighted = NO;
|
}
|
}
|
|
- (void)setHighlightedBorderColor:(UIColor *)highlightedBorderColor {
|
_highlightedBorderColor = highlightedBorderColor;
|
if (_highlightedBorderColor) {
|
// 只要开启了highlightedBorderColor,就默认不需要alpha的高亮
|
self.adjustsButtonWhenHighlighted = NO;
|
}
|
}
|
|
- (void)setHighlighted:(BOOL)highlighted {
|
[super setHighlighted:highlighted];
|
|
if (highlighted && !self.originBorderColor) {
|
// 手指按在按钮上会不断触发setHighlighted:,所以这里做了保护,设置过一次就不用再设置了
|
self.originBorderColor = [UIColor colorWithCGColor:self.layer.borderColor];
|
}
|
|
// 渲染背景色
|
if (self.highlightedBackgroundColor || self.highlightedBorderColor) {
|
[self adjustsButtonHighlighted];
|
}
|
// 如果此时是disabled,则disabled的样式优先
|
if (!self.enabled) {
|
return;
|
}
|
// 自定义highlighted样式
|
if (self.adjustsButtonWhenHighlighted) {
|
if (highlighted) {
|
self.alpha = ButtonHighlightedAlpha;
|
} else {
|
self.alpha = 1;
|
}
|
}
|
}
|
|
- (void)setEnabled:(BOOL)enabled {
|
[super setEnabled:enabled];
|
if (self.adjustsButtonWhenDisabled) {
|
self.alpha = enabled ? 1 : ButtonDisabledAlpha;
|
}
|
}
|
|
- (void)adjustsButtonHighlighted {
|
if (self.highlightedBackgroundColor) {
|
if (!self.highlightedBackgroundLayer) {
|
self.highlightedBackgroundLayer = [CALayer layer];
|
[self.highlightedBackgroundLayer qmui_removeDefaultAnimations];
|
[self.layer insertSublayer:self.highlightedBackgroundLayer atIndex:0];
|
}
|
self.highlightedBackgroundLayer.frame = self.bounds;
|
self.highlightedBackgroundLayer.cornerRadius = self.layer.cornerRadius;
|
self.highlightedBackgroundLayer.maskedCorners = self.layer.maskedCorners;
|
self.highlightedBackgroundLayer.backgroundColor = self.highlighted ? self.highlightedBackgroundColor.CGColor : UIColorClear.CGColor;
|
}
|
|
if (self.highlightedBorderColor) {
|
self.layer.borderColor = self.highlighted ? self.highlightedBorderColor.CGColor : self.originBorderColor.CGColor;
|
}
|
}
|
|
- (void)setAdjustsTitleTintColorAutomatically:(BOOL)adjustsTitleTintColorAutomatically {
|
_adjustsTitleTintColorAutomatically = adjustsTitleTintColorAutomatically;
|
[self updateTitleColorIfNeeded];
|
}
|
|
- (void)updateTitleColorIfNeeded {
|
if (self.adjustsTitleTintColorAutomatically && self.currentTitleColor) {
|
[self setTitleColor:self.tintColor forState:UIControlStateNormal];
|
}
|
if (self.adjustsTitleTintColorAutomatically && self.currentAttributedTitle) {
|
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:self.currentAttributedTitle];
|
[attributedString addAttribute:NSForegroundColorAttributeName value:self.tintColor range:NSMakeRange(0, attributedString.length)];
|
[self setAttributedTitle:attributedString forState:UIControlStateNormal];
|
}
|
}
|
|
- (void)setAdjustsImageTintColorAutomatically:(BOOL)adjustsImageTintColorAutomatically {
|
BOOL valueDifference = _adjustsImageTintColorAutomatically != adjustsImageTintColorAutomatically;
|
_adjustsImageTintColorAutomatically = adjustsImageTintColorAutomatically;
|
|
if (valueDifference) {
|
[self updateImageRenderingModeIfNeeded];
|
}
|
}
|
|
- (void)updateImageRenderingModeIfNeeded {
|
if (self.currentImage) {
|
NSArray<NSNumber *> *states = @[@(UIControlStateNormal), @(UIControlStateHighlighted), @(UIControlStateSelected), @(UIControlStateSelected|UIControlStateHighlighted), @(UIControlStateDisabled)];
|
|
for (NSNumber *number in states) {
|
UIImage *image = [self imageForState:number.unsignedIntegerValue];
|
if (!image) {
|
continue;
|
}
|
if (number.unsignedIntegerValue != UIControlStateNormal && image == [self imageForState:UIControlStateNormal]) {
|
continue;
|
}
|
|
if (self.adjustsImageTintColorAutomatically) {
|
// 这里的 setImage: 操作不需要使用 renderingMode 对 image 重新处理,而是放到重写的 setImage:forState 里去做就行了
|
[self setImage:image forState:[number unsignedIntegerValue]];
|
} else {
|
// 如果不需要用template的模式渲染,并且之前是使用template的,则把renderingMode改回Original
|
[self setImage:[image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forState:[number unsignedIntegerValue]];
|
}
|
}
|
}
|
}
|
|
- (void)setImage:(UIImage *)image forState:(UIControlState)state {
|
if (self.adjustsImageTintColorAutomatically) {
|
image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
}
|
|
[super setImage:image forState:state];
|
}
|
|
- (void)tintColorDidChange {
|
[super tintColorDidChange];
|
|
[self updateTitleColorIfNeeded];
|
|
if (self.adjustsImageTintColorAutomatically) {
|
[self updateImageRenderingModeIfNeeded];
|
}
|
}
|
|
- (void)setTintColorAdjustsTitleAndImage:(UIColor *)tintColorAdjustsTitleAndImage {
|
_tintColorAdjustsTitleAndImage = tintColorAdjustsTitleAndImage;
|
if (tintColorAdjustsTitleAndImage) {
|
self.tintColor = tintColorAdjustsTitleAndImage;
|
self.adjustsTitleTintColorAutomatically = YES;
|
self.adjustsImageTintColorAutomatically = YES;
|
}
|
}
|
|
- (void)setCornerRadius:(CGFloat)cornerRadius {
|
_cornerRadius = cornerRadius;
|
if (cornerRadius != QMUIButtonCornerRadiusAdjustsBounds) {
|
self.layer.cornerRadius = cornerRadius;
|
}
|
[self setNeedsLayout];
|
}
|
|
@end
|