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

이미지와 텍스트 시작 사이에 약 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를 사용하여 이미지와 텍스트를 가운데에 배치하는 방법은 무엇입니까?



답변

나는 문서에 동의 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];

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


답변

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

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


답변

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


답변

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

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

당신은 필요

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


답변

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

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

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

스위프트 3 :

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


답변

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

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

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

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

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


답변

나는이 파티에도 약간 늦었지만 추가 할 유용한 것이 있다고 생각합니다 : 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()
    }
}