我的自定义 UIButton 不能用于替换 leftBarButtonItem
Posted
技术标签:
【中文标题】我的自定义 UIButton 不能用于替换 leftBarButtonItem【英文标题】:My custom UIButton can't be used to replace leftBarButtonItem 【发布时间】:2019-03-22 07:17:42 【问题描述】:我从 UIControl 创建了一个自定义 UIButton 子类(称为“OZCustomButton”)。它在 Storyboard 中运行良好,但是当我尝试使用它以编程方式替换 leftBarButtonItem 后退按钮时,它的布局出现问题。
这是我用来替换 leftBarButtonItem 的代码。
OZCustomButton *customBackButton = [[OZCustomButton alloc] initWithFrame:CGRectZero];
customBackButton.buttonImage = [UIImage imageNamed:@"BackArrow"];
customBackButton.buttonText = @"Back";
[customBackButton sizeToFit];
NSLog(@"this size: %@", NSStringFromCGRect(customBackButton.frame));
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:customBackButton];
self.navigationItem.leftBarButtonItem = item;
但导航栏左侧没有显示任何内容。
导航栏:
Dubug View Hierarchy 工具会显示以下警告消息:
customBackButton 似乎在 View Hierarchy 中,但布局不正确。
这是我的 OZCustomButton 的代码。
OZCustomButton.h:
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
IB_DESIGNABLE
@interface OZCustomButton : UIControl <NSCoding>
@property (assign, nonatomic) IBInspectable CGFloat borderWidth;
@property (assign, nonatomic) IBInspectable CGFloat borderRadius;
@property (strong, nonatomic) IBInspectable UIColor *borderColor;
@property (strong, nonatomic) IBInspectable UIColor *fillColor;
@property (strong, nonatomic) IBInspectable UIColor *tintColor;
@property (strong, nonatomic) IBInspectable NSString *buttonText;
@property (assign, nonatomic) IBInspectable CGFloat textSize;
@property (assign, nonatomic) IBInspectable BOOL isTextBold;
@property (strong, nonatomic) UIFont *textFont;
@property (nullable, strong, nonatomic) IBInspectable UIImage *buttonImage;
@property (nullable, strong, nonatomic) NSArray *gradientColors;
@end
NS_ASSUME_NONNULL_END
OZCustomButton.m
#import "OZCustomButton.h"
#import "UIColor+Custom.h"
#import "CAGradientLayer+Utilities.h"
@interface OZCustomButton ()
@property (strong, nonatomic) UILabel *buttonLabel;
@property (nullable, strong, nonatomic) UIImageView *buttonImageView;
@property (nullable, strong, nonatomic) CAGradientLayer *gradientLayer;
@property (nullable, strong, nonatomic) UIStackView *stackView;
@end
@implementation OZCustomButton
- (instancetype)initWithFrame:(CGRect)frame
self = [super initWithFrame:frame];
if (self)
[self setupDefaults];
return self;
- (instancetype)initWithCoder:(NSCoder *)aDecoder
self = [super initWithCoder:aDecoder];
if (self)
[self setupDefaults];
return self;
- (void)layoutLabelAndImageView
_buttonLabel = [[UILabel alloc] initWithFrame:CGRectZero];
_buttonLabel.numberOfLines = 1; // need to set to 1
_buttonLabel.textAlignment = NSTextAlignmentCenter;
//_buttonLabel.backgroundColor = [UIColor redColor];
_buttonImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
_buttonImageView.contentMode = UIViewContentModeScaleAspectFit;
//_buttonImageView.backgroundColor = [UIColor redColor];
_stackView = [[UIStackView alloc] init];
_stackView.axis = UILayoutConstraintAxisHorizontal;
_stackView.alignment = UIStackViewAlignmentCenter;
_stackView.distribution = UIStackViewDistributionFillProportionally;
_stackView.spacing = 8;
_stackView.userInteractionEnabled = NO;
[_stackView addArrangedSubview:_buttonImageView];
[_stackView addArrangedSubview:_buttonLabel];
_stackView.translatesAutoresizingMaskIntoConstraints = false;
[self addSubview:_stackView];
[[_stackView.centerXAnchor constraintEqualToAnchor:self.centerXAnchor] setActive:YES];
[[_stackView.centerYAnchor constraintEqualToAnchor:self.centerYAnchor] setActive:YES];
- (void)layoutGradientLayer
_gradientLayer = [CAGradientLayer createGradientLayerWithBounds:self.bounds
colors:nil
direction:GradientFromLeftTopToRightBottom
locations:@[@0.0, @1.0]];
_gradientLayer.anchorPoint = CGPointMake(0, 0);
[self.layer insertSublayer:_gradientLayer below:_stackView.layer];
- (void)setupDefaults
_borderWidth = 0.0f;
_borderRadius = 0.0f;
_borderColor = [UIColor blackColor];
_fillColor = [UIColor whiteColor];
_buttonText = @"Button";
_tintColor = [UIColor blackColor];
_textSize = 17.0f;
_isTextBold = false;
_textFont = _isTextBold ? [UIFont fontWithName:@"AlteHaasGrotesk_Bold" size:_textSize] : [UIFont fontWithName:@"AlteHaasGrotesk" size:_textSize];
_gradientColors = nil;
_buttonImage = nil;
[self layoutLabelAndImageView];
[self updateView];
- (void)updateView
self.layer.borderColor = _borderColor.CGColor;
self.layer.borderWidth = _borderWidth;
self.layer.cornerRadius = _borderRadius;
self.layer.masksToBounds = true;
self.layer.backgroundColor = _fillColor.CGColor;
// update button text label
_buttonLabel.text = _buttonText;
_buttonLabel.textColor = _tintColor;
_textFont = _isTextBold ? [UIFont fontWithName:@"AlteHaasGrotesk_Bold" size:_textSize] : [UIFont fontWithName:@"AlteHaasGrotesk" size:_textSize];
_buttonLabel.font = _textFont;
_buttonLabel.textAlignment = NSTextAlignmentCenter;
[_buttonLabel sizeToFit];
// update button image
if (_buttonImage != nil)
_buttonImageView.hidden = NO;
_buttonImageView.image = [_buttonImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
_buttonImageView.tintColor = _tintColor;
[_buttonImageView sizeToFit];
else
_buttonImageView.image = nil;
_buttonImageView.hidden = YES;
[_buttonImageView sizeToFit];
// update gradient layer
if (_gradientColors != nil)
// if gradient layer is not initialized, call layoutGradientLayer()
if (_gradientLayer == nil)
[self layoutGradientLayer];
// transform the UIColor to CGColorRef
NSMutableArray *colors = [NSMutableArray arrayWithCapacity:_gradientColors.count];
for (UIColor *color in _gradientColors)
[colors addObject:(id)[color CGColor]];
if (colors.count > 0)
_gradientLayer.colors = [colors copy];
#pragma mark - setters
- (void)setTextSize:(CGFloat)textSize
if (_textSize != textSize)
_textSize = textSize;
_textFont = _isTextBold ? [UIFont fontWithName:@"AlteHaasGrotesk_Bold" size:_textSize] : [UIFont fontWithName:@"AlteHaasGrotesk" size:_textSize];
[self updateView];
- (void)setBorderColor:(UIColor *)borderColor
if (_borderColor != borderColor)
_borderColor = borderColor;
[self updateView];
- (void)setBorderWidth:(CGFloat)borderWidth
if (_borderWidth != borderWidth)
_borderWidth = borderWidth;
[self updateView];
- (void)setBorderRadius:(CGFloat)borderRadius
if (_borderRadius != borderRadius)
_borderRadius = borderRadius;
[self updateView];
- (void)setFillColor:(UIColor *)fillColor
if (_fillColor != fillColor)
_fillColor = fillColor;
[self updateView];
- (void)setTintColor:(UIColor *)tintColor
if (_tintColor != tintColor)
_tintColor = tintColor;
[self updateView];
- (void)setButtonText:(NSString *)buttonText
if (_buttonText != buttonText)
_buttonText = buttonText;
[self updateView];
- (void)setTextFont:(UIFont *)textFont
if (_textFont != textFont)
_textFont = textFont;
[self updateView];
- (void)setGradientColors:(NSArray *)gradientColors
if (_gradientColors != gradientColors)
_gradientColors = gradientColors;
[self updateView];
- (void)setButtonImage:(UIImage *)buttonImage
if (_buttonImage != buttonImage)
_buttonImage = buttonImage;
[self updateView];
- (void)setIsTextBold:(BOOL)isTextBold
if (_isTextBold != isTextBold)
_isTextBold = isTextBold;
[self updateView];
#pragma mark - UIControl actions
- (void)setHighlighted:(BOOL)highlighted
[super setHighlighted:highlighted];
CABasicAnimation *fadeAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
fadeAnimation.duration = 0.2f;
if (highlighted)
fadeAnimation.toValue = @0.6f;
else
fadeAnimation.toValue = @1.0f;
self.buttonLabel.layer.opacity = [fadeAnimation.toValue floatValue];
self.layer.opacity = [fadeAnimation.toValue floatValue];
[self.buttonLabel.layer addAnimation:fadeAnimation forKey:@"textFadeAnimation"];
[self.layer addAnimation:fadeAnimation forKey:@"backgroundFadeAnimation"];
- (void)setEnabled:(BOOL)enabled
[super setEnabled:enabled];
if (enabled)
self.layer.backgroundColor = self.fillColor.CGColor;
self.buttonLabel.textColor = self.tintColor;
else
self.layer.backgroundColor = [UIColor lighterColorForColor:self.fillColor].CGColor;
self.buttonLabel.textColor = [UIColor lightGrayColor];
#pragma mark - Override functions
- (void)layoutSubviews
[super layoutSubviews];
[self updateView];
- (CGSize)sizeThatFits:(CGSize)size
CGFloat minWidth = _buttonImageView.frame.size.width + 8 + _buttonLabel.frame.size.width;
CGFloat minHeight = MAX(_buttonImageView.frame.size.height, _buttonLabel.frame.size.height);
return CGSizeMake(minWidth + 6, minHeight + 4);
@end
【问题讨论】:
设置框架并尝试一次CGRect setFframe = CGRectMake(15,5, 25,25); OZCustomButton *customBackButton = [[OZCustomButton alloc] initWithFrame:setFframe];
检查文本颜色。它不应该是白色的。以及您正在设置的图像的颜色是什么。 ?另见this answer
如 viewDebugger 所示,您的自定义按钮存在约束问题,无法确定 x 位置。我建议您对所有子视图正确使用NSLayoutConstraints
以使其正常工作。
我发现堆栈视图的自动布局代码导致的问题。如果我为堆栈视图设置一个固定框架,它将能够显示在 leftBarButtonItem 上。但是,为什么自动布局代码不能正常工作??
【参考方案1】:
我找到了一种解决方案。我没有设置 centenX 锚点和 centerY 锚点,而是将其更改为使用左、右、上和下锚点:
[[_stackView.topAnchor constraintEqualToAnchor:_stackView.superview.topAnchor] setActive:YES];
[[_stackView.bottomAnchor constraintEqualToAnchor:_stackView.superview.bottomAnchor] setActive:YES];
[[_stackView.leftAnchor constraintEqualToAnchor:_stackView.superview.leftAnchor] setActive:YES];
[[_stackView.rightAnchor constraintEqualToAnchor:_stackView.superview.rightAnchor] setActive:YES];
然后它得到了正确的布局。
如果您有其他解决问题的方法,请告诉我。
【讨论】:
以上是关于我的自定义 UIButton 不能用于替换 leftBarButtonItem的主要内容,如果未能解决你的问题,请参考以下文章
针对 iPhone 4s 和 5 上 xcode 5 中的背景图像问题的自定义 UIButton 对齐 - 添加约束不能解决问题