UIButton의 텍스트 및 이미지를 imageEdgeInsets 및 titleEdgeInsets와 정렬


249

이미지와 텍스트 시작 사이에 약 2-3 픽셀의 공간이 있도록 두 줄의 텍스트 왼쪽에 아이콘을 배치하고 싶습니다. 컨트롤 자체는 가로로 가운데 정렬됩니다 (인터페이스 빌더를 통해 설정).

버튼은 다음과 유사합니다.

|                  |
|[Image] Add To    |
|        Favorites |

contentEdgeInset, imageEdgeInsets 및 titleEdgeInsets를 사용 하여이 구성을 시도하고 있습니다. 음수 값은 가장자리를 확장하고 양수 값은 축소하여 중앙에 더 가깝게 이동한다는 것을 알고 있습니다.

나는 시도했다 :

[button setTitleEdgeInsets:UIEdgeInsetsMake(0, -image.size.width, 0, 0)];
[button setImageEdgeInsets:UIEdgeInsetsMake(0, button.titleLabel.bounds.size.width, 0, 0)];

그러나 이것은 올바르게 표시되지 않습니다. 값을 조정했지만 왼쪽 삽입 값에서 -5에서 -10으로 이동해도 예상대로 이동하지 않는 것 같습니다. -10은 텍스트를 왼쪽 끝까지 스쿠 팅하므로 -5는 왼쪽에서 절반 정도 스쿠 트 할 것으로 예상했지만 그렇지 않습니다.

삽입의 논리는 무엇입니까? 이미지 배치 및 관련 용어에 익숙하지 않습니다.

나는이 SO 질문을 참조로 사용했지만 내 가치에 관한 것이 잘못되었습니다. UIButton : imageEdgeInsets 및 titleEdgeInsets를 사용하여 이미지와 텍스트를 가운데에 배치하는 방법은 무엇입니까?

답변:


392

나는 문서에 동의 imageEdgeInsets하고titleEdgeInsets 더 나은해야하지만, 내가 시행 착오에 의존하지 않고 정확한 위치를 얻는 방법을 알아 냈어.

일반적인 아이디어는 이 질문 에 있지만 텍스트와 이미지를 모두 중앙에 두려는 경우였습니다. 이미지와 텍스트가 개별적으로 중앙에 배치되는 것을 원하지 않고 이미지와 텍스트가 단일 엔티티로 함께 중앙에 배치되기를 원합니다. 이것은 실제로 UIButton이 이미 수행하는 것이므로 간격을 조정하기 만하면됩니다.

CGFloat spacing = 10; // the amount of spacing to appear between image and title
tabBtn.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, spacing);
tabBtn.titleEdgeInsets = UIEdgeInsetsMake(0, spacing, 0, 0);

나는 이것을 UIButton의 카테고리로 바 꾸었으므로 사용하기 쉽습니다.

UIButton + Position.h

@interface UIButton(ImageTitleCentering)

-(void) centerButtonAndImageWithSpacing:(CGFloat)spacing;

@end

UIButton + Position.m

@implementation UIButton(ImageTitleCentering)

-(void) centerButtonAndImageWithSpacing:(CGFloat)spacing {
    self.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, spacing);
    self.titleEdgeInsets = UIEdgeInsetsMake(0, spacing, 0, 0);
}

@end

이제 내가해야 할 일은 다음과 같습니다.

[button centerButtonAndImageWithSpacing:10];

그리고 매번 필요한 것을 얻습니다. 더 이상 가장자리를 수동으로 엉망으로 만들지 않아도됩니다.

편집 : 이미지 및 텍스트 교환

댓글에서 @Javal 님의 답변에 답변

동일한 메커니즘을 사용하여 이미지와 텍스트를 바꿀 수 있습니다. 스왑을 수행하려면 음수 간격을 사용하고 텍스트 너비와 이미지 너비도 포함하십시오. 이를 위해서는 프레임을 알고 레이아웃을 이미 수행해야합니다.

[self.view layoutIfNeeded];
CGFloat flippedSpacing = -(desiredSpacing + button.currentImage.size.width + button.titleLabel.frame.size.width);
[button centerButtonAndImageWithSpacing:flippedSpacing];

물론 두 번째 범주 방법을 추가 할 수있는 좋은 방법을 만들고 싶을 것입니다. 이것은 독자에게 연습으로 남겨 둡니다.


