UIButton에서 이미지 아래 레이블


158

아이콘 아래에 텍스트가있는 버튼을 만들려고하는데 (앱 버튼과 같은 분류) 달성하기가 매우 어려워 보입니다. 어떤 아이디어를 얻으려면 어떻게 텍스트를 이미지 아래UIButton?


UIImage 및 UILabel을 포함하는 UIbutton의 사용자 정의 서브 클래스를 만드는 것이 매우 쉽고 가능합니다.
NP Compete

7
또는 UIButton과 UILabel을 사용하십시오.
raidfive

정확한 크기와 자동 레이아웃을 제어하려면, 당신이 시도 할 수 있습니다 : https://github.com/albert-zhang/AZCenterLabelButton( 링크 )
알버트 장

이 솔루션과 함께 작동합니다 stackoverflow.com/a/59666154/1576134
Shreyank

답변:


111

또는이 카테고리를 사용할 수 있습니다 :

ObjC

@interface UIButton (VerticalLayout)

- (void)centerVerticallyWithPadding:(float)padding;
- (void)centerVertically;

@end

@implementation UIButton (VerticalLayout)

- (void)centerVerticallyWithPadding:(float)padding {
    CGSize imageSize = self.imageView.frame.size;
    CGSize titleSize = self.titleLabel.frame.size;
    CGFloat totalHeight = (imageSize.height + titleSize.height + padding);
    
    self.imageEdgeInsets = UIEdgeInsetsMake(- (totalHeight - imageSize.height),
                                            0.0f,
                                            0.0f,
                                            - titleSize.width);
    
    self.titleEdgeInsets = UIEdgeInsetsMake(0.0f,
                                            - imageSize.width,
                                            - (totalHeight - titleSize.height),
                                            0.0f);
    
    self.contentEdgeInsets = UIEdgeInsetsMake(0.0f,
                                              0.0f,
                                              titleSize.height,
                                              0.0f);
}

- (void)centerVertically {
    const CGFloat kDefaultPadding = 6.0f;
    [self centerVerticallyWithPadding:kDefaultPadding];
}

@end

스위프트 확장

extension UIButton {
    
    func centerVertically(padding: CGFloat = 6.0) {
        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: -(totalHeight - imageViewSize.height),
            left: 0.0,
            bottom: 0.0,
            right: -titleLabelSize.width
        )
        
        self.titleEdgeInsets = UIEdgeInsets(
            top: 0.0,
            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
        )
    }
    
}

제안 : 버튼 높이가보다 작 으면 totalHeight이미지가 외부 경계를 그립니다.

imageEdgeInset.top 해야한다:

max(0, -(totalHeight - imageViewSize.height))

5
프레임을 수동으로 조정하는 대신 edgeInsets를 사용하기 때문에 이것이 가장 좋은 대답이라고 생각합니다. 버튼의 superview에있는 layoutSubviews에서 호출 될 때 자동 레이아웃에도 효과적입니다. imageView 및 titleLabel 높이와 너비를 얻을 때 CGRectGetHeight()와 제안하는 것이 CGRectGetWidth()좋습니다.
Jesse

1
이 기능을 사용하면 이미지가 버튼보기 위로 튀어 오도록 중앙에 표시됩니다. CGFloat inset = (self.frame.size.height - totalHeight)/2; self.contentEdgeInsets = UIEdgeInsetsMake(inset, 0.0f, inset, 0.0f);
Alex Hedley

12
스위프트 확장 프로그램이 나를 올바르게 배치하지 못했습니다.
패트릭

Image가 setBackgroundImage가 아닌 setImage로 설정된 경우 작동합니다.
Argus

1
이 솔루션과 함께 작동합니다 stackoverflow.com/a/59666154/1576134
Shreyank

87

Xcode에서는 Edge Title Left Inset을 이미지 너비의 음수로 간단히 설정할 수 있습니다. 이미지 중앙에 레이블이 표시됩니다.

이미지 아래에 레이블을 표시하려면 (앱 버튼과 같은 분류) 가장자리 제목 상단 삽입을 양수로 설정해야 할 수 있습니다.


1
여러 가지 크기의 버튼으로이 작업을 반복적으로 수행하지 않는 한 ... Erik W 솔루션의 조정 된 버전으로 좋은 결과를 얻었습니다
Kenny Winker

