Căn chỉnh văn bản và hình ảnh trên UIButton với imageEdgeInsets và titleEdgeInsets


247

Tôi muốn đặt một biểu tượng bên trái của hai dòng văn bản sao cho có khoảng 2-3 pixel khoảng cách giữa hình ảnh và phần bắt đầu của văn bản. Bản thân điều khiển được căn giữa theo chiều ngang (được đặt qua Trình tạo giao diện)

Nút này sẽ giống như thế này:

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

Tôi đang cố gắng định cấu hình này với contentEdgeInset, imageEdgeInsets và titleEdgeInsets nhưng không có kết quả. Tôi hiểu rằng một giá trị âm mở rộng cạnh trong khi giá trị dương thu nhỏ nó để di chuyển nó đến gần trung tâm hơn.

Tôi đã thử:

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

nhưng điều này không hiển thị chính xác. Tôi đã điều chỉnh các giá trị nhưng đi từ -5 đến -10 ở giá trị bên trái không xuất hiện để di chuyển nó theo cách mong đợi. -10 sẽ di chuyển văn bản theo hướng bên trái, vì vậy tôi mong đợi -5 sẽ di chuyển nó một nửa từ bên trái nhưng không được.

Logic đằng sau miếng lót là gì? Tôi không quen thuộc với các vị trí hình ảnh và thuật ngữ liên quan.

Tôi đã sử dụng câu hỏi SO này làm tài liệu tham khảo nhưng có gì đó về giá trị của tôi không đúng. UIButton: làm cách nào để căn giữa hình ảnh và văn bản bằng imageEdgeInsets và titleEdgeInsets?

Câu trả lời:


391

Tôi đồng ý các tài liệu về imageEdgeInsetstitleEdgeInsets sẽ tốt hơn, nhưng tôi đã tìm ra cách để có được vị trí chính xác mà không cần dùng đến thử và sai.

Ý tưởng chung là ở đây trong câu hỏi này , nhưng đó là nếu bạn muốn cả văn bản và hình ảnh làm trung tâm. Chúng tôi không muốn hình ảnh và văn bản được căn giữa riêng lẻ, chúng tôi muốn hình ảnh và văn bản được tập trung lại với nhau như một thực thể duy nhất. Thực tế đây là những gì UIButton đã làm nên chúng ta chỉ cần điều chỉnh khoảng cách.

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

Tôi cũng đã biến nó thành một danh mục cho UIButton để nó dễ sử dụng:

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

Vì vậy, bây giờ tất cả những gì tôi phải làm là:

[button centerButtonAndImageWithSpacing:10];

Và tôi nhận được những gì tôi cần mỗi lần. Không còn lộn xộn với các cạnh chèn thủ công.

EDIT: Hoán đổi hình ảnh và văn bản

Đáp lại @Javal trong các bình luận

Sử dụng cơ chế tương tự, chúng ta có thể trao đổi hình ảnh và văn bản. Để thực hiện trao đổi, chỉ cần sử dụng khoảng cách âm nhưng cũng bao gồm chiều rộng của văn bản và hình ảnh. Điều này sẽ yêu cầu các khung phải được biết và bố trí được thực hiện.

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

Tất nhiên bạn có thể sẽ muốn tạo ra một phương thức hay cho việc này, có khả năng thêm một phương thức thể loại thứ hai, đây là một bài tập cho người đọc.


Nếu tôi có các tiêu đề khác nhau cho bình thường và được tô sáng, làm cách nào tôi có thể căn giữa lại khi người dùng làm nổi bật và không làm nổi bật nút?
user102008

@ user102008 Tiêu đề khác nhau hoàn toàn? Hay chỉ là những màu sắc khác nhau? Định vị không nên thay đổi nếu bạn đang sử dụng [UIButton setTitleColor:forState:]hoặc thậm chí [UIButton setTitle:forState:].
Kekoa

5
Làm thế nào để bạn sửa [nút sizeToFit] để nó có kích thước phù hợp?
RonLugge

@RonLugge Tôi không chắc tại sao bạn cần sizeToFit, vì vậy tôi không thể trả lời câu hỏi của bạn. Nó hoạt động tốt với tôi mà không cần sử dụng sizeToFit.
Kekoa

Tôi cần sizeToFit vì các nút / văn bản là động, vì vậy tôi cần kích thước nút để phù hợp với nhãn (do người dùng xác định). Vấn đề là nó không bù cho không gian được thêm vào. Tôi cố gắng ghi đè lên nó và tăng thủ công chiều rộng của khung lên 10.
RonLugge

395

Tôi đến bữa tiệc muộn một chút, nhưng tôi nghĩ rằng tôi có một cái gì đó hữu ích để thêm vào.

Câu trả lời của Kekoa rất hay nhưng, như RonLugge đề cập, nó có thể khiến nút không còn tôn trọng sizeToFithoặc quan trọng hơn, có thể khiến nút này cắt nội dung của nó khi nó có kích thước thực sự. Rất tiếc!

