我的自定义 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的主要内容,如果未能解决你的问题,请参考以下文章

UIButton 的自定义类视图

针对 iPhone 4s 和 5 上 xcode 5 中的背景图像问题的自定义 UIButton 对齐 - 添加约束不能解决问题

iPhone上的自定义相机工具栏

带有圆形图像视图的自定义圆形 UIButton

如何为数组中的每个创建一个具有唯一图像和选择器的自定义 UIButton?

自定义 UIButton - IBAction 不起作用