5
사람들이 이것을 깨닫게하기 위해. 버튼이 이미지 너비보다 넓더라도 값은 이미지의 음의 너비 여야합니다.
Liron

1
이것은 나를 위해 작동하지 않았습니다. 내 텍스트는 여전히 이미지 오른쪽에 나타납니다. 즉, 그 아래에 줄 바꿈이 없습니다.
Cindeselia

1
@Cindeselia 놀랍습니다. Top Inset에 얼마나 큰 가치를 사용 했습니까? 어쩌면 더 큰 값으로 늘리려 고합니까?
Chris

3
iOS7에서는 작동하지 않는 것 같습니다. 레이블은 이미지의 맨 아래로 이동하고 숨겨져 더 이상 표시되지 않습니다.
용감한

50

이는 재정 의하여 스위프트 구현하는 간단한을 중심으로 제목 버튼입니다 titleRect(forContentRect:)imageRect(forContentRect:). 또한 intrinsicContentSizeAutoLayout과 함께 사용하도록 구현 합니다.

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
    }
}

5
이것이 가장 올바른 해결책입니다. 그러나 고유 컨텐츠 크기에 대한 일부 수정이 필요했습니다. 이미지와 레이블 사이에 최대 너비를 반환해야합니다. return CGSizeMake (MAX (labelSize.width, self.imageView.image.size.width), self.imageView.image.size.height + labelHeight)
kirander

39

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
        )
    }

}

4
당신은 또한 이미지가 수직 중심 원하는 경우, 교체 leftimageEdgeInsets(self.frame.size.width - imageSize.width) / 2
elsurudo

자동 레이아웃을 사용하는 경우 layoutSubviews()슈퍼 뷰 에서이 메소드를 호출 하십시오.
AlexVogel

33

서브 클래스 UIButton. 무시- layoutSubviews내장 subviews을 새로운 위치로 옮기기 :

- (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;
}

개인적으로 titleLabel y 값을 0으로 설정하고 높이를 프레임 높이로 설정하여 이미지와 함께 텍스트를 표시해야했습니다. 이해가되지 않지만 작동합니다 ...하지만 여전히 컨트롤을 설정하는 'Apple'방법을 배우고 있습니다.
Russ

6
실제로, 더 좋은 방법은 재정의 titleRectForContentRect하고imageRectForContentRect
Mazyod

28

icecrystal23의 답변을 리팩토링했습니다.

자동 레이아웃, xib, 스토리 보드와 함께 작동하는 Swift 3는 애니메이션을 적용 할 수 있습니다.

원래 icecrystal23의 대답에있는 버튼에는 잘못 계산 된 프레임이 있습니다. 나는 그것을 고쳤다 고 생각한다.

편집 : Swift 5로 업데이트되어 Interface Builder / Storyboards에서 작업했습니다.

import UIKit

@IBDesignable
class VerticalButton: UIButton {

    @IBInspectable public var padding: CGFloat = 20.0 {
        didSet {
            setNeedsLayout()
        }
    }

    override var intrinsicContentSize: CGSize {
        let maxSize = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)

        if let titleSize = titleLabel?.sizeThatFits(maxSize), let imageSize = imageView?.sizeThatFits(maxSize) {
            let width = ceil(max(imageSize.width, titleSize.width))
            let height = ceil(imageSize.height + titleSize.height + padding)

            return CGSize(width: width, height: height)
        }

        return super.intrinsicContentSize
    }

    override func layoutSubviews() {
        if let image = imageView?.image, let title = titleLabel?.attributedText {
            let imageSize = image.size
            let titleSize = title.size()

            titleEdgeInsets = UIEdgeInsets(top: 0.0, left: -imageSize.width, bottom: -(imageSize.height + padding), right: 0.0)
            imageEdgeInsets = UIEdgeInsets(top: -(titleSize.height + padding), left: 0.0, bottom: 0.0, right: -titleSize.width)
        }

        super.layoutSubviews()
    }

}

2
이미지가 제거 될 때이 문제가 발생합니다. 선택한 상태의 이미지를 사용하고 기본 상태의 이미지는 사용하지 않습니다. 상태가 선택된 상태에서 기본값으로 변경되면 레이블이 엉망입니다. 따라서 몇 가지 수정이 필요합니다. 이미지보기를 확인하지 말고 'image (for : state)'를 사용하십시오. layoutSubviews의 else 문에 이미지가없는 경우 제로 에지 삽입을 설정하십시오.
Matic Oblak

