Label under image in UIButton

后端 未结 30 1977
佛祖请我去吃肉
佛祖请我去吃肉 2020-11-29 15:53

I\'m trying to create a button which has some text beneath the icon (sorta like the app buttons) however it seems to be quite difficult to achieve. Any ideas how can I go ab

相关标签:
30条回答
  • 2020-11-29 16:26

    Swift 5 - below method works for me

    func centerVerticallyWithPadding(padding : CGFloat) {
            guard
                let imageViewSize = self.imageView?.frame.size,
                let titleLabelSize = self.titleLabel?.frame.size else {
                return
            }
    
            let totalHeight = imageViewSize.height + titleLabelSize.height + padding
    
            self.imageEdgeInsets = UIEdgeInsets(
                top: max(0, -(totalHeight - imageViewSize.height)),
                left: 0.0,
                bottom: 0.0,
                right: -titleLabelSize.width
            )
    
            self.titleEdgeInsets = UIEdgeInsets(
                top: (totalHeight - imageViewSize.height),
                left: -imageViewSize.width,
                bottom: -(totalHeight - titleLabelSize.height),
                right: 0.0
            )
    
            self.contentEdgeInsets = UIEdgeInsets(
                top: 0.0,
                left: 0.0,
                bottom: titleLabelSize.height,
                right: 0.0
            )
        }
    

    Make sure your button title is not truncate in storyboard/xib else go for
    Solution 2

    class SVVerticalButton: UIButton {
    
        override func layoutSubviews() {
            super.layoutSubviews()
            let padding : CGFloat = 2.0
            if let imageView = self.imageView {
                imageView.frame.origin.x = (self.bounds.size.width - imageView.frame.size.width) / 2.0
                imageView.frame.origin.y = max(0,(self.bounds.size.height - (imageView.frame.size.height + (titleLabel?.frame.size.height ?? 0.0) + padding)) / 2.0)
            }
            if let titleLabel = self.titleLabel {
                titleLabel.frame.origin.x = 0
                titleLabel.frame.origin.y = self.bounds.size.height - titleLabel.frame.size.height
                titleLabel.frame.size.width = self.bounds.size.width
                titleLabel.textAlignment = .center
            }
        }
    
    }
    
    0 讨论(0)
  • 2020-11-29 16:27

    Use this two methods:

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

    Example:

    class VerticalButton: UIButton {
    
      override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
        let titleRect = super.titleRect(forContentRect: contentRect)
        let imageRect = super.imageRect(forContentRect: contentRect)
    
        return CGRect(x: 0,
                      y: contentRect.height - (contentRect.height - padding - imageRect.size.height - titleRect.size.height) / 2 - titleRect.size.height,
                      width: contentRect.width,
                      height: titleRect.height)
      }
    
      override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
        let imageRect = super.imageRect(forContentRect: contentRect)
        let titleRect = self.titleRect(forContentRect: contentRect)
    
        return CGRect(x: contentRect.width/2.0 - imageRect.width/2.0,
                      y: (contentRect.height - padding - imageRect.size.height - titleRect.size.height) / 2,
                      width: imageRect.width,
                      height: imageRect.height)
      }
    
      private let padding: CGFloat
      init(padding: CGFloat) {
        self.padding = padding
    
        super.init(frame: .zero)
        self.titleLabel?.textAlignment = .center
      }
    
      required init?(coder aDecoder: NSCoder) { fatalError() }
    }
    
    extension UIButton {
    
      static func vertical(padding: CGFloat) -> UIButton {
        return VerticalButton(padding: padding)
      }
    }
    

    And you can use:

    let myButton = UIButton.vertical(padding: 6)
    
    0 讨论(0)
  • 2020-11-29 16:28

    I found that Simeon's answer was probably the best but it was giving me strange results on some buttons and I just couldn't work out why. So using his answer as a base I implemented my buttons as per below:

    #define PADDING 2.0f
    
    @implementation OOButtonVerticalImageText
    
    -(CGSize) intrinsicContentSize {
      CGSize size = [super intrinsicContentSize];
      CGFloat labelHeight = 0.0f;
      CGSize titleSize = [self.titleLabel sizeThatFits:CGSizeMake([self contentRectForBounds:self.bounds].size.width, CGFLOAT_MAX)];
      labelHeight = titleSize.height;
      return CGSizeMake(MAX(titleSize.width, self.imageView.image.size.width), self.imageView.image.size.height + labelHeight + PADDING);
    }
    
    -(void) layoutSubviews {
      [super layoutSubviews];
    
      CGSize titleSize = [self.titleLabel sizeThatFits:CGSizeMake([self contentRectForBounds:self.bounds].size.width, CGFLOAT_MAX)];
      self.titleLabel.frame = CGRectMake((self.bounds.size.width - titleSize.width)/2.0f,
                                         self.bounds.size.height - titleSize.height - PADDING,
                                         titleSize.width,
                                         titleSize.height);
    
      CGSize ivSize = self.imageView.frame.size;
      self.imageView.frame = CGRectMake((self.bounds.size.width - ivSize.width)/2.0f,
                                        self.titleLabel.frame.origin.y - ivSize.height - PADDING,
                                        ivSize.width,
                                        ivSize.height);
    }
    
    @end
    
    0 讨论(0)
  • 2020-11-29 16:30

    This is a modified version of Erik W's excellent answer. But instead of placing the image centered at the TOP of the view, it places the image and the label centred in the view as a group.

    The difference is:

    +-----------+
    |    ( )    |
    |   Hello   |     // Erik W's code
    |           |
    |           |
    +-----------+
    

    vs

    +-----------+
    |           |
    |    ( )    |     // My modified version
    |   Hello   |
    |           |
    +-----------+
    

    Source below:

    -(void)layoutSubviews {
        [super layoutSubviews];
    
        CGRect titleLabelFrame = self.titleLabel.frame;
        CGSize labelSize = [self.titleLabel.text sizeWithFont:self.titleLabel.font constrainedToSize:CGSizeMake(self.frame.size.width, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
    
        CGRect imageFrame = self.imageView.frame;
    
        CGSize fitBoxSize = (CGSize){.height = labelSize.height + kTextTopPadding +  imageFrame.size.height, .width = MAX(imageFrame.size.width, labelSize.width)};
    
        CGRect fitBoxRect = CGRectInset(self.bounds, (self.bounds.size.width - fitBoxSize.width)/2, (self.bounds.size.height - fitBoxSize.height)/2);
    
        imageFrame.origin.y = fitBoxRect.origin.y;
        imageFrame.origin.x = CGRectGetMidX(fitBoxRect) - (imageFrame.size.width/2);
        self.imageView.frame = imageFrame;
    
        // Adjust the label size to fit the text, and move it below the image
    
        titleLabelFrame.size.width = labelSize.width;
        titleLabelFrame.size.height = labelSize.height;
        titleLabelFrame.origin.x = (self.frame.size.width / 2) - (labelSize.width / 2);
        titleLabelFrame.origin.y = fitBoxRect.origin.y + imageFrame.size.height + kTextTopPadding;
        self.titleLabel.frame = titleLabelFrame;
    }
    

    FYI: This can break when combined with UIView animations, as layoutSubviews is called during them.

    0 讨论(0)
  • 2020-11-29 16:30

    Here is "Bear With Me"s answer as a subclass in Swift 2.0. To use it just change your button class in Interface Builder to VerticalButton and it will magically update the preview.

    I also updated it to calculate the correct intrinsic content size for autolayout.

    import UIKit
    
    @IBDesignable
    
    class VerticalButton: UIButton {
        @IBInspectable var padding: CGFloat = 8
    
        override func prepareForInterfaceBuilder() {
            super.prepareForInterfaceBuilder()
    
            update()
        }
    
        override func layoutSubviews() {
            super.layoutSubviews()
    
            update()
        }
    
        func update() {
            let imageBounds = self.imageView!.bounds
            let titleBounds = self.titleLabel!.bounds
            let totalHeight = CGRectGetHeight(imageBounds) + padding + CGRectGetHeight(titleBounds)
    
            self.imageEdgeInsets = UIEdgeInsets(
                top: -(totalHeight - CGRectGetHeight(imageBounds)),
                left: 0,
                bottom: 0,
                right: -CGRectGetWidth(titleBounds)
            )
    
            self.titleEdgeInsets = UIEdgeInsets(
                top: 0,
                left: -CGRectGetWidth(imageBounds),
                bottom: -(totalHeight - CGRectGetHeight(titleBounds)),
                right: 0
            )
        }
    
        override func intrinsicContentSize() -> CGSize {
            let imageBounds = self.imageView!.bounds
            let titleBounds = self.titleLabel!.bounds
    
            let width = CGRectGetWidth(imageBounds) > CGRectGetWidth(titleBounds) ? CGRectGetWidth(imageBounds) : CGRectGetWidth(titleBounds)
            let height = CGRectGetHeight(imageBounds) + padding + CGRectGetHeight(titleBounds)
    
            return CGSizeMake(width, height)
        }
    }
    
    0 讨论(0)
  • 2020-11-29 16:31

    @simeon's solution in Objective-C

    #import "CenteredButton.h"
    
    @implementation CenteredButton
    
    - (CGRect)titleRectForContentRect:(CGRect)contentRect
    {
        CGRect rect = [super titleRectForContentRect: contentRect];
        return CGRectMake(0,
                          contentRect.size.height - rect.size.height - 5,
                          contentRect.size.width,
                          rect.size.height);
    }
    
    - (CGRect)imageRectForContentRect:(CGRect)contentRect
    {
        CGRect rect = [super imageRectForContentRect: contentRect];
        CGRect titleRect = [self titleRectForContentRect: contentRect];
    
        return CGRectMake(contentRect.size.width / 2.0 - rect.size.width / 2.0,
                          (contentRect.size.height - titleRect.size.height)/2.0 - rect.size.height/2.0,
                          rect.size.width,
                          rect.size.height);
    }
    
    - (CGSize)intrinsicContentSize {
        CGSize imageSize = [super intrinsicContentSize];
    
        if (self.imageView.image) {
            UIImage* image = self.imageView.image;
            CGFloat labelHeight = 0.0;
    
            CGSize labelSize = [self.titleLabel sizeThatFits: CGSizeMake([self contentRectForBounds: self.bounds].size.width, CGFLOAT_MAX)];
            if (CGSizeEqualToSize(imageSize, labelSize)) {
                labelHeight = imageSize.height;
            }
    
            return CGSizeMake(MAX(labelSize.width, imageSize.width), image.size.height + labelHeight + 5);
        }
    
        return imageSize;
    }
    
    - (id) initWithFrame:(CGRect)frame {
        self = [super initWithFrame:frame];
         if (self) {
             [self centerTitleLabel];
         }
        return self;
    
    }
    
    - (id)initWithCoder:(NSCoder *)aDecoder {
        self = [super initWithCoder:aDecoder];
        if (self) {
            [self centerTitleLabel];
        }
        return self;
    }
    
    - (void)centerTitleLabel {
        self.titleLabel.textAlignment = NSTextAlignmentCenter;
    }
    
    @end
    
    0 讨论(0)
提交回复
热议问题