Đầu tiên, mặc dù,

Một lời giải thích ngắn gọn về cách tôi tin imageEdgeInsetstitleEdgeInsetslàm việc:

Các tài liệu choimageEdgeInsets có những điều sau đây để nói, một phần:

Sử dụng thuộc tính này để thay đổi kích thước và định vị lại hình chữ nhật vẽ hiệu quả cho hình ảnh nút. Bạn có thể chỉ định một giá trị khác nhau cho mỗi trong số bốn phần tử (trên cùng, bên trái, dưới cùng, bên phải). Một giá trị dương co lại, hoặc chèn vào, cạnh đó di chuyển nó đến gần trung tâm của nút. Một giá trị âm mở rộng, hoặc đặt ra, cạnh đó.

Tôi tin rằng tài liệu này được viết tưởng tượng rằng nút không có tiêu đề, chỉ là một hình ảnh. Nó có ý nghĩa hơn rất nhiều khi nghĩ về cách này, và hành xử như thế nào UIEdgeInsetsthường làm. Về cơ bản, khung của hình ảnh (hoặc tiêu đề, với titleEdgeInsets) được di chuyển vào trong cho các phần tử dương và hướng ra ngoài cho các phần tử âm.

Được, vậy thì sao?

Tôi đang đến đó! Đây là những gì bạn có theo mặc định, đặt hình ảnh và tiêu đề (viền nút chỉ màu xanh lá cây để hiển thị vị trí của nó):

Hình ảnh bắt đầu;  không có khoảng cách giữa tiêu đề và hình ảnh

Khi bạn muốn khoảng cách giữa một hình ảnh và một tiêu đề, mà không làm cho bị nghiền nát, bạn cần đặt bốn phần tử khác nhau, hai phần trên mỗi hình ảnh và tiêu đề. Đó là bởi vì bạn không muốn thay đổi kích thước của các khung đó, mà chỉ là vị trí của chúng. Khi bạn bắt đầu nghĩ theo cách này, sự thay đổi cần thiết cho danh mục xuất sắc của Kekoa trở nên rõ ràng:

@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

Nhưng chờ đã , bạn nói, khi tôi làm điều đó, tôi nhận được điều này:

Giãn cách là tốt, nhưng hình ảnh và tiêu đề nằm ngoài khung nhìn.

Ồ vâng! Tôi quên mất, các tài liệu đã cảnh báo tôi về điều này. Họ nói, một phần:

Thuộc tính này chỉ được sử dụng để định vị hình ảnh trong khi bố trí. Nút không sử dụng thuộc tính này để xác định intrinsicContentSizesizeThatFits:.

Nhưng có một tài sản có thể giúp, và rằng nhân contentEdgeInsets. Các tài liệu cho rằng, một phần:

Nút sử dụng thuộc tính này để xác định intrinsicContentSizesizeThatFits:.

Điều đó nghe có vẻ tốt. Vì vậy, hãy điều chỉnh danh mục một lần nữa:

@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

Và bạn nhận được gì?

Khoảng cách và khung bây giờ là chính xác.

Trông như một người thắng cuộc đối với tôi.


Làm việc trong Swift và không muốn suy nghĩ gì cả? Đây là phiên bản cuối cùng của tiện ích mở rộng trong 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
Thật là một câu trả lời tuyệt vời! Vâng, một vài năm muộn cho bữa tiệc, nhưng điều này đã giải quyết vấn đề intrinsicContentSizekhông chính xác, điều này rất quan trọng trong những ngày bố trí tự động này kể từ khi câu trả lời ban đầu được chấp nhận.
Acey

2
Và nếu bạn muốn có cùng khoảng cách giữa bên ngoài nút và hình ảnh và nhãn, thì hãy thêm spacingvào bốn giá trị của self.contentEdgeInsets, như vậy:self.contentEdgeInsets = UIEdgeInsetsMake(spacing, spacing + insetAmount, spacing, spacing + insetAmount);
Erik van der Neut

1
Câu trả lời tuyệt vời, thật đáng tiếc nó không hoạt động khá tốt khi hình ảnh được căn chỉnh đúng và độ dài văn bản có thể thay đổi.
jlpiedrahita

2
Câu trả lời gần như hoàn hảo! Điều duy nhất còn thiếu là các hình ảnh và tiêu đề phải được đảo ngược khi nó chạy trên giao diện Phải sang trái.
jeeeyul

Cách đặt tỷ lệ hình ảnh thành 1 đến 1
Yestay Muratov 8/12/2016

39

Trong giao diện Builder. Chọn UIButton -> Attector Inspector -> Edge = Title và sửa đổi các phần tử cạnh


38

Ngoài ra nếu bạn muốn làm một cái gì đó tương tự như

nhập mô tả hình ảnh ở đây

Bạn cần

1. Đặt căn chỉnh ngang và dọc cho nút để

nhập mô tả hình ảnh ở đây

  1. Tìm tất cả các giá trị cần thiết và thiết lập 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)];