일반 타이틀과 강조 표시된 타이틀이 다른 경우 사용자가 버튼을 강조 표시하고 강조 표시를 해제 할 때 제목을 중앙에 맞추려면 어떻게해야합니까?
user102008

@ user102008 완전히 다른 제목입니까? 아니면 그냥 다른 색? 위치 결정은 당신이 사용하는 경우 변경하지 마십시오 [UIButton setTitleColor:forState:], 또는 [UIButton setTitle:forState:].
Kekoa

5
[button sizeToFit]가 제대로 크기가되도록 어떻게 고정합니까?
RonLugge

@RonLugge 왜 sizeToFit이 필요한지 잘 모르겠으므로 질문에 대답 할 수 없습니다. sizeToFit을 사용하지 않고 나에게 잘 작동합니다.
Kekoa

버튼 / 텍스트가 동적이므로 sizeToFit가 필요하므로 (사용자 정의) 레이블에 맞게 버튼 크기를 조정해야합니다. 문제는 추가 공간을 보상하지 않는다는 것입니다. 나는 그것을 재정의하고, 프레임의 너비를 10

397

이 파티에 조금 늦었지만 추가 할만한 것이 있다고 생각합니다.

Kekoa의 답변은 훌륭하지만 RonLugge가 언급했듯이 버튼을 더 이상 존중하지 않거나 sizeToFit더 중요하게는 본질적으로 크기가 조정 될 때 버튼이 내용을 클리핑 할 수 있습니다. 이케!

하지만 먼저

내가 믿는 방법에 대한 간략한 설명 imageEdgeInsetstitleEdgeInsets일 :

에 대한 문서imageEdgeInsets 는 부분적으로 다음과 같습니다.

이 속성을 사용하여 버튼 이미지의 유효 그리기 사각형의 크기를 조정하고 위치를 조정할 수 있습니다. 4 개의 삽입 (상단, 왼쪽, 하단, 오른쪽) 각각에 대해 다른 값을 지정할 수 있습니다. 양수 값은 해당 가장자리를 줄이거 나 삽입하여 버튼 중앙에 더 가깝게 이동합니다. 음수 값은 해당 가장자리를 확장하거나 시작합니다.

이 문서는 버튼에 제목이없고 이미지 일 뿐이라고 상상하여 작성되었다고 생각합니다. 이런 식으로 생각하는 것이 훨씬 합리적이며 UIEdgeInsets평소 처럼 행동합니다 . 기본적으로 이미지의 프레임 (또는 제목이있는 titleEdgeInsets)은 양의 삽입으로 안쪽으로 이동하고 음의 삽입으로 바깥쪽으로 이동합니다.

그래, 뭐?

나는 거기에 도착하고있다! 기본적으로 이미지와 제목을 설정하면 다음과 같이 설정됩니다 (버튼 테두리는 녹색으로 표시되어 있습니다).

시작 이미지;  제목과 이미지 사이에 공백이 없습니다.

이미지와 제목 사이에 간격을 두려면 어느 쪽이든 으 깨지 말고 각 이미지와 제목에 2 개씩 4 개의 다른 삽입을 설정해야합니다. 해당 요소의 프레임 크기 를 변경하지 않고 위치 만 변경하기 때문 입니다. 이런 식으로 생각하기 시작하면 Kekoa의 우수한 범주에 필요한 변경 사항이 분명해집니다.

@implementation UIButton(ImageTitleCentering)

- (void)centerButtonAndImageWithSpacing:(CGFloat)spacing {
    CGFloat insetAmount = spacing / 2.0;
    self.imageEdgeInsets = UIEdgeInsetsMake(0, -insetAmount, 0, insetAmount);
    self.titleEdgeInsets = UIEdgeInsetsMake(0, insetAmount, 0, -insetAmount);
}

@end

그러나 기다려라 . 내가 그렇게하면 나는 이것을 얻는다.

간격은 좋지만 이미지와 제목은보기의 프레임 외부에 있습니다.

오 예! 나는 문서 가 이것에 대해 경고했다는 것을 잊었다 . 그들은 부분적으로 말합니다 :

이 속성은 레이아웃 중에 이미지를 배치 할 때만 사용됩니다. 버튼은이 속성을 사용하여 intrinsicContentSize및 을 결정하지 않습니다 sizeThatFits:.

그러나이 있다 캔 도움이 속성, 그의는 contentEdgeInsets. 이에 대한 문서 는 부분적으로 말합니다.

버튼은 결정이 속성을 사용 intrinsicContentSize하고 sizeThatFits:.

