Label under image in UIButton

后端 未结 30 1974
佛祖请我去吃肉
佛祖请我去吃肉 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:35

    @Tiago I change your answer like this. Its works fine with all sizes

    func alignImageAndTitleVertically(padding: CGFloat = 5.0) {
            self.sizeToFit()
            let imageSize = self.imageView!.frame.size
            let titleSize = self.titleLabel!.frame.size
            let totalHeight = imageSize.height + titleSize.height + padding
    
            self.imageEdgeInsets = UIEdgeInsets(
                top: -(totalHeight - imageSize.height),
                left: 0,
                bottom: 0,
                right: -titleSize.width
            )
    
            self.titleEdgeInsets = UIEdgeInsets(
                top: 0,
                left: 0,
                bottom: -(totalHeight - titleSize.height),
                right: titleSize.height
            )
        }
    
    0 讨论(0)
  • 2020-11-29 16:36

    This is a simple centered title button implemented in Swift by overriding titleRect(forContentRect:) and imageRect(forContentRect:). It also implements intrinsicContentSize for use with AutoLayout.

    import UIKit
    
    class CenteredButton: UIButton
    {
        override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
            let rect = super.titleRect(forContentRect: contentRect)
    
            return CGRect(x: 0, y: contentRect.height - rect.height + 5,
                width: contentRect.width, height: rect.height)
        }
    
        override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
            let rect = super.imageRect(forContentRect: contentRect)
            let titleRect = self.titleRect(forContentRect: contentRect)
    
            return CGRect(x: contentRect.width/2.0 - rect.width/2.0,
                y: (contentRect.height - titleRect.height)/2.0 - rect.height/2.0,
                width: rect.width, height: rect.height)
        }
    
        override var intrinsicContentSize: CGSize {
            let size = super.intrinsicContentSize
    
            if let image = imageView?.image {
                var labelHeight: CGFloat = 0.0
    
                if let size = titleLabel?.sizeThatFits(CGSize(width: self.contentRect(forBounds: self.bounds).width, height: CGFloat.greatestFiniteMagnitude)) {
                    labelHeight = size.height
                }
    
                return CGSize(width: size.width, height: image.size.height + labelHeight + 5)
            }
    
            return size
        }
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            centerTitleLabel()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            centerTitleLabel()
        }
    
        private func centerTitleLabel() {
            self.titleLabel?.textAlignment = .center
        }
    }
    
    0 讨论(0)
  • 2020-11-29 16:38

    On iOS 11/ Swift 4 none of the above answers really worked for me. I found some examples and put my spin on it:

    extension UIButton {
    
        func centerImageAndButton(_ gap: CGFloat, imageOnTop: Bool) {
    
          guard let imageView = self.currentImage,
          let titleLabel = self.titleLabel?.text else { return }
    
          let sign: CGFloat = imageOnTop ? 1 : -1
          self.titleEdgeInsets = UIEdgeInsetsMake((imageView.size.height + gap) * sign, -imageView.size.width, 0, 0);
    
          let titleSize = titleLabel.size(withAttributes:[NSAttributedStringKey.font: self.titleLabel!.font!])
          self.imageEdgeInsets = UIEdgeInsetsMake(-(titleSize.height + gap) * sign, 0, 0, -titleSize.width)
        }
    }
    

    Hope this helps someone.

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

    That's definitely is an overkill for this question, however... In one of my projects I first had to implement a button with icon aligned leftmost. Then we got another button with title under image. I searched for an existing solution but with no luck So, here goes the alignable button:

    @IBDesignable
    class AlignableButton: UIButton {
    
    override class var requiresConstraintBasedLayout: Bool {
        return true
    }
    
    @objc enum IconAlignment: Int {
        case top, left, right, bottom
    }
    
    // MARK: - Designables
    @IBInspectable var iconAlignmentValue: Int {
        set {
            iconAlignment = IconAlignment(rawValue: newValue) ?? .left
        }
        get {
            return iconAlignment.rawValue
        }
    }
    
    var iconAlignment: IconAlignment = .left
    
    @IBInspectable var titleAlignmentValue: Int {
        set {
            titleAlignment = NSTextAlignment(rawValue: newValue) ?? .left
        }
        get {
            return titleAlignment.rawValue
        }
    }
    
    var titleAlignment: NSTextAlignment = .left
    
    // MARK: - Corner Radius
    @IBInspectable
    var cornerRadius: CGFloat {
        get {
            return layer.cornerRadius
        }
        set {
            layer.masksToBounds = (newValue != 0)
            layer.cornerRadius = newValue
        }
    }
    
    // MARK: - Content size
    override var intrinsicContentSize: CGSize {
        
        switch iconAlignment {
        case .top, .bottom:
            return verticalAlignedIntrinsicContentSize
        
        default:
            return super.intrinsicContentSize
        }
    }
    
    private var verticalAlignedIntrinsicContentSize: CGSize {
        
        if let imageSize = imageView?.intrinsicContentSize,
            let labelSize = titleLabel?.intrinsicContentSize {
            
            let width = max(imageSize.width, labelSize.width) + contentEdgeInsets.left + contentEdgeInsets.right
            let height = imageSize.height + labelSize.height + contentEdgeInsets.top + contentEdgeInsets.bottom
            
            return CGSize(
                width: ceil(width),
                height: ceil(height)
            )
        }
        
        return super.intrinsicContentSize
    }
    
    // MARK: - Image Rect
    override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
        
        switch iconAlignment {
        case .top:
            return topAlignedImageRect(forContentRect: contentRect)
        case .bottom:
            return bottomAlignedImageRect(forContentRect: contentRect)
        case .left:
            return leftAlignedImageRect(forContentRect: contentRect)
        case .right:
            return rightAlignedImageRect(forContentRect: contentRect)
        }
    }
    
    func topAlignedImageRect(forContentRect contentRect: CGRect) -> CGRect {
        let rect = super.imageRect(forContentRect: contentRect)
        
        let x = (contentRect.width - rect.width) / 2.0 + contentRect.minX
        let y = contentRect.minY
        let w = rect.width
        let h = rect.height
        
        return CGRect(
            x: x,
            y: y,
            width: w,
            height: h
        ).inset(by: imageEdgeInsets)
    }
    
    func bottomAlignedImageRect(forContentRect contentRect: CGRect) -> CGRect {
        let rect = super.imageRect(forContentRect: contentRect)
        
        let x = (contentRect.width - rect.width) / 2.0 + contentRect.minX
        let y = contentRect.height - rect.height + contentRect.minY
        let w = rect.width
        let h = rect.height
        
        return CGRect(
            x: x,
            y: y,
            width: w,
            height: h
        ).inset(by: imageEdgeInsets)
    }
    
    func leftAlignedImageRect(forContentRect contentRect: CGRect) -> CGRect {
        let rect = super.imageRect(forContentRect: contentRect)
        
        let x = contentRect.minX
        let y = (contentRect.height - rect.height) / 2 + contentRect.minY
        let w = rect.width
        let h = rect.height
        
        return CGRect(
            x: x,
            y: y,
            width: w,
            height: h
        ).inset(by: imageEdgeInsets)
    }
    
    func rightAlignedImageRect(forContentRect contentRect: CGRect) -> CGRect {
        let rect = super.imageRect(forContentRect: contentRect)
        
        let x = (contentRect.width - rect.width) + contentRect.minX
        let y = (contentRect.height - rect.height) / 2 + contentRect.minY
        let w = rect.width
        let h = rect.height
        
        return CGRect(
            x: x,
            y: y,
            width: w,
            height: h
        ).inset(by: imageEdgeInsets)
    }
    
    // MARK: - Title Rect
    override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
        
        switch iconAlignment {
        case .top:
            return topAlignedTitleRect(forContentRect: contentRect)
        case .bottom:
            return bottomAlignedTitleRect(forContentRect: contentRect)
        case .left:
            return leftAlignedTitleRect(forContentRect: contentRect)
        case .right:
            return rightAlignedTitleRect(forContentRect: contentRect)
        }
    }
    
    func topAlignedTitleRect(forContentRect contentRect: CGRect) -> CGRect {
        
        let rect = super.titleRect(forContentRect: contentRect)
    
        let x = contentRect.minX
        let y = contentRect.height - rect.height + contentRect.minY
        let w = contentRect.width
        let h = rect.height
        
        return CGRect(
            x: x,
            y: y,
            width: w,
            height: h
        ).inset(by: titleEdgeInsets)
    }
    
    func bottomAlignedTitleRect(forContentRect contentRect: CGRect) -> CGRect {
        
        let rect = super.titleRect(forContentRect: contentRect)
        
        let x = contentRect.minX
        let y = contentRect.minY
        let w = contentRect.width
        let h = rect.height
        
        return CGRect(
            x: x,
            y: y,
            width: w,
            height: h
        ).inset(by: titleEdgeInsets)
    }
    
    func leftAlignedTitleRect(forContentRect contentRect: CGRect) -> CGRect {
        
        let titleRect = super.titleRect(forContentRect: contentRect)
        let imageRect = self.imageRect(forContentRect: contentRect)
        
        let x = imageRect.width + imageRect.minX
        let y = (contentRect.height - titleRect.height) / 2.0 + contentRect.minY
        let w = contentRect.width - imageRect.width * 2.0
        let h = titleRect.height
        
        return CGRect(
            x: x,
            y: y,
            width: w,
            height: h
        ).inset(by: titleEdgeInsets)
    }
    
    func rightAlignedTitleRect(forContentRect contentRect: CGRect) -> CGRect {
        
        let titleRect = super.titleRect(forContentRect: contentRect)
        let imageRect = self.imageRect(forContentRect: contentRect)
    
        let x = contentRect.minX + imageRect.width
        let y = (contentRect.height - titleRect.height) / 2.0 + contentRect.minY
        let w = contentRect.width - imageRect.width * 2.0
        let h = titleRect.height
        
        return CGRect(
            x: x,
            y: y,
            width: w,
            height: h
        ).inset(by: titleEdgeInsets)
    }
    
    // MARK: - Lifecycle
    override func awakeFromNib() {
        super.awakeFromNib()
        
        titleLabel?.textAlignment = titleAlignment
    }
    
    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        
        titleLabel?.textAlignment = titleAlignment
    }
    }
    

    Hope you find it useful.

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

    Look at this great answer in Swift.

    extension UIButton {
    
        func alignImageAndTitleVertically(padding: CGFloat = 6.0) {
            let imageSize = self.imageView!.frame.size
            let titleSize = self.titleLabel!.frame.size
            let totalHeight = imageSize.height + titleSize.height + padding
    
            self.imageEdgeInsets = UIEdgeInsets(
                top: -(totalHeight - imageSize.height),
                left: 0,
                bottom: 0,
                right: -titleSize.width
            )
    
            self.titleEdgeInsets = UIEdgeInsets(
                top: 0,
                left: -imageSize.width,
                bottom: -(totalHeight - titleSize.height),
                right: 0
            )
        }
    
    }
    
    0 讨论(0)
  • 2020-11-29 16:39

    Subclass UIButton. Override -layoutSubviews to move the built-in subviews into new positions:

    - (void)layoutSubviews
    {
        [super layoutSubviews];
    
        CGRect frame = self.imageView.frame;
        frame = CGRectMake(truncf((self.bounds.size.width - frame.size.width) / 2), 0.0f, frame.size.width, frame.size.height);
        self.imageView.frame = frame;
    
        frame = self.titleLabel.frame;
        frame = CGRectMake(truncf((self.bounds.size.width - frame.size.width) / 2), self.bounds.size.height - frame.size.height, frame.size.width, frame.size.height);
        self.titleLabel.frame = frame;
    }
    
    0 讨论(0)
提交回复
热议问题