子类化 UIButton 并将 UILabel 添加到视图层次结构会导致 currentImage 压缩

Posted

技术标签:

【中文标题】子类化 UIButton 并将 UILabel 添加到视图层次结构会导致 currentImage 压缩【英文标题】:Subclassing UIButton and adding UILabel to the view hierarchy causes currentImage to condense 【发布时间】:2015-12-16 19:46:15 【问题描述】:

我最初希望在突出显示按钮时出现一个蓝色椭圆形,并且标题位于该图像的中心。

当我设置背景图片时,我的蓝色椭圆会被拉长。

当我设置前景图像时,蓝色椭圆形完美地出现了,但结果,这将我的 titleLabel 偏移到了一边,使它看起来很奇怪。

我没有为插入而烦恼,而是将 UIButton 子类化,并使用自动布局约束将 UILabel 添加到视图的中心。

现在,当我按下按钮时,会出现蓝色椭圆形,但图像会被压缩。当我再次按下按钮时,蓝色椭圆会正常显示。根据doc,我发现图像的凝结很奇怪:

使用图像 (currentImage) 字段,您可以指定要在按钮内容中显示的图像。如果按钮有标题,则此图像显示在其左侧,否则居中。图片不会拉伸或压缩,因此请确保选择尺寸合适的图片以显示在您的按钮中。

当我从 UIButton 的视图层次结构中删除 UILabel 时,前景图像看起来又正常了。这可能是什么原因造成的?

- (void)awakeFromNib 
    [self configureTextLabel];


- (void)configureTextLabel 
    self.textLabel = [UILabel new];
    self.textLabel.text = self.text;
    self.textLabel.translatesAutoresizingMaskIntoConstraints = NO;

    [self refreshLabelTextColor];

    [self addSubview:self.textLabel];
    [self addConstraint:[NSLayoutConstraint constraintWithItem:self
                                                     attribute:NSLayoutAttributeCenterX
                                                     relatedBy:NSLayoutRelationEqual
                                                        toItem:self.textLabel
                                                     attribute:NSLayoutAttributeCenterX
                                                    multiplier:1.f
                                                      constant:0.f]];

    [self addConstraint:[NSLayoutConstraint constraintWithItem:self
                                                     attribute:NSLayoutAttributeCenterY
                                                     relatedBy:NSLayoutRelationEqual
                                                        toItem:self.textLabel
                                                     attribute:NSLayoutAttributeCenterY
                                                    multiplier:1.f
                                                      constant:0.f]];


- (void)setHighlighted:(BOOL)highlighted 
    [super setHighlighted:highlighted];

    if (self.text != nil) 
        [self refreshLabelTextColor];
    


- (void)refreshLabelTextColor 
    self.textLabel.textColor = self.highlighted ? [UIColor whiteColor] : [UIColor grayColor];

【问题讨论】:

会不会和图片的渲染方式有关系?我将图像的渲染模式设置为 UIImageRenderingModeAlwaysOriginal,但这并没有解决它:( 我认为我在上面的评论中所说的并不相关,它与颜色的关系比什么都重要。 【参考方案1】:

所以,我最终决定不继承 UIButton,并恢复使用 UIButton 的前景图像。

前景图像不再在 UIButton 中被压缩,但我仍然遇到数字向右偏移的问题,而不是在椭圆形背景图像上居中。

我决定使用 KVO 来观察按钮何时被突出显示,并调整前景图像和文本标签的插图。我提供了以下代码:

- (void)addKeyValueObserversToButtons 
    for (UIButton *button in self.buttons) 
        [button addObserver:self forKeyPath:@"highlighted" options:0 context:NULL];
    


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context 

    if ([keyPath isEqualToString:@"highlighted"]) 
        UIButton *button = nil;
        if ([object isKindOfClass:[UIButton class]]) 
            button = (UIButton *)object;
        

        UIImage *highlightImage = button.currentImage;
        button.imageEdgeInsets = UIEdgeInsetsMake(0, button.titleLabel.frame.size.width, 0, 0);
        button.titleEdgeInsets = UIEdgeInsetsMake(0, -highlightImage.size.width, 0, 0);
    

我很难调整插图的原因是因为我错误地假设前景图像在突出显示时始终位于按钮的中心。

最终发生的情况是,通过将前景图像包含到按钮中,它实质上增加了文本标签的宽度,并且 UIButton 将它们作为一个实体居中,同时考虑了两个宽度。

这就是前景图像看起来稍微偏向左侧的原因。

通过从前景图像的左侧添加文本标签宽度的插图,并从文本标签的左侧减去图像宽度的插图,这产生了预期的结果:

一个居中的图像,文本居中于图像本身。

【讨论】:

【参考方案2】:

对按钮进行私有扩展可能会更简单,它会重载以下 UIButton 方法中的一个或组合给定的内容矩形:

class UIButton 

    // these return the rectangle for the background (assumes bounds), the content (image + title) and for the image and title separately. the content rect is calculated based
    // on the title and image size and padding and then adjusted based on the control content alignment. there are no draw methods since the contents
    // are rendered in separate subviews (UIImageView, UILabel)

    open func backgroundRect(forBounds bounds: CGRect) -> CGRect

    open func contentRect(forBounds bounds: CGRect) -> CGRect

    open func titleRect(forContentRect contentRect: CGRect) -> CGRect

    open func imageRect(forContentRect contentRect: CGRect) -> CGRect

有关这些方法的更多信息,请参阅 Apple 的 Documentation“获取维度”。

【讨论】:

以上是关于子类化 UIButton 并将 UILabel 添加到视图层次结构会导致 currentImage 压缩的主要内容,如果未能解决你的问题,请参考以下文章

在 UILabel 和 UIButton 之间拆分单元格的全部内容

子类化 UIButton 标题字体大小意外更改

我在 MonoTouch 中将 UIButton 子类化,但底层 ObjC 类仍然是 UIButton

使用 NSLayoutConstraint 实例化和设置 UIButton 子类的大小

子类化 UIButton 来做一个圆,最后是一个矩形

子类化 UIButton 并覆盖触摸事件 - 不起作用