여기서 만 해결책이 작동합니다. 다른 답변은 효과가있는 것처럼 보이지만 실제로 버튼 경계는 레이블 및 이미지 크기에 따라 크기가 조정되지 않습니다. 이것을 보려면 배경색을 설정하십시오.
마누엘

이 솔루션이 작동하지 않아 일종의 무한 루프가 발생하여 Xcode 충돌이 발생했다고 생각합니다. intrinsicContentSize 부분을 제거하고 제대로 작동했습니다 (Xcode 11.5)
mojuba

23

여기서 답변 중 하나를 수정했습니다.

스위프트 3 :

class CenteredButton: UIButton
{
    override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
        let rect = super.titleRect(forContentRect: contentRect)
        let imageRect = super.imageRect(forContentRect: contentRect)

        return CGRect(x: 0, y: imageRect.maxY + 10,
                      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 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
    }
}

16

이것은 Erik W의 훌륭한 답변의 수정 된 버전입니다. 그러나 이미지를 뷰의 상단 중앙에 배치하는 대신 이미지와 레이블을 뷰 중앙에 그룹으로 배치합니다.

차이점은 다음과 같습니다.

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

vs

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

아래 출처 :

-(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;
}

참고 : UIView 애니메이션과 결합하면 layoutSubviews가 호출되기 때문에이 오류가 발생할 수 있습니다.


labelSize를 계산하는 행이 self.frame.size.width 대신 self.bounds.size.width를 사용해서는 안됩니까?
Jeremy Wiebe

16

Swift의 Dave 솔루션 :

override func layoutSubviews() {
    super.layoutSubviews()
    if let imageView = self.imageView {
        imageView.frame.origin.x = (self.bounds.size.width - imageView.frame.size.width) / 2.0
        imageView.frame.origin.y = 0.0
    }
    if let titleLabel = self.titleLabel {
        titleLabel.frame.origin.x = (self.bounds.size.width - titleLabel.frame.size.width) / 2.0
        titleLabel.frame.origin.y = self.bounds.size.height - titleLabel.frame.size.height
    }
}

좋은 대답입니다. 서브 클래스에 @IBDesignable을 추가하고 스토리 보드에서보십시오.
Joel Teply 2016 년

16

당신이 서브 클래스 경우 UIButtonoverride layoutSubviews, 당신은 이미지 중심과 제목이 그 아래 중앙에 배치 아래를 사용할 수 있습니다 :

kTextTopPadding 이미지와 그 아래 텍스트 사이의 간격을 결정하는 소개해야 할 상수입니다.

-(void)layoutSubviews {
    [super layoutSubviews];

    // Move the image to the top and center it horizontally
    CGRect imageFrame = self.imageView.frame;
    imageFrame.origin.y = 0;
    imageFrame.origin.x = (self.frame.size.width / 2) - (imageFrame.size.width / 2);
    self.imageView.frame = imageFrame;

    // Adjust the label size to fit the text, and move it below the image
    CGRect titleLabelFrame = self.titleLabel.frame;
    CGSize labelSize = [self.titleLabel.text sizeWithFont:self.titleLabel.font
                                        constrainedToSize:CGSizeMake(self.frame.size.width, CGFLOAT_MAX)
                                        lineBreakMode:NSLineBreakByWordWrapping];
    titleLabelFrame.size.width = labelSize.width;
    titleLabelFrame.size.height = labelSize.height;
    titleLabelFrame.origin.x = (self.frame.size.width / 2) - (labelSize.width / 2);
    titleLabelFrame.origin.y = self.imageView.frame.origin.y + self.imageView.frame.size.height + kTextTopPadding;
    self.titleLabel.frame = titleLabelFrame;

}

8

sizeWithFont가 iOS 7에서 더 이상 사용되지 않으므로 Kenny Winker의 답변이 업데이트되었습니다.

-(void)layoutSubviews {
[super layoutSubviews];

int kTextTopPadding = 3;

CGRect titleLabelFrame = self.titleLabel.frame;

CGRect labelSize = [self.titleLabel.text boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGRectGetHeight(self.bounds)) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:self.titleLabel.font} context:nil];