Điều này sẽ sắp xếp tiêu đề và hình ảnh của bạn trên nút.

Ngoài ra xin vui lòng lưu ý cập nhật này trên mỗi rơle


Nhanh

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

Bạn có thể tránh nhiều rắc rối bằng cách sử dụng này -

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

Điều này sẽ tự động căn chỉnh tất cả nội dung của bạn sang trái (hoặc bất cứ nơi nào bạn muốn)

Swift 3:

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

myButton.contentV verticalAlocate = UIControlContentV verticalAlocate.center; // Typo đã sửa
Naishta

25

Trong Xcode 8.0 bạn chỉ cần làm điều đó bằng cách thay đổi insetskích thước thanh tra.

Chọn UIButton -> Thanh tra thuộc tính -> chuyển đến thanh tra kích thước và sửa đổi nội dung, hình ảnh và tiêu đề.

nhập mô tả hình ảnh ở đây

Và nếu bạn muốn thay đổi hình ảnh ở phía bên phải, bạn chỉ cần thay đổi thuộc tính ngữ nghĩa thành Force Right-to-lefttrong trình kiểm tra thuộc tính.

nhập mô tả hình ảnh ở đây


trong trường hợp của tôi hình ảnh nút không bao giờ di chuyển sang bên phải của văn bản với xcode 10? bạn có thể giúp?
Satish Mavani

Xin chào Satish, điều này cũng hoạt động tốt với xcode 10. Hy vọng bạn đang thiết lập hình ảnh không phải hình nền và bạn cũng có thể thay đổi côn trùng hình ảnh bằng cách sử dụng trình kiểm tra kích thước.
Sahil

18

Tôi cũng đến bữa tiệc muộn một chút, nhưng tôi nghĩ rằng tôi có một cái gì đó hữu ích để thêm vào: o).

Tôi đã tạo một UIButtonlớp con với mục đích là có thể chọn vị trí hình ảnh của nút, theo chiều dọc hoặc chiều ngang.

Nó có nghĩa là bạn có thể thực hiện loại nút này: các loại nút khác nhau

Dưới đây là chi tiết về cách tạo các nút này với lớp của tôi:

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)

Để làm điều đó, tôi đã thêm 2 thuộc tính: imageVerticalAlignmentimageHorizontalAlignment. Dĩ nhiên, nếu nút của bạn chỉ có hình ảnh hoặc tiêu đề ... thì đừng sử dụng lớp này!

Tôi cũng đã thêm một thuộc tính có tên imageToTitleSpacing cho phép bạn điều chỉnh không gian giữa tiêu đề và hình ảnh.

Lớp này cố gắng hết sức mình để tương thích nếu bạn muốn sử dụng imageEdgeInsets, titleEdgeInsetscontentEdgeInsetstrực tiếp hoặc trong combinaison với các thuộc tính bố trí mới.

Như @ravron giải thích cho chúng tôi, tôi cố gắng hết sức để làm cho cạnh nội dung nút chính xác (như bạn có thể thấy với các viền màu đỏ).

Bạn cũng có thể sử dụng nó trong Trình tạo giao diện:

  1. Tạo một UIButton
  2. Thay đổi lớp nút
  3. Điều chỉnh các thuộc tính có thể bố trí bằng cách sử dụng "trung tâm", "trên cùng", "dưới cùng", "trái" hoặc "phải" thuộc tính nút

Đây là mã ( ý chính ):

@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
Tôi sửa một số nội dung, để không phá vỡ IB với error: IB Designables: Failed to update auto layout status: The agent crashed, gist.github.com/nebiros/ecf69ff9cb90568edde071386c6c4ddb
nebiros

@nebiros bạn có thể giải thích những gì sai và cách bạn sửa nó không?
gbitaudeau

@gbitaudeau khi tôi sao chép và dán tập lệnh, tôi đã gặp lỗi đó error: IB Designables: Failed to update auto layout status: The agent crashed, vì tôi init(frame: CGRect)không bị ghi đè, ngoài ra, tôi thêm @availablechú thích, bạn có thể diff -Naurnếu bạn muốn, ;-)
nebiros

9

Một bổ sung nhỏ cho câu trả lời của Riley Avron cho thay đổi ngôn ngữ tài khoản:

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

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

Cách sử dụng :

button.centerTextAndImage(spacing: 10.0)

Làm thế nào có thể sử dụng với kích thước hình ảnh lưu ký?
midhun p

2

Tôi viết mã bewlow. Nó hoạt động tốt trong phiên bản sản phẩm. 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

Dưới đây là một ví dụ đơn giản về cách sử dụng imageEdgeInsets Điều này sẽ tạo một nút 30x30 với diện tích có thể điều chỉnh lớn hơn 10 pixel trong suốt khoảng cách (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

Một cách thanh lịch trong Swift 3 và tốt hơn để hiểu:

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

Phiên bản 4.2 nhanh chóng của giải pháp sẽ như sau:

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.