그 좋은 소리. 카테고리를 다시 한 번 조정하겠습니다.

@implementation UIButton(ImageTitleCentering)

- (void)centerButtonAndImageWithSpacing:(CGFloat)spacing {
    CGFloat insetAmount = spacing / 2.0;
    self.imageEdgeInsets = UIEdgeInsetsMake(0, -insetAmount, 0, insetAmount);
    self.titleEdgeInsets = UIEdgeInsetsMake(0, insetAmount, 0, -insetAmount);
    self.contentEdgeInsets = UIEdgeInsetsMake(0, insetAmount, 0, insetAmount);
}

@end

그리고 당신은 무엇을 얻습니까?

간격과 프레임이 이제 정확합니다.

나에게 승자처럼 보인다.


스위프트에서 일하면서 전혀 생각하고 싶지 않습니까? 다음은 Swift의 최종 확장 버전입니다.

extension UIButton {
    func centerTextAndImage(spacing: CGFloat) {
        let insetAmount = spacing / 2
        imageEdgeInsets = UIEdgeInsets(top: 0, left: -insetAmount, bottom: 0, right: insetAmount)
        titleEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount, bottom: 0, right: -insetAmount)
        contentEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount, bottom: 0, right: insetAmount)
    }
}

25
정말 좋은 답변입니다! 네, 파티에 몇 년 늦었지만이 문제 intrinsicContentSize가 잘못되었다는 문제를 해결했습니다 . 이는 원래 답변이 수락 된 후 자동 레이아웃 날짜에 매우 중요합니다.
Acey

2
버튼 외부와 이미지 및 레이블 사이에 동일한 간격 spacing을 원하면 다음과 같이 self.contentEdgeInsets의 네 가지 값 각각에 추가하십시오 .self.contentEdgeInsets = UIEdgeInsetsMake(spacing, spacing + insetAmount, spacing, spacing + insetAmount);
Erik van der Neut

1
훌륭한 대답은 이미지가 올바르게 정렬되어 있고 텍스트 길이가 다를 때 제대로 작동하지 않는 것입니다.
jlpiedrahita

2
거의 완벽한 답변! 누락 된 것은 오른쪽에서 왼쪽으로 인터페이스에서 실행될 때 이미지와 제목의 삽입물을 뒤집어 야한다는 것입니다.
jeeeyul

방법 1~1에 화상 비율을 설정
Yestay 무라 토프

39

인터페이스 빌더에서. UIButton-> 속성 관리자-> Edge = Title을 선택하고 가장자리 삽입을 수정하십시오


38

또한 비슷한 것을 만들고 싶다면

여기에 이미지 설명을 입력하십시오

당신은 필요

1. 버튼의 수평 및 수직 정렬을

여기에 이미지 설명을 입력하십시오

  1. 필요한 모든 값을 찾고 설정 UIImageEdgeInsets

            CGSize buttonSize = button.frame.size;
            NSString *buttonTitle = button.titleLabel.text;
            CGSize titleSize = [buttonTitle sizeWithAttributes:@{ NSFontAttributeName : [UIFont camFontZonaProBoldWithSize:12.f] }];
            UIImage *buttonImage = button.imageView.image;
            CGSize buttonImageSize = buttonImage.size;
    
            CGFloat offsetBetweenImageAndText = 10; //vertical space between image and text
    
            [button setImageEdgeInsets:UIEdgeInsetsMake((buttonSize.height - (titleSize.height + buttonImageSize.height)) / 2 - offsetBetweenImageAndText,
                                                        (buttonSize.width - buttonImageSize.width) / 2,
                                                        0,0)];                
            [button setTitleEdgeInsets:UIEdgeInsetsMake((buttonSize.height - (titleSize.height + buttonImageSize.height)) / 2 + buttonImageSize.height + offsetBetweenImageAndText,
                                                        titleSize.width + [button imageEdgeInsets].left > buttonSize.width ? -buttonImage.size.width  +  (buttonSize.width - titleSize.width) / 2 : (buttonSize.width - titleSize.width) / 2 - buttonImage.size.width,
                                                        0,0)];

타이틀과 이미지가 버튼에 정렬됩니다.

또한 각 릴레이 아웃에서 이것을 업데이트하십시오.


빠른

import UIKit

extension UIButton {
    // MARK: - UIButton+Aligment