CGRect imageFrame = self.imageView.frame;

CGSize fitBoxSize = (CGSize){.height = labelSize.size.height + kTextTopPadding +  imageFrame.size.height, .width = MAX(imageFrame.size.width, labelSize.size.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.size.width;
titleLabelFrame.size.height = labelSize.size.height;
titleLabelFrame.origin.x = (self.frame.size.width / 2) - (labelSize.size.width / 2);
titleLabelFrame.origin.y = fitBoxRect.origin.y + imageFrame.size.height + kTextTopPadding;
self.titleLabel.frame = titleLabelFrame;
}

iOS 7 이전의 버전이 점점 구식이되어 가고 있기 때문에 새로운 답변이 될 것입니다.
Mehlyfication

7

iOS 11 / Swift 4에서는 위의 답변 중 어느 것도 실제로 효과가 없었습니다. 나는 몇 가지 예를 찾아 내 스핀을 넣었다.

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)
    }
}

이것이 누군가를 돕기를 바랍니다.


contentEdgeInsets에 제목과 이미지가 완전히 포함되지 않는 문제가 있지만 Roman을 제안 해 주셔서 감사합니다.
Patrick

6

Kenny Winker와 simeon의 코드를 사용 하여이 빠른 코드를 만들어 나에게 효과적입니다.

import UIKit

@IBDesignable
class TopIconButton: UIButton {
    override func layoutSubviews() {
        super.layoutSubviews()

        let kTextTopPadding:CGFloat = 3.0;

        var titleLabelFrame = self.titleLabel!.frame;


        let labelSize = titleLabel!.sizeThatFits(CGSizeMake(CGRectGetWidth(self.contentRectForBounds(self.bounds)), CGFloat.max))

        var imageFrame = self.imageView!.frame;

        let fitBoxSize = CGSizeMake(max(imageFrame.size.width, labelSize.width), labelSize.height + kTextTopPadding + imageFrame.size.    height)

        let 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;
        self.titleLabel!.textAlignment = .Center
    }

}

3

이미지의 크기와 제목 레이블에 따라 세 개의 모서리 삽입을 모두 조정하면됩니다.

button.contentEdgeInsets = UIEdgeInsetsMake(0, 0, titleLabelBounds.height + 4, 0)
button.titleEdgeInsets = UIEdgeInsetsMake(image.size.height + 8, -image.size.width, 0, 0)
button.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, -titleLabelBounds.width)

텍스트를 설정 한 후 sizeToFit를 호출하여 제목 레이블 범위를 얻을 수 있습니다. 가로 간격은 텍스트, 글꼴 및 이미지의 크기에 관계없이 작동해야하지만 세로 간격과 아래쪽 내용 가장자리가 일관되게하는 단일 솔루션을 모릅니다.


3

다음은 "Bear With Me"의 하위 클래스 답변입니다 Swift 2.0. 그것을 사용하려면 버튼 클래스를 Interface Builder로 변경 VerticalButton하면 미리보기가 마술처럼 업데이트됩니다.

또한 자동 레이아웃에 대한 고유 한 고유 컨텐츠 크기를 계산하도록 업데이트했습니다.

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)
    }
}

2
무한 루프로 엔드까지 layoutSubviews()내 경우에는 반복적으로 호출됩니다 intrinsicContentSize액세스 imageView하게 layoutSubviews호출되고있는 액세스 imageView
ctietze

3

@Tiago 나는 당신의 대답을 이렇게 바 꾸었습니다. 모든 크기에서 잘 작동합니다

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
        )
    }

3

나는 여기에 답변을 조합하여 Swift에서 나를 위해 일하는 것처럼 보였습니다. 나는 삽입물을 어떻게 무시 하는지를 좋아하지 않지만 작동합니다. 의견 개선을 제안하기 위해 열려 있습니다. sizeToFit()자동 레이아웃과 함께 올바르게 작동하는 것 같습니다 .

import UIKit

/// A button that displays an image centered above the title.  This implementation 
/// only works when both an image and title are set, and ignores
/// any changes you make to edge insets.
class CenteredButton: UIButton
{
    let padding: CGFloat = 0.0

