// // HDLEZOPButton.m // EZSDK // // Created by Davin on 2023/6/20. // #import "HDLEZOPButton.h" #define HDLOPScreenScale ([[UIScreen mainScreen] scale]) #pragma mark - Clang #define ArgumentToString(macro) #macro #define ClangWarningConcat(warning_name) ArgumentToString(clang diagnostic ignored warning_name) /// 参数可直接传入 clang 的 warning 名,warning 列表参考:https://clang.llvm.org/docs/DiagnosticsReference.html #define BeginIgnoreClangWarning(warningName) _Pragma("clang diagnostic push") _Pragma(ClangWarningConcat(#warningName)) #define EndIgnoreClangWarning _Pragma("clang diagnostic pop") #define HDLOPBeginIgnorePerformSelectorLeaksWarning BeginIgnoreClangWarning(-Warc-performSelector-leaks) #define HDLOPEndIgnorePerformSelectorLeaksWarning EndIgnoreClangWarning #define CGSizeMax CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) CG_INLINE CGFloat removeFloatMin(CGFloat floatValue) { return floatValue == CGFLOAT_MIN ? 0 : floatValue; } CG_INLINE CGFloat flatSpecificScale(CGFloat floatValue, CGFloat scale) { floatValue = removeFloatMin(floatValue); scale = scale ?: HDLOPScreenScale; CGFloat flattedValue = ceil(floatValue * scale) / scale; return flattedValue; } CG_INLINE CGFloat flat(CGFloat floatValue) { return flatSpecificScale(floatValue, 0); } CG_INLINE UIEdgeInsets UIEdgeInsetsRemoveFloatMin(UIEdgeInsets insets) { UIEdgeInsets result = UIEdgeInsetsMake(removeFloatMin(insets.top), removeFloatMin(insets.left), removeFloatMin(insets.bottom), removeFloatMin(insets.right)); return result; } /// 获取UIEdgeInsets在水平方向上的值 CG_INLINE CGFloat UIEdgeInsetsGetHorizontalValue(UIEdgeInsets insets) { return insets.left + insets.right; } /// 获取UIEdgeInsets在垂直方向上的值 CG_INLINE CGFloat UIEdgeInsetsGetVerticalValue(UIEdgeInsets insets) { return insets.top + insets.bottom; } /// 传入size,返回一个x/y为0的CGRect CG_INLINE CGRect CGRectMakeWithSize(CGSize size) { return CGRectMake(0, 0, size.width, size.height); } CG_INLINE CGRect CGRectSetX(CGRect rect, CGFloat x) { rect.origin.x = flat(x); return rect; } CG_INLINE CGRect CGRectSetY(CGRect rect, CGFloat y) { rect.origin.y = flat(y); return rect; } CG_INLINE CGRect CGRectSetXY(CGRect rect, CGFloat x, CGFloat y) { rect.origin.x = flat(x); rect.origin.y = flat(y); return rect; } CG_INLINE CGRect CGRectSetWidth(CGRect rect, CGFloat width) { if (width < 0) { return rect; } rect.size.width = flat(width); return rect; } CG_INLINE CGRect CGRectSetHeight(CGRect rect, CGFloat height) { if (height < 0) { return rect; } rect.size.height = flat(height); return rect; } /// 将一个 CGSize 像素对齐 CG_INLINE CGSize CGSizeFlatted(CGSize size) { return CGSizeMake(flat(size.width), flat(size.height)); } CG_INLINE CGRect CGRectSetSize(CGRect rect, CGSize size) { rect.size = CGSizeFlatted(size); return rect; } /// 用于居中运算 CG_INLINE CGFloat CGFloatGetCenter(CGFloat parent, CGFloat child) { return flat((parent - child) / 2.0); } /// 对CGRect的x/y、width/height都调用一次flat,以保证像素对齐 CG_INLINE CGRect CGRectFlatted(CGRect rect) { return CGRectMake(flat(rect.origin.x), flat(rect.origin.y), flat(rect.size.width), flat(rect.size.height)); } /// 判断一个 CGSize 是否为空(宽或高为0) CG_INLINE BOOL CGSizeIsEmpty(CGSize size) { return size.width <= 0 || size.height <= 0; } const CGFloat HDLOPButtonCornerRadiusAdjustsBounds = -1; @interface HDLEZOPButton () @property(nonatomic, strong) CALayer *highlightedBackgroundLayer; @property(nonatomic, strong) UIColor *originBorderColor; @end @implementation HDLEZOPButton - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { self.tintColor = HDLEZHEXCOLOR(0x5C62FE,0.1); [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 = HDLEZOPButtonImagePositionLeft; } // 系统访问 self.imageView 会触发 layout,而私有方法 _imageView 则是简单地访问 imageView,所以在 HDLOPButton layoutSubviews 里应该用这个方法 - (UIImageView *)_hdlop_imageView { HDLOPBeginIgnorePerformSelectorLeaksWarning return [self performSelector:NSSelectorFromString(@"_imageView")]; HDLOPEndIgnorePerformSelectorLeaksWarning } - (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 HDLEZOPButtonImagePositionTop: case HDLEZOPButtonImagePositionBottom: { // 图片和文字上下排版时,宽度以文字或图片的最大宽度为最终宽度 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);// HDLOPButton 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 HDLEZOPButtonImagePositionLeft: case HDLEZOPButtonImagePositionRight: { // 图片和文字水平排版时,高度以文字或图片的最大高度为最终高度 // 注意这里有一个和系统不一致的行为:当 titleLabel 为多行时,系统的 sizeThatFits: 计算结果固定是单行的,所以当 HDLOPButtonImagePositionLeft 并且titleLabel 多行的情况下,HDLOPButton 计算的结果与系统不一致 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);// HDLOPButton 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 == HDLOPButtonCornerRadiusAdjustsBounds) { 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._hdlop_imageView.image ? [self._hdlop_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 布局时会产生偏移,因此这里做一次保护 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._hdlop_imageView); } if (isTitleLabelShowing) { makesureBoundsPositive(self.titleLabel); } if (self.imagePosition == HDLEZOPButtonImagePositionTop || self.imagePosition == HDLEZOPButtonImagePositionBottom) { 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 == HDLEZOPButtonImagePositionTop) { 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._hdlop_imageView.frame = imageFrame; } if (isTitleLabelShowing) { titleFrame = CGRectFlatted(titleFrame); self.titleLabel.frame = titleFrame; } } else if (self.imagePosition == HDLEZOPButtonImagePositionLeft || self.imagePosition == HDLEZOPButtonImagePositionRight) { 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 == HDLEZOPButtonImagePositionLeft) { 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._hdlop_imageView.frame = imageFrame; } if (isTitleLabelShowing) { titleFrame = CGRectFlatted(titleFrame); self.titleLabel.frame = titleFrame; } } } - (void)setSpacingBetweenImageAndTitle:(CGFloat)spacingBetweenImageAndTitle { _spacingBetweenImageAndTitle = spacingBetweenImageAndTitle; [self setNeedsLayout]; } - (void)setImagePosition:(HDLEZOPButtonImagePosition)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 (!enabled && self.adjustsButtonWhenDisabled) { // self.alpha = ButtonDisabledAlpha; self.alpha = 1; } else { self.alpha = 1; } } //- (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 *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 != HDLOPButtonCornerRadiusAdjustsBounds) { self.layer.cornerRadius = cornerRadius; } [self setNeedsLayout]; } @end