    func alignContentVerticallyByCenter(offset:CGFloat = 10) {
        let buttonSize = frame.size

        if let titleLabel = titleLabel,
            let imageView = imageView {

            if let buttonTitle = titleLabel.text,
                let image = imageView.image {
                let titleString:NSString = NSString(string: buttonTitle)
                let titleSize = titleString.sizeWithAttributes([
                    NSFontAttributeName : titleLabel.font
                    ])
                let buttonImageSize = image.size

                let topImageOffset = (buttonSize.height - (titleSize.height + buttonImageSize.height + offset)) / 2
                let leftImageOffset = (buttonSize.width - buttonImageSize.width) / 2
                imageEdgeInsets = UIEdgeInsetsMake(topImageOffset,
                                                   leftImageOffset,
                                                   0,0)

                let titleTopOffset = topImageOffset + offset + buttonImageSize.height
                let leftTitleOffset = (buttonSize.width - titleSize.width) / 2 - image.size.width

                titleEdgeInsets = UIEdgeInsetsMake(titleTopOffset,
                                                   leftTitleOffset,
                                                   0,0)
            }
        }
    }
}

29

이것을 사용하면 많은 문제를 피할 수 있습니다.

myButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;   
myButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;

그러면 모든 콘텐츠가 자동으로 왼쪽에 정렬됩니다 (또는 원하는 위치에)

스위프트 3 :

myButton.contentHorizontalAlignment = UIControlContentHorizontalAlignment.left;   
myButton.contentVerticalAlignment = UIControlContentVerticalAlignment.center;

myButton.contentVerticalAlignment = UIControlContentVerticalAlignment.center; //
타이포

25

Xcode 8.0 에서는 insets크기 관리자 를 변경 하여 간단하게 수행 할 수 있습니다 .

UIButton-> 속성 관리자-> 크기 관리자로 이동하여 내용, 이미지 및 제목 삽입을 수정하십시오.

여기에 이미지 설명을 입력하십시오

오른쪽에서 이미지를 변경하려면 Force Right-to-left속성 관리자에서 시맨틱 속성을 로 변경하면 됩니다.

여기에 이미지 설명을 입력하십시오


제 경우에는 버튼 이미지가 xcode 10을 사용하여 텍스트의 오른쪽으로 이동하지 않습니까? 도울 수 있니?
Satish Mavani

안녕하세요 Satish, 이것은 xcode 10에서도 잘 작동합니다. 배경 이미지가 아닌 이미지를 설정하고 크기 관리자에서 이미지 곤충을 변경할 수 있기를 바랍니다.
Sahil

18

나는이 파티에도 약간 늦었지만 추가 할 유용한 것이 있다고 생각합니다 : o).

UIButton버튼의 이미지가 세로 또는 가로로 레이아웃되는 위치를 선택할 수 있도록 서브 클래스를 만들었습니다 .

이런 종류의 버튼을 만들 수 있음을 의미합니다. 다른 종류의 버튼

내 클래스로 이러한 버튼을 만드는 방법에 대한 자세한 내용은 다음과 같습니다.

func makeButton (imageVerticalAlignment:LayoutableButton.VerticalAlignment, imageHorizontalAlignment:LayoutableButton.HorizontalAlignment, title:String) -> LayoutableButton {
    let button = LayoutableButton ()

    button.imageVerticalAlignment = imageVerticalAlignment
    button.imageHorizontalAlignment = imageHorizontalAlignment

    button.setTitle(title, for: .normal)

    // add image, border, ...

    return button
}

let button1 = makeButton(imageVerticalAlignment: .center, imageHorizontalAlignment: .left, title: "button1")
let button2 = makeButton(imageVerticalAlignment: .center, imageHorizontalAlignment: .right, title: "button2")
let button3 = makeButton(imageVerticalAlignment: .top, imageHorizontalAlignment: .center, title: "button3")
let button4 = makeButton(imageVerticalAlignment: .bottom, imageHorizontalAlignment: .center, title: "button4")
let button5 = makeButton(imageVerticalAlignment: .bottom, imageHorizontalAlignment: .center, title: "button5")
button5.contentEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)

이를 위해, 나는 2 개 속성을 추가 : imageVerticalAlignmentimageHorizontalAlignment. 물론, 버튼에 이미지 나 제목 만있는 경우에는이 클래스를 전혀 사용하지 마십시오!

또한 이름이 지정된 속성을 추가했습니다. imageToTitleSpacing제목과 이미지 사이의 간격을 조정할 수 .