    override func layoutSubviews() {
        if imageView?.image != nil && titleLabel?.text != nil {
            let imageSize: CGSize = imageView!.image!.size
            titleEdgeInsets = UIEdgeInsetsMake(0.0, -imageSize.width, -(imageSize.height + padding), 0.0)
            let labelString = NSString(string: titleLabel!.text!)
            let titleSize = labelString.sizeWithAttributes([NSFontAttributeName: titleLabel!.font])
            imageEdgeInsets = UIEdgeInsetsMake(-(titleSize.height + padding), 0.0, 0.0, -titleSize.width)
            let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0;
            contentEdgeInsets = UIEdgeInsetsMake(edgeOffset, 0.0, edgeOffset, 0.0)
        }
        super.layoutSubviews()
    }

    override func sizeThatFits(size: CGSize) -> CGSize {
        let defaultSize = super.sizeThatFits(size)
        if let titleSize = titleLabel?.sizeThatFits(size),
        let imageSize = imageView?.sizeThatFits(size) {
            return CGSize(width: ceil(max(imageSize.width, titleSize.width)), height: ceil(imageSize.height + titleSize.height + padding))
        }
        return defaultSize
    }

    override func intrinsicContentSize() -> CGSize {
        let size = sizeThatFits(CGSize(width: CGFloat.max, height: CGFloat.max))
        return size
    }
}

3

이 두 가지 방법을 사용하십시오.

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

예:

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)
  }
}

그리고 당신은 사용할 수 있습니다 :

let myButton = UIButton.vertical(padding: 6)

3

스위프트 5- 아래 방법이 저에게 효과적입니다.

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
        )
    }

스토리 보드 / xib에서 버튼 제목이 잘리지 않도록하십시오. 그렇지 않으면 솔루션 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
        }
    }

}

2

나는 Simeon의 대답 이 아마도 최고라고 생각했지만 일부 버튼에서 이상한 결과를 얻었고 그 이유를 알 수 없었습니다. 그래서 그의 대답을 기본으로 사용하여 아래 버튼에 따라 버튼을 구현했습니다.

#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

1

UIButton이 문제를 해결하는 하위 클래스는 다음과 같습니다 .

@implementation MyVerticalButton

@synthesize titleAtBottom; // BOOL property

- (id)initWithFrame:(CGRect)frame
{
  self = [super initWithFrame:frame];
  if (self) {
    self.titleAtBottom = YES;
  }
  return self;
}

- (CGSize)sizeThatFits:(CGSize)size {
  self.titleLabel.text = [self titleForState: self.state];

  UIEdgeInsets imageInsets = self.imageEdgeInsets;
  UIEdgeInsets titleInsets = self.titleEdgeInsets;

  CGSize imageSize = [self imageForState: self.state].size;
  if (!CGSizeEqualToSize(imageSize, CGSizeZero)) {
    imageSize.width += imageInsets.left + imageInsets.right;
    imageSize.height += imageInsets.top + imageInsets.bottom;

  }

  CGSize textSize = [self.titleLabel sizeThatFits: CGSizeMake(size.width - titleInsets.left - titleInsets.right,
                                                              size.height -(imageSize.width +
                                                                            titleInsets.top+titleInsets.bottom))];
  if (!CGSizeEqualToSize(textSize, CGSizeZero)) {
    textSize.width += titleInsets.left + titleInsets.right;
    textSize.height += titleInsets.top + titleInsets.bottom;
  }

  CGSize result = CGSizeMake(MAX(textSize.width, imageSize.width),
                             textSize.height + imageSize.height);
  return result;
}