이 클래스를 사용하려는 경우 호환 될 수 있도록 최선을 노력 imageEdgeInsets, titleEdgeInsets그리고 contentEdgeInsets새로운 레이아웃 속성을 직접 또는 combinaison에.

@ravron이 설명했듯이 버튼 내용을 올바르게 만들기 위해 최선을 다합니다 (빨간색 테두리로 볼 수 있듯이).

Interface Builder에서도 사용할 수 있습니다.

  1. UIButton 만들기
  2. 버튼 클래스 변경
  3. "중앙", "상단", "하단", "왼쪽"또는 "오른쪽"을 사용하여 레이아웃 가능 속성 조정 버튼 속성

여기 코드 ( 장점 ) :

@IBDesignable
class LayoutableButton: UIButton {

    enum VerticalAlignment : String {
        case center, top, bottom, unset
    }


    enum HorizontalAlignment : String {
        case center, left, right, unset
    }


    @IBInspectable
    var imageToTitleSpacing: CGFloat = 8.0 {
        didSet {
            setNeedsLayout()
        }
    }


    var imageVerticalAlignment: VerticalAlignment = .unset {
        didSet {
            setNeedsLayout()
        }
    }

    var imageHorizontalAlignment: HorizontalAlignment = .unset {
        didSet {
            setNeedsLayout()
        }
    }

    @available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'imageVerticalAlignment' instead.")
    @IBInspectable
    var imageVerticalAlignmentName: String {
        get {
            return imageVerticalAlignment.rawValue
        }
        set {
            if let value = VerticalAlignment(rawValue: newValue) {
                imageVerticalAlignment = value
            } else {
                imageVerticalAlignment = .unset
            }
        }
    }

    @available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'imageHorizontalAlignment' instead.")
    @IBInspectable
    var imageHorizontalAlignmentName: String {
        get {
            return imageHorizontalAlignment.rawValue
        }
        set {
            if let value = HorizontalAlignment(rawValue: newValue) {
                imageHorizontalAlignment = value
            } else {
                imageHorizontalAlignment = .unset
            }
        }
    }

    var extraContentEdgeInsets:UIEdgeInsets = UIEdgeInsets.zero

    override var contentEdgeInsets: UIEdgeInsets {
        get {
            return super.contentEdgeInsets
        }
        set {
            super.contentEdgeInsets = newValue
            self.extraContentEdgeInsets = newValue
        }
    }

    var extraImageEdgeInsets:UIEdgeInsets = UIEdgeInsets.zero

    override var imageEdgeInsets: UIEdgeInsets {
        get {
            return super.imageEdgeInsets
        }
        set {
            super.imageEdgeInsets = newValue
            self.extraImageEdgeInsets = newValue
        }
    }

    var extraTitleEdgeInsets:UIEdgeInsets = UIEdgeInsets.zero

    override var titleEdgeInsets: UIEdgeInsets {
        get {
            return super.titleEdgeInsets
        }
        set {
            super.titleEdgeInsets = newValue
            self.extraTitleEdgeInsets = newValue
        }
    }

    //Needed to avoid IB crash during autolayout
    override init(frame: CGRect) {
        super.init(frame: frame)
    }


    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        self.imageEdgeInsets = super.imageEdgeInsets
        self.titleEdgeInsets = super.titleEdgeInsets
        self.contentEdgeInsets = super.contentEdgeInsets
    }

    override func layoutSubviews() {
        if let imageSize = self.imageView?.image?.size,
            let font = self.titleLabel?.font,
            let textSize = self.titleLabel?.attributedText?.size() ?? self.titleLabel?.text?.size(attributes: [NSFontAttributeName: font]) {

            var _imageEdgeInsets = UIEdgeInsets.zero
            var _titleEdgeInsets = UIEdgeInsets.zero
            var _contentEdgeInsets = UIEdgeInsets.zero

            let halfImageToTitleSpacing = imageToTitleSpacing / 2.0

            switch imageVerticalAlignment {
            case .bottom:
                _imageEdgeInsets.top = (textSize.height + imageToTitleSpacing) / 2.0
                _imageEdgeInsets.bottom = (-textSize.height - imageToTitleSpacing) / 2.0
                _titleEdgeInsets.top = (-imageSize.height - imageToTitleSpacing) / 2.0
                _titleEdgeInsets.bottom = (imageSize.height + imageToTitleSpacing) / 2.0
                _contentEdgeInsets.top = (min (imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0
                _contentEdgeInsets.bottom = (min (imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0
                //only works with contentVerticalAlignment = .center
                contentVerticalAlignment = .center
            case .top:
                _imageEdgeInsets.top = (-textSize.height - imageToTitleSpacing) / 2.0
                _imageEdgeInsets.bottom = (textSize.height + imageToTitleSpacing) / 2.0
                _titleEdgeInsets.top = (imageSize.height + imageToTitleSpacing) / 2.0
                _titleEdgeInsets.bottom = (-imageSize.height - imageToTitleSpacing) / 2.0
                _contentEdgeInsets.top = (min (imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0
                _contentEdgeInsets.bottom = (min (imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0
                //only works with contentVerticalAlignment = .center
                contentVerticalAlignment = .center
            case .center:
                //only works with contentVerticalAlignment = .center
                contentVerticalAlignment = .center
                break
            case .unset:
                break
            }

            switch imageHorizontalAlignment {
            case .left:
                _imageEdgeInsets.left = -halfImageToTitleSpacing
                _imageEdgeInsets.right = halfImageToTitleSpacing
                _titleEdgeInsets.left = halfImageToTitleSpacing
                _titleEdgeInsets.right = -halfImageToTitleSpacing
                _contentEdgeInsets.left = halfImageToTitleSpacing
                _contentEdgeInsets.right = halfImageToTitleSpacing
            case .right:
                _imageEdgeInsets.left = textSize.width + halfImageToTitleSpacing
                _imageEdgeInsets.right = -textSize.width - halfImageToTitleSpacing
                _titleEdgeInsets.left = -imageSize.width - halfImageToTitleSpacing
                _titleEdgeInsets.right = imageSize.width + halfImageToTitleSpacing
                _contentEdgeInsets.left = halfImageToTitleSpacing
                _contentEdgeInsets.right = halfImageToTitleSpacing
            case .center:
                _imageEdgeInsets.left = textSize.width / 2.0
                _imageEdgeInsets.right = -textSize.width / 2.0
                _titleEdgeInsets.left = -imageSize.width / 2.0
                _titleEdgeInsets.right = imageSize.width / 2.0
                _contentEdgeInsets.left = -((imageSize.width + textSize.width) - max (imageSize.width, textSize.width)) / 2.0
                _contentEdgeInsets.right = -((imageSize.width + textSize.width) - max (imageSize.width, textSize.width)) / 2.0
            case .unset:
                break
            }

            _contentEdgeInsets.top += extraContentEdgeInsets.top
            _contentEdgeInsets.bottom += extraContentEdgeInsets.bottom
            _contentEdgeInsets.left += extraContentEdgeInsets.left
            _contentEdgeInsets.right += extraContentEdgeInsets.right

            _imageEdgeInsets.top += extraImageEdgeInsets.top
            _imageEdgeInsets.bottom += extraImageEdgeInsets.bottom
            _imageEdgeInsets.left += extraImageEdgeInsets.left
            _imageEdgeInsets.right += extraImageEdgeInsets.right

            _titleEdgeInsets.top += extraTitleEdgeInsets.top
            _titleEdgeInsets.bottom += extraTitleEdgeInsets.bottom
            _titleEdgeInsets.left += extraTitleEdgeInsets.left
            _titleEdgeInsets.right += extraTitleEdgeInsets.right

            super.imageEdgeInsets = _imageEdgeInsets
            super.titleEdgeInsets = _titleEdgeInsets
            super.contentEdgeInsets = _contentEdgeInsets

        } else {
            super.imageEdgeInsets = extraImageEdgeInsets
            super.titleEdgeInsets = extraTitleEdgeInsets
            super.contentEdgeInsets = extraContentEdgeInsets
        }

        super.layoutSubviews()
    }
}

1
내가 가진 IB의 균형을 위해, 몇 가지 물건을 수정 error: IB Designables: Failed to update auto layout status: The agent crashed, gist.github.com/nebiros/ecf69ff9cb90568edde071386c6c4ddb
nebiros

@nebiros 당신은 무엇이 잘못되었고 어떻게 고칠 수 있는지 설명해 줄 수 있습니까?
gbitaudeau

내가 복사하고 스크립트를 붙여 넣을 때 @gbitaudeau, 나는 그 오류를 가지고, error: IB Designables: Failed to update auto layout status: The agent crashed때문에 init(frame: CGRect)무시되지도, 내가 추가 @available주석을 ..., 당신은 할 수 있습니다 diff -Naur당신은 ;-) 원하는 경우
nebiros

9

로캘 변경 사항에 대한 Riley Avron의 간단한 추가 사항 :

extension UIButton {
    func centerTextAndImage(spacing: CGFloat) {
        let insetAmount = spacing / 2
        let writingDirection = UIApplication.sharedApplication().userInterfaceLayoutDirection
        let factor: CGFloat = writingDirection == .LeftToRight ? 1 : -1

        self.imageEdgeInsets = UIEdgeInsets(top: 0, left: -insetAmount*factor, bottom: 0, right: insetAmount*factor)
        self.titleEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount*factor, bottom: 0, right: -insetAmount*factor)
        self.contentEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount, bottom: 0, right: insetAmount)
    }
}

6

스위프트 4.x

extension UIButton {
    func centerTextAndImage(spacing: CGFloat) {
        let insetAmount = spacing / 2
        let writingDirection = UIApplication.shared.userInterfaceLayoutDirection
        let factor: CGFloat = writingDirection == .leftToRight ? 1 : -1

        self.imageEdgeInsets = UIEdgeInsets(top: 0, left: -insetAmount*factor, bottom: 0, right: insetAmount*factor)
        self.titleEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount*factor, bottom: 0, right: -insetAmount*factor)
        self.contentEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount, bottom: 0, right: insetAmount)
    }
}

사용법 :

button.centerTextAndImage(spacing: 10.0)

custiom 이미지 크기와 함께 사용하는 방법
midhun p

2

아래 코드를 작성합니다. 제품 버전에서 잘 작동합니다. Supprot Swift 4.2 이상

extension UIButton{
 enum ImageTitleRelativeLocation {
    case imageUpTitleDown
    case imageDownTitleUp
    case imageLeftTitleRight
    case imageRightTitleLeft
}
 func centerContentRelativeLocation(_ relativeLocation: 
                                      ImageTitleRelativeLocation,
                                   spacing: CGFloat = 0) {
    assert(contentVerticalAlignment == .center,
           "only works with contentVerticalAlignment = .center !!!")

    guard (title(for: .normal) != nil) || (attributedTitle(for: .normal) != nil) else {
        assert(false, "TITLE IS NIL! SET TITTLE FIRST!")
        return
    }

    guard let imageSize = self.currentImage?.size else {
        assert(false, "IMGAGE IS NIL! SET IMAGE FIRST!!!")
        return
    }
    guard let titleSize = titleLabel?
        .systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) else {
            assert(false, "TITLELABEL IS NIL!")
            return
    }

    let horizontalResistent: CGFloat
    // extend contenArea in case of title is shrink
    if frame.width < titleSize.width + imageSize.width {
        horizontalResistent = titleSize.width + imageSize.width - frame.width
        print("horizontalResistent", horizontalResistent)
    } else {
        horizontalResistent = 0
    }

    var adjustImageEdgeInsets: UIEdgeInsets = .zero
    var adjustTitleEdgeInsets: UIEdgeInsets = .zero
    var adjustContentEdgeInsets: UIEdgeInsets = .zero

    let verticalImageAbsOffset = abs((titleSize.height + spacing) / 2)
    let verticalTitleAbsOffset = abs((imageSize.height + spacing) / 2)

    switch relativeLocation {
    case .imageUpTitleDown:

        adjustImageEdgeInsets.top = -verticalImageAbsOffset
        adjustImageEdgeInsets.bottom = verticalImageAbsOffset
        adjustImageEdgeInsets.left = titleSize.width / 2 + horizontalResistent / 2
        adjustImageEdgeInsets.right = -titleSize.width / 2 - horizontalResistent / 2

        adjustTitleEdgeInsets.top = verticalTitleAbsOffset
        adjustTitleEdgeInsets.bottom = -verticalTitleAbsOffset
        adjustTitleEdgeInsets.left = -imageSize.width / 2 + horizontalResistent / 2
        adjustTitleEdgeInsets.right = imageSize.width / 2 - horizontalResistent / 2

        adjustContentEdgeInsets.top = spacing
        adjustContentEdgeInsets.bottom = spacing
        adjustContentEdgeInsets.left = -horizontalResistent
        adjustContentEdgeInsets.right = -horizontalResistent
    case .imageDownTitleUp:
        adjustImageEdgeInsets.top = verticalImageAbsOffset
        adjustImageEdgeInsets.bottom = -verticalImageAbsOffset
        adjustImageEdgeInsets.left = titleSize.width / 2 + horizontalResistent / 2
        adjustImageEdgeInsets.right = -titleSize.width / 2 - horizontalResistent / 2

        adjustTitleEdgeInsets.top = -verticalTitleAbsOffset
        adjustTitleEdgeInsets.bottom = verticalTitleAbsOffset
        adjustTitleEdgeInsets.left = -imageSize.width / 2 + horizontalResistent / 2
        adjustTitleEdgeInsets.right = imageSize.width / 2 - horizontalResistent / 2

        adjustContentEdgeInsets.top = spacing
        adjustContentEdgeInsets.bottom = spacing
        adjustContentEdgeInsets.left = -horizontalResistent
        adjustContentEdgeInsets.right = -horizontalResistent
    case .imageLeftTitleRight:
        adjustImageEdgeInsets.left = -spacing / 2
        adjustImageEdgeInsets.right = spacing / 2

        adjustTitleEdgeInsets.left = spacing / 2
        adjustTitleEdgeInsets.right = -spacing / 2

        adjustContentEdgeInsets.left = spacing
        adjustContentEdgeInsets.right = spacing
    case .imageRightTitleLeft:
        adjustImageEdgeInsets.left = titleSize.width + spacing / 2
        adjustImageEdgeInsets.right = -titleSize.width - spacing / 2

        adjustTitleEdgeInsets.left = -imageSize.width - spacing / 2
        adjustTitleEdgeInsets.right = imageSize.width + spacing / 2

        adjustContentEdgeInsets.left = spacing
        adjustContentEdgeInsets.right = spacing
    }

    imageEdgeInsets = adjustImageEdgeInsets
    titleEdgeInsets = adjustTitleEdgeInsets
    contentEdgeInsets = adjustContentEdgeInsets

    setNeedsLayout()
}
}

1

다음은 imageEdgeInsets 사용 방법에 대한 간단한 예입니다. 이렇게하면 적중 가능 영역이 10 픽셀 더 큰 30x30 버튼 (50x50)이 만들어집니다.

    var expandHittableAreaAmt : CGFloat = 10
    var buttonWidth : CGFloat = 30
    var button = UIButton.buttonWithType(UIButtonType.Custom) as UIButton
    button.frame = CGRectMake(0, 0, buttonWidth+expandHittableAreaAmt, buttonWidth+expandHittableAreaAmt)
    button.imageEdgeInsets = UIEdgeInsetsMake(expandHittableAreaAmt, expandHittableAreaAmt, expandHittableAreaAmt, expandHittableAreaAmt)
    button.setImage(UIImage(named: "buttonImage"), forState: .Normal)
    button.addTarget(self, action: "didTouchButton:", forControlEvents:.TouchUpInside)

0

Swift 3의 우아한 방법으로 이해하는 것이 좋습니다.

override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
    let leftMargin:CGFloat = 40
    let imgWidth:CGFloat = 24
    let imgHeight:CGFloat = 24
    return CGRect(x: leftMargin, y: (contentRect.size.height-imgHeight) * 0.5, width: imgWidth, height: imgHeight)
}