- (void)layoutSubviews {
  // needed to update all properities of child views:
  [super layoutSubviews];

  CGRect bounds = self.bounds;

  CGRect titleFrame = UIEdgeInsetsInsetRect(bounds, self.titleEdgeInsets);
  CGRect imageFrame = UIEdgeInsetsInsetRect(bounds, self.imageEdgeInsets);
  if (self.titleAtBottom) {
    CGFloat titleHeight = [self.titleLabel sizeThatFits: titleFrame.size].height;
    titleFrame.origin.y = CGRectGetMaxY(titleFrame)-titleHeight;
    titleFrame.size.height = titleHeight;
    titleFrame = CGRectStandardize(titleFrame);
    self.titleLabel.frame = titleFrame;

    CGFloat imageBottom = CGRectGetMinY(titleFrame)-(self.titleEdgeInsets.top+self.imageEdgeInsets.bottom);
    imageFrame.size.height = imageBottom - CGRectGetMinY(imageFrame);
    self.imageView.frame = CGRectStandardize(imageFrame);
  } else {
    CGFloat titleHeight = [self.titleLabel sizeThatFits: titleFrame.size].height;
    titleFrame.size.height = titleHeight;
    titleFrame = CGRectStandardize(titleFrame);
    self.titleLabel.frame = titleFrame;

    CGFloat imageTop = CGRectGetMaxY(titleFrame)+(self.titleEdgeInsets.bottom+self.imageEdgeInsets.top);
    imageFrame.size.height = CGRectGetMaxY(imageFrame) - imageTop;
    self.imageView.frame = CGRectStandardize(imageFrame);
  }
}

- (void)setTitleAtBottom:(BOOL)newTitleAtBottom {
  if (titleAtBottom!=newTitleAtBottom) {
    titleAtBottom=newTitleAtBottom;
    [self setNeedsLayout];
  }
}

@end

그게 다야. 매력처럼 작동합니다. 제목과 텍스트에 맞게 버튼이 작 으면 문제가 발생할 수 있습니다.


그것은 실제로 매력처럼 작동합니다! 자동 레이아웃도 있습니다. 이 솔루션을 공유해 주셔서 감사합니다. 나는 이것에 견디고 내 자신의 UIControl 서브 클래스를 만들기 위해 의지했다.
valeCocoa

1

가장 좋은 방법 중 하나는 UIButton을 서브 클래스 화하고 렌더링 방법을 재정의하는 것입니다.

- (void)awakeFromNib
{
    [super awakeFromNib];
    [self setupSubViews];
}

- (instancetype)init
{
    if (self = [super init])
    {
        [self setupSubViews];
    }
    return self;
}