override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
    let leftMargin:CGFloat = 80
    let rightMargin:CGFloat = 80
    return CGRect(x: leftMargin, y: 0, width: contentRect.size.width-leftMargin-rightMargin, height: contentRect.size.height)
}
override func backgroundRect(forBounds bounds: CGRect) -> CGRect {
    let leftMargin:CGFloat = 10
    let rightMargin:CGFloat = 10
    let topMargin:CGFloat = 10
    let bottomMargin:CGFloat = 10
    return CGRect(x: leftMargin, y: topMargin, width: bounds.size.width-leftMargin-rightMargin, height: bounds.size.height-topMargin-bottomMargin)
}
override func contentRect(forBounds bounds: CGRect) -> CGRect {
    let leftMargin:CGFloat = 5
    let rightMargin:CGFloat = 5
    let topMargin:CGFloat = 5
    let bottomMargin:CGFloat = 5
    return CGRect(x: leftMargin, y: topMargin, width: bounds.size.width-leftMargin-rightMargin, height: bounds.size.height-topMargin-bottomMargin)
}

-1

신속한 4.2 버전의 솔루션은 다음과 같습니다.

let spacing: CGFloat = 10 // the amount of spacing to appear between image and title
self.button?.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: spacing)
self.button?.titleEdgeInsets = UIEdgeInsets(top: 0, left: spacing, bottom: 0, right: 0)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.