- (void)setupSubViews
{
    [self.imageView setTranslatesAutoresizingMaskIntoConstraints:NO];
    [self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.imageView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];
    [self.titleLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[imageView][titleLabel]|" options:NSLayoutFormatAlignAllCenterX metrics:nil views:@{@"imageView": self.imageView, @"titleLabel": self.titleLabel}]];
}

- (CGSize)intrinsicContentSize
{
    CGSize imageSize = self.imageView.image.size;
    CGSize titleSize = [self.titleLabel.text sizeWithAttributes:@{NSFontAttributeName: self.titleLabel.font}];
    return CGSizeMake(MAX(imageSize.width, titleSize.width), imageSize.height + titleSize.height);
}

1

Objective-C에서 @simeon의 솔루션

#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

intrinsicContentSize가 여기에 맞지 않다고 생각합니다. CGSizeEqualToSize가있는 부분이 무엇인지 이해하지 못하지만 레이블 크기가 UILabel의 intrinsicContentSize와 일치하는 경우 레이블 크기가 0보다 큽니다. CGSizeMake(MAX(labelSize.width, image.size.width), image.size.height + labelSize.height + 5.0)if-case로 돌아 가면 충분합니다
Oliver

1

사용자 정의 글꼴을 사용 하는 경우 titleLabel 크기 계산이 제대로 작동하지 않으면

let titleLabelSize = self.titleLabel?.text?.size(withAttributes: [NSAttributedStringKey.font: self.titleLabel!.font!])


1

아이콘과 텍스트를 인서트가있는 위치에 배치하는 대신 이미지를 첨부 파일로 사용하여 NSAttributedString을 생성하고 대신 버튼의 타이틀로 설정할 수 있습니다.

let titleText = NSAttributedString(string: yourTitle, attributes: attributes)
let imageAttachment = NSTextAttachment()
imageAttachment.image = yourImage

let title = NSMutableAttributedString(string: "")
title.append(NSAttributedString(attachment: imageAttachment))
title.append(titleText)

button.setAttributedTitle(title, for: .normal)

텍스트가 이미지 아래 중앙에 위치해야하는 OPs 질문에는 작동하지 않습니다 . UIButton의 텍스트 필드 레이아웃은 단 1 줄을 표시하려면 기인 문자열에 줄 바꿈을 사용하는 경우, 따라서 심지어 작동하지 않습니다. 그렇지 않으면 좋은 해결책이 될 것입니다.
Manuel

button.titleLabel?.numberOfLines필요한 라인 수를 얻기 위해 설정하는 것도 중요합니다
swearwolf

1

현지화 친화적 솔루션 :

많은 훌륭한 솔루션 전문가이지만 현지화를 사용하는 사람들을 위해 여기에 메모를 추가하고 싶습니다.

언어 방향이 LtR에서 RtL로 변경되는 경우 버튼을 올바르게 배치하려면 왼쪽 및 오른쪽 EdgeInstets 값을 반대로 설정해야합니다.

비슷한 솔루션을 사용하여 다음과 같이 구현했습니다.

extension UIButton {

    func alignVertical(spacing: CGFloat, lang: String) {
        guard let imageSize = self.imageView?.image?.size,
            let text = self.titleLabel?.text,
            let font = self.titleLabel?.font
        else { return }

        let labelString = NSString(string: text)
        let titleSize = labelString.size(
            withAttributes: [NSAttributedString.Key.font: font]
        )

        var titleLeftInset: CGFloat = -imageSize.width
        var titleRigtInset: CGFloat = 0.0

        var imageLeftInset: CGFloat = 0.0
        var imageRightInset: CGFloat = -titleSize.width

        if Locale.current.languageCode! != "en" { // If not Left to Right language
            titleLeftInset = 0.0
            titleRigtInset = -imageSize.width

            imageLeftInset = -titleSize.width
            imageRightInset = 0.0
        }

        self.titleEdgeInsets = UIEdgeInsets(
            top: 0.0,
            left: titleLeftInset,
            bottom: -(imageSize.height + spacing),
            right: titleRigtInset
        )
        self.imageEdgeInsets = UIEdgeInsets(
            top: -(titleSize.height + spacing),
            left: imageLeftInset,
            bottom: 0.0,
            right: imageRightInset
        )
        let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0;
        self.contentEdgeInsets = UIEdgeInsets(
            top: edgeOffset,
            left: 0.0,
            bottom: edgeOffset,
            right: 0.0
        )
    }
}

1
영어 이외의 LTR 언어가 많이 있습니다. 버튼에서 effectiveUserInterfaceLayoutDirection을 확인하는 것이 좋습니다.
Alexsander Akers

1

서브 클래 싱 UIButton이있는 상단 이미지 및 하단 제목 버튼

class VerticalButton: UIButton {
  override func layoutSubviews() {
    super.layoutSubviews()
    let padding: CGFloat = 8
    let iH = imageView?.frame.height ?? 0
    let tH = titleLabel?.frame.height ?? 0
    let v: CGFloat = (frame.height - iH - tH - padding) / 2
    if let iv = imageView {
      let x = (frame.width - iv.frame.width) / 2
      iv.frame.origin.y = v
      iv.frame.origin.x = x
    }

    if let tl = titleLabel {
      let x = (frame.width - tl.frame.width) / 2
      tl.frame.origin.y = frame.height - tl.frame.height - v
      tl.frame.origin.x = x
    }
  }
}

1

그러나 이것은 분명히이 질문에 대한 과잉입니다 ... 내 프로젝트 중 하나에서 처음에는 아이콘이 가장 왼쪽에 정렬 된 버튼을 구현해야했습니다. 그런 다음 이미지 아래에 제목이있는 다른 버튼이 있습니다. 기존 솔루션을 검색했지만 운이 없었으므로 정렬 가능한 버튼이 있습니다.

@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
}
}

도움이 되길 바랍니다.


0

UIButton서브 클래스 내부에서 이와 같은 것

public override func layoutSubviews() {
    super.layoutSubviews()

    imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 0, 0)
    titleEdgeInsets = UIEdgeInsetsMake(0, -bounds.size.width/2 - 10, -30, 0)
}

0

꽤 간단합니다.

이 대신에 :

   button.setImage(UIImage(named: "image"), forState: .Normal)

이것을 사용하십시오 :

   button.setBackgroundImage(UIImage(named: "image", forState: .Normal)

그런 다음 다음을 사용하여 버튼에 쉽게 텍스트를 추가 할 수 있습니다.

// button.titleLabel! .font = UIFont (이름 : "FontName", 크기 : 30)

 button.setTitle("TitleText", forState: UIControlState.Normal)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.