UIButton: làm cách nào để căn giữa hình ảnh và văn bản bằng imageEdgeInsets và titleEdgeInsets?


159

Nếu tôi chỉ đặt một hình ảnh vào một nút và đặt imageEdgeInsets gần với đỉnh hơn, hình ảnh sẽ ở giữa và tất cả đều hoạt động như mong đợi:

[button setImage:image forState:UIControlStateNormal];
[button setImageEdgeInsets:UIEdgeInsetsMake(-15.0, 0.0, 0.0, 0.0)];

Nếu tôi chỉ đặt một văn bản trong một nút và đặt titleEdgeInsets gần phía dưới hơn, văn bản sẽ ở giữa và tất cả các công việc như mong đợi:

[button setTitle:title forState:UIControlStateNormal];
[button setTitleEdgeInsets:UIEdgeInsetsMake(0.0, 0.0, -30, 0.0)];

Nhưng, nếu tôi đặt 4 dòng lại với nhau, văn bản sẽ can thiệp vào hình ảnh và cả hai đều mất căn chỉnh trung tâm.

Tất cả hình ảnh của tôi có chiều rộng 30 pixel và nếu tôi đặt 30 vào tham số bên trái của UIEdgeInsetMake cho setTitleEdgeInsets, văn bản sẽ được căn giữa một lần nữa. Vấn đề là hình ảnh không bao giờ được căn giữa vì có vẻ như nó phụ thuộc vào kích thước button.titleLabel. Tôi đã thử nhiều tính toán với kích thước nút, kích thước hình ảnh, kích thước tiêu đề và không bao giờ có được cả hai trung tâm hoàn hảo.

Ai đó đã có cùng một vấn đề?

Câu trả lời:


412

Để biết giá trị của nó, đây là một giải pháp chung để định vị hình ảnh ở giữa văn bản mà không sử dụng bất kỳ số ma thuật nào. Lưu ý rằng mã sau đây đã lỗi thời và có lẽ bạn nên sử dụng một trong các phiên bản cập nhật bên dưới :

// the space between the image and text
CGFloat spacing = 6.0;

// lower the text and push it left so it appears centered 
//  below the image
CGSize imageSize = button.imageView.frame.size;
button.titleEdgeInsets = UIEdgeInsetsMake(
  0.0, - imageSize.width, - (imageSize.height + spacing), 0.0);

// raise the image and push it right so it appears centered
//  above the text
CGSize titleSize = button.titleLabel.frame.size;
button.imageEdgeInsets = UIEdgeInsetsMake(
  - (titleSize.height + spacing), 0.0, 0.0, - titleSize.width);

Phiên bản sau có chứa các thay đổi để hỗ trợ iOS 7+ đã được đề xuất trong các nhận xét bên dưới. Tôi đã không tự mình kiểm tra mã này, vì vậy tôi không chắc nó hoạt động tốt như thế nào hoặc liệu nó có bị hỏng nếu được sử dụng trong các phiên bản iOS trước.

// the space between the image and text
CGFloat spacing = 6.0;

// lower the text and push it left so it appears centered 
//  below the image
CGSize imageSize = button.imageView.image.size;
button.titleEdgeInsets = UIEdgeInsetsMake(
  0.0, - imageSize.width, - (imageSize.height + spacing), 0.0);

// raise the image and push it right so it appears centered
//  above the text
CGSize titleSize = [button.titleLabel.text sizeWithAttributes:@{NSFontAttributeName: button.titleLabel.font}];
button.imageEdgeInsets = UIEdgeInsetsMake(
  - (titleSize.height + spacing), 0.0, 0.0, - titleSize.width);

// increase the content height to avoid clipping
CGFloat edgeOffset = fabsf(titleSize.height - imageSize.height) / 2.0;
button.contentEdgeInsets = UIEdgeInsetsMake(edgeOffset, 0.0, edgeOffset, 0.0);

Phiên bản Swift 5.0

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

    titleEdgeInsets = UIEdgeInsets(
      top: 0.0,
      left: -imageSize.width,
      bottom: -(imageSize.height + spacing),
      right: 0.0
    )

    let titleSize = text.size(withAttributes: [.font: font])
    imageEdgeInsets = UIEdgeInsets(
      top: -(titleSize.height + spacing),
      left: 0.0,
      bottom: 0.0, right: -titleSize.width
    )

    let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0
    contentEdgeInsets = UIEdgeInsets(
      top: edgeOffset,
      left: 0.0,
      bottom: edgeOffset,
      right: 0.0
    )
  }
}

5
Tuyệt vời - cảm ơn! Tôi nghĩ rằng rất nhiều câu trả lời khác đi về "cách khó khăn" này - điều này có vẻ tốt hơn nhiều.
Joe D'Andrea

3
Tôi tìm thấy ở trên để wok, nhưng tôi hoàn toàn không có mô hình tinh thần cho làm thế nào. Bất cứ ai cũng có một liên kết đến một lời giải thích bằng hình ảnh về những điều mà các thông số EdgeInsets riêng lẻ ảnh hưởng đến? Và tại sao chiều rộng văn bản sẽ thay đổi?
Robert Atkins

3
Điều này không hoạt động khi hình ảnh được thu nhỏ để vừa với nút. Dường như UIButton (ít nhất là trong iOS 7) sử dụng image.size chứ không phải imageView.frame.size cho các tính toán định tâm của nó.
Dan Jackson

5
@Hemang và Dan Jackson, tôi đang kết hợp các đề xuất của bạn mà không tự mình kiểm tra chúng. Tôi thấy thật nực cười khi ban đầu tôi đã viết cái này cho iOS 4 và sau nhiều phiên bản này, chúng tôi vẫn phải sử dụng thuật toán bố cục của Apple để có được một tính năng rõ ràng như vậy. Hoặc ít nhất tôi cho rằng chưa có một giải pháp nào tốt hơn từ dòng upvote nhất quán và các câu trả lời không kém phần bên dưới (không có ý xúc phạm).
Jesse Crossen

5
Trong iOS8, tôi đã thấy tốt hơn khi sử dụng button.currentTitlethay vì button.titleLabel.textđặc biệt là nếu văn bản nút sẽ thay đổi. currentTitleđược đưa vào ngay lập tức, trong khi titleLabel.textcó thể chậm thay đổi, điều này có thể dẫn đến các nội dung bị sai lệch.
mjangda

59

Tìm thấy thế nào.

Đầu tiên, định cấu hình văn bản của titleLabel(vì kiểu, nghĩa là in đậm, in nghiêng, v.v.). Sau đó, sử dụng setTitleEdgeInsetsxem xét chiều rộng của hình ảnh của bạn:

[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[button setTitle:title forState:UIControlStateNormal];
[button.titleLabel setFont:[UIFont boldSystemFontOfSize:10.0]];

// Left inset is the negative of image width.
[button setTitleEdgeInsets:UIEdgeInsetsMake(0.0, -image.size.width, -25.0, 0.0)]; 

Sau đó, sử dụng setTitleEdgeInsetsxem xét độ rộng của giới hạn văn bản:

[button setImage:image forState:UIControlStateNormal];

// Right inset is the negative of text bounds width.
[button setImageEdgeInsets:UIEdgeInsetsMake(-15.0, 0.0, 0.0, -button.titleLabel.bounds.size.width)];

Bây giờ hình ảnh và văn bản sẽ được căn giữa (trong ví dụ này, hình ảnh xuất hiện phía trên văn bản).

Chúc mừng.


3
7 năm, anh bạn. Thực sự không nhớ. Khi tôi hỏi, tôi đã tự trả lời (câu trả lời của tôi là duy nhất tại thời điểm đó). Tôi đã thay đổi câu trả lời được chọn khi tác giả của câu trả lời được chọn hiện tại tập trung vào việc loại bỏ những con số ma thuật đó. Vì vậy, trong câu trả lời được chọn, bạn có thể tìm ra ý nghĩa của chúng.
reinaldoluckman

20

Bạn có thể làm điều đó với tiện ích mở rộng Swift này, một phần dựa trên câu trả lời của Jesse Crossen:

extension UIButton {
  func centerLabelVerticallyWithPadding(spacing:CGFloat) {
    // update positioning of image and title
    let imageSize = self.imageView.frame.size
    self.titleEdgeInsets = UIEdgeInsets(top:0,
                                        left:-imageSize.width,
                                        bottom:-(imageSize.height + spacing),
                                        right:0)
    let titleSize = self.titleLabel.frame.size
    self.imageEdgeInsets = UIEdgeInsets(top:-(titleSize.height + spacing),
                                        left:0,
                                        bottom: 0,
                                        right:-titleSize.width)

    // reset contentInset, so intrinsicContentSize() is still accurate
    let trueContentSize = CGRectUnion(self.titleLabel.frame, self.imageView.frame).size
    let oldContentSize = self.intrinsicContentSize()
    let heightDelta = trueContentSize.height - oldContentSize.height
    let widthDelta = trueContentSize.width - oldContentSize.width
    self.contentEdgeInsets = UIEdgeInsets(top:heightDelta/2.0,
                                          left:widthDelta/2.0,
                                          bottom:heightDelta/2.0,
                                          right:widthDelta/2.0)
  }
}

Điều này xác định một chức năng centerLabelVerticallyWithPaddingđặt tiêu đề và hình ảnh chèn phù hợp.

Nó cũng đặt contentEdgeInsets, mà tôi tin là cần thiết để đảm bảo rằng nó intrinsicContentSizevẫn hoạt động chính xác, cần sử dụng Bố cục tự động.

Tôi tin rằng tất cả các giải pháp mà UIButton phân lớp là bất hợp pháp về mặt kỹ thuật, vì bạn không được yêu cầu phân lớp điều khiển UIKit. Tức là, về lý thuyết họ có thể phá vỡ trong các phiên bản tương lai.


Thử nghiệm trên iOS9. Hình ảnh xuất hiện ở giữa, nhưng văn bản xuất hiện ở bên trái :(
endavid 20/03/2016

13

Chỉnh sửa: Đã cập nhật cho Swift 3

Trong trường hợp bạn đang tìm kiếm một giải pháp Swift cho câu trả lời của Jesse Crossen, bạn có thể thêm giải pháp này vào một lớp con của UIButton:

override func layoutSubviews() {

    let spacing: CGFloat = 6.0

    // lower the text and push it left so it appears centered
    //  below the image
    var titleEdgeInsets = UIEdgeInsets.zero
    if let image = self.imageView?.image {
        titleEdgeInsets.left = -image.size.width
        titleEdgeInsets.bottom = -(image.size.height + spacing)
    }
    self.titleEdgeInsets = titleEdgeInsets

    // raise the image and push it right so it appears centered
    //  above the text
    var imageEdgeInsets = UIEdgeInsets.zero
    if let text = self.titleLabel?.text, let font = self.titleLabel?.font {
        let attributes = [NSFontAttributeName: font]
        let titleSize = text.size(attributes: attributes)
        imageEdgeInsets.top = -(titleSize.height + spacing)
        imageEdgeInsets.right = -titleSize.width
    }
    self.imageEdgeInsets = imageEdgeInsets

    super.layoutSubviews()
}

9

Có một số ví dụ tuyệt vời ở đây, nhưng tôi không thể làm cho nó hoạt động trong mọi trường hợp khi cũng xử lý nhiều dòng văn bản (gói văn bản). Để cuối cùng làm cho nó hoạt động tôi đã kết hợp một vài kỹ thuật:

  1. Tôi đã sử dụng ví dụ Jesse Crossen ở trên. Tuy nhiên, tôi đã khắc phục sự cố chiều cao văn bản và tôi đã thêm khả năng chỉ định lề văn bản ngang. Lề rất hữu ích khi cho phép văn bản được bọc để nó không chạm vào cạnh của nút:

    // the space between the image and text
    CGFloat spacing = 10.0;
    float   textMargin = 6;
    
    // get the size of the elements here for readability
    CGSize  imageSize   = picImage.size;
    CGSize  titleSize   = button.titleLabel.frame.size;
    CGFloat totalHeight = (imageSize.height + titleSize.height + spacing);      // get the height they will take up as a unit
    
    // lower the text and push it left to center it
    button.titleEdgeInsets = UIEdgeInsetsMake( 0.0, -imageSize.width +textMargin, - (totalHeight - titleSize.height), +textMargin );   // top, left, bottom, right
    
    // the text width might have changed (in case it was shortened before due to 
    // lack of space and isn't anymore now), so we get the frame size again
    titleSize = button.titleLabel.bounds.size;
    
    button.imageEdgeInsets = UIEdgeInsetsMake(-(titleSize.height + spacing), 0.0, 0.0, -titleSize.width );     // top, left, bottom, right        
  2. Hãy chắc chắn rằng bạn thiết lập nhãn văn bản để bọc

    button.titleLabel.numberOfLines = 2; 
    button.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
    button.titleLabel.textAlignment = UITextAlignmentCenter;
  3. Điều này chủ yếu sẽ làm việc bây giờ. Tuy nhiên, tôi có một số nút không hiển thị chính xác hình ảnh của họ. Hình ảnh được chuyển sang bên phải hoặc bên trái (nó không nằm ở giữa). Vì vậy, tôi đã sử dụng kỹ thuật ghi đè bố cục UIButton để buộc imageView được căn giữa.

    @interface CategoryButton : UIButton
    @end
    
    @implementation CategoryButton
    
    - (void)layoutSubviews
    {
        // Allow default layout, then center imageView
        [super layoutSubviews];
    
        UIImageView *imageView = [self imageView];
        CGRect imageFrame = imageView.frame;
        imageFrame.origin.x = (int)((self.frame.size.width - imageFrame.size.width)/ 2);
        imageView.frame = imageFrame;
    }
    @end

Có vẻ như đây là một giải pháp tốt, tuy nhiên không nên nhấn nút.titleLabel.numberOfLines là 0 sao cho nó có thể có nhiều dòng như nó muốn?
Ben Lachman

Trong trường hợp của tôi, tôi chỉ muốn tối đa hai dòng. Nếu không, hình ảnh sẽ có vấn đề với kích thước tổng thể của nút.
Tod Cickyham

9

Tôi đã thực hiện một phương pháp cho câu trả lời của @ TodCastyham

 -(void) AlignTextAndImageOfButton:(UIButton *)button
 {
   CGFloat spacing = 2; // the amount of spacing to appear between image and title
   button.imageView.backgroundColor=[UIColor clearColor];
   button.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
   button.titleLabel.textAlignment = UITextAlignmentCenter;
   // get the size of the elements here for readability
   CGSize imageSize = button.imageView.frame.size;
   CGSize titleSize = button.titleLabel.frame.size;

  // lower the text and push it left to center it
  button.titleEdgeInsets = UIEdgeInsetsMake(0.0, - imageSize.width, - (imageSize.height   + spacing), 0.0);

  // the text width might have changed (in case it was shortened before due to 
  // lack of space and isn't anymore now), so we get the frame size again
   titleSize = button.titleLabel.frame.size;

  // raise the image and push it right to center it
  button.imageEdgeInsets = UIEdgeInsetsMake(- (titleSize.height + spacing), 0.0, 0.0, -     titleSize.width);
 }

7

Đã cập nhật cho Xcode 11+

Các phần tử được mô tả trong câu trả lời ban đầu của tôi đã được chuyển đến trình kiểm tra kích thước trong các phiên bản Xcode gần đây hơn. Tôi không rõ ràng 100% khi chuyển đổi xảy ra nhưng độc giả nên xem lại trình kiểm tra kích thước nếu không tìm thấy thông tin nội dung trong trình kiểm tra thuộc tính. Dưới đây là một mẫu của màn hình chèn mới (nằm ở đầu thanh tra thuộc tính kích thước tính đến 11,5).

insets_mond_to_size_inspector

Câu trả lời gốc

Không có gì sai với các câu trả lời khác, tuy nhiên tôi chỉ muốn lưu ý rằng hành vi tương tự có thể được thực hiện một cách trực quan trong Xcode bằng cách sử dụng các dòng mã bằng không. Giải pháp này rất hữu ích nếu bạn không cần một giá trị được tính toán hoặc đang xây dựng với bảng phân cảnh / xib (nếu không thì các giải pháp khác sẽ được áp dụng).

Lưu ý - Tôi hiểu rằng câu hỏi của OP là một mã yêu cầu. Tôi chỉ cung cấp câu trả lời này cho đầy đủ và như là một thay thế hợp lý cho những người sử dụng bảng phân cảnh / xibs.

Để sửa đổi khoảng cách trên hình ảnh, tiêu đề và chế độ xem nội dung của một nút bằng cách sử dụng các cạnh trong, bạn có thể chọn nút / điều khiển và mở trình kiểm tra thuộc tính. Cuộn xuống giữa của thanh tra và tìm phần cho các phần tử cạnh.

cạnh trong

Người ta cũng có thể truy cập và sửa đổi các phần chèn cạnh cụ thể cho chế độ xem tiêu đề, hình ảnh hoặc nội dung.

Tùy chọn menu


Điều tôi không hiểu là tại sao tôi dường như không thể nhập số âm trong bảng phân cảnh cho một số giá trị.
Daniel T.

Nó có hoạt động cho phiên bản swift mới hơn không? Tôi không thể tìm thấy thuộc tính Edge ở bất cứ đâu
brockhampton

@brockhampton - xem câu trả lời cập nhật cho vị trí mới
Tommie C.

6

Đừng chống lại hệ thống. Nếu bố cục của bạn trở nên quá phức tạp để quản lý bằng Trình tạo giao diện + có thể là một số mã cấu hình đơn giản, hãy thực hiện các bố cục theo cách thủ công theo cách đơn giản hơn layoutSubviews- đó là những gì dành cho! Mọi thứ khác sẽ lên tới hack.

Tạo một lớp con UIButton và ghi đè layoutSubviewsphương thức của nó để căn chỉnh văn bản và hình ảnh của bạn theo chương trình. Hoặc sử dụng một cái gì đó như https://github.com/nickpaulson/BlockKit/blob/master/Source/UIView-BKAdditions.h để bạn có thể triển khai layoutSubview bằng cách sử dụng một khối.


6

UIButton phân lớp

- (void)layoutSubviews {
    [super layoutSubviews];
    CGFloat spacing = 6.0;
    CGSize imageSize = self.imageView.image.size;
    CGSize titleSize = [self.titleLabel sizeThatFits:CGSizeMake(self.frame.size.width, self.frame.size.height - (imageSize.height + spacing))];
    self.imageView.frame = CGRectMake((self.frame.size.width - imageSize.width)/2, (self.frame.size.height - (imageSize.height+spacing+titleSize.height))/2, imageSize.width, imageSize.height);
    self.titleLabel.frame = CGRectMake((self.frame.size.width - titleSize.width)/2, CGRectGetMaxY(self.imageView.frame)+spacing, titleSize.width, titleSize.height);
}

6

Câu trả lời cập nhật của Jesse Crossen cho Swift 4 :

extension UIButton {
    func alignVertical(spacing: CGFloat = 6.0) {
        guard let imageSize = self.imageView?.image?.size,
            let text = self.titleLabel?.text,
            let font = self.titleLabel?.font
            else { return }
        self.titleEdgeInsets = UIEdgeInsets(top: 0.0, left: -imageSize.width, bottom: -(imageSize.height + spacing), right: 0.0)
        let labelString = NSString(string: text)
        let titleSize = labelString.size(withAttributes: [kCTFontAttributeName as NSAttributedStringKey: font])
        self.imageEdgeInsets = UIEdgeInsets(top: -(titleSize.height + spacing), left: 0.0, bottom: 0.0, right: -titleSize.width)
        let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0;
        self.contentEdgeInsets = UIEdgeInsets(top: edgeOffset, left: 0.0, bottom: edgeOffset, right: 0.0)
    }
}

Sử dụng theo cách này:

override func viewDidLayoutSubviews() {
    button.alignVertical()
}

Sau nhiều, nhiều giờ. Đây là điều duy nhất có hiệu quả :)
Harry Blue

4

Với đoạn mã này, bạn sẽ nhận được một cái gì đó như thế này căn chỉnh tiêu đề và hình ảnh

extension UIButton {
    func alignTextUnderImage() {
        guard let imageView = imageView else {
                return
        }
        self.contentVerticalAlignment = .Top
        self.contentHorizontalAlignment = .Center
        let imageLeftOffset = (CGRectGetWidth(self.bounds) - CGRectGetWidth(imageView.bounds)) / 2//put image in center
        let titleTopOffset = CGRectGetHeight(imageView.bounds) + 5
        self.imageEdgeInsets = UIEdgeInsetsMake(0, imageLeftOffset, 0, 0)
        self.titleEdgeInsets = UIEdgeInsetsMake(titleTopOffset, -CGRectGetWidth(imageView.bounds), 0, 0)
    }
}

2
Tôi đã viết phần mở rộng nhỏ để định vị hình ảnh và văn bản bên trong nút. Nếu bạn quan tâm, đây là mã nguồn. github.com/sssbohdan/ButtonAlocateExtension/blob/master/
triệt

4

Tiện ích mở rộng UIButton với cú pháp Swift 3+ :

extension UIButton {
    func alignImageAndTitleVertically(padding: CGFloat = 6.0) {
        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.size(attributes: [NSFontAttributeName: titleLabel!.font])
        self.imageEdgeInsets = UIEdgeInsetsMake(-(titleSize.height + padding), 0.0, 0.0, -titleSize.width)
        let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0;
        self.contentEdgeInsets = UIEdgeInsetsMake(edgeOffset, 0.0, edgeOffset, 0.0)
    }
}

Câu trả lời gốc: https://stackoverflow.com/a/7199529/3659227


3

Chỉ cần một thay đổi nhỏ đối với câu trả lời của Jesse Crossen đã khiến nó hoạt động hoàn hảo với tôi:

thay vì:

CGSize titleSize = button.titleLabel.frame.size;

Tôi đã sử dụng điều này:

CGSize titleSize = [button.titleLabel.text sizeWithAttributes: @{NSFontAttributeName:button.titleLabel.font}];

Chào mừng đến với SO! Thay vì thêm một câu trả lời riêng (cho một thay đổi nhỏ), bạn có thể viết câu này cho Jesse để anh ta có thể kiểm tra và cập nhật chính xác câu trả lời [được chấp nhận] của mình (nếu cần).
Hemang

3

Sử dụng chỉ button.titleLabel.frame.size.widthhoạt động tốt miễn là nhãn đủ ngắn để không bị cắt ngắn. Khi văn bản nhãn bị cắt ngắn định vị không hoạt động mặc dù. Đang lấy

CGSize titleSize = [[[button titleLabel] text] sizeWithFont:[[button titleLabel] font]];

làm việc cho tôi ngay cả khi văn bản nhãn bị cắt ngắn.


bạn có lỗi đánh máy.
Bhimbim

2

Tôi đã xem xét các câu trả lời hiện có nhưng tôi cũng thấy rằng việc đặt khung nút là bước đầu tiên quan trọng.

Đây là một chức năng mà tôi sử dụng quan tâm đến điều này:

const CGFloat kImageTopOffset   = -15;
const CGFloat kTextBottomOffset = -25;

+ (void) centerButtonImageTopAndTextBottom: (UIButton*)         button 
                                     frame: (CGRect)            buttonFrame
                                      text: (NSString*)         textString
                                 textColor: (UIColor*)          textColor
                                      font: (UIFont*)           textFont
                                     image: (UIImage*)          image
                                  forState: (UIControlState)    buttonState
{
    button.frame = buttonFrame;

    [button setTitleColor: (UIColor*)       textColor
                 forState: (UIControlState) buttonState];

    [button setTitle: (NSString*) textString
            forState: (UIControlState) buttonState ];


    [button.titleLabel setFont: (UIFont*) textFont ];

    [button setTitleEdgeInsets: UIEdgeInsetsMake( 0.0, -image.size.width, kTextBottomOffset,  0.0)]; 

    [button setImage: (UIImage*)       image 
            forState: (UIControlState) buttonState ];

    [button setImageEdgeInsets: UIEdgeInsetsMake( kImageTopOffset, 0.0, 0.0,- button.titleLabel.bounds.size.width)];
}

2

Hoặc bạn chỉ có thể sử dụng danh mục này:

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

}


- (void)centerVertically
{  
    const CGFloat kDefaultPadding = 6.0f;

    [self centerVerticallyWithPadding:kDefaultPadding];  
}  


@end

1
Điều này sẽ không làm việc với phông chữ tùy chỉnh. Từ iOS7 +,CGSize titleSize = [button.titleLabel.text sizeWithAttributes: @{NSFontAttributeName: button.titleLabel.font}];
Hemang

1

Trường hợp sử dụng của tôi làm cho insets không thể quản lý:

  1. hình nền trên nút vẫn nhất quán
  2. văn bản và hình ảnh động thay đổi trong đó độ dài chuỗi và kích thước hình ảnh khác nhau

Đây là những gì tôi đã làm và tôi khá hài lòng với nó:

  • Tạo nút trên bảng phân cảnh với hình ảnh nền (vòng tròn có mờ và màu).

  • Khai báo một UIImageView trong lớp của tôi:

    @implementation BlahViewController {
        UIImageView *_imageView;
    }
  • Tạo ví dụ xem hình ảnh trên init:

    -(id)initWithCoder:(NSCoder *)aDecoder {
        self = [super initWithCoder:aDecoder];
        if (self) {
            _imageView = [[UIImageView alloc] initWithCoder:aDecoder];
         }
         return self;
     }
  • Trong viewDidLoad, thêm một lớp mới vào nút để xem hình ảnh của chúng tôi và đặt căn chỉnh văn bản:

    [self.btn addSubview:_imageView];
    [self.btn.titleLabel setTextAlignment:NSTextAlignmentCenter];
  • Trong phương pháp nhấp vào nút, thêm hình ảnh lớp phủ đã chọn của tôi vào chế độ xem hình ảnh, kích thước nó để phù hợp với hình ảnh và căn giữa nó trong nút nhưng di chuyển nó lên 15 để tôi có thể đặt văn bản bù vào bên dưới nó:

    [_imageView setImage:[UIImage imageNamed:@"blahImageBlah]];
    [_imageView sizeToFit];
    _imageView.center = CGPointMake(ceilf(self.btn.bounds.size.width / 2.0f),
             ceilf((self.btn.bounds.size.height / 2.0f) - 15));
    [self.btn setTitle:@"Some new text" forState:UIControlStateNormal];

Lưu ý: ceilf () rất quan trọng để đảm bảo nó nằm trên ranh giới pixel cho chất lượng hình ảnh.


1
Chắc chắn là một cách tiếp cận tốt hơn cho trường hợp sử dụng của tôi vì tôi đang thêm nút vào chế độ xem ngăn xếp.
valeCocoa

0

Giả sử rằng bạn muốn cả văn bản và hình ảnh được căn giữa theo chiều ngang, hình ảnh phía trên văn bản: Căn giữa văn bản từ trình tạo giao diện và thêm một hình nhỏ trên cùng (nhường chỗ cho hình ảnh). (để lại hình nhỏ bên trái về 0). Sử dụng trình tạo giao diện để chọn hình ảnh - vị trí thực tế của nó sẽ được đặt từ mã, vì vậy đừng lo lắng rằng mọi thứ sẽ không ổn trong IB. Không giống như các câu trả lời khác ở trên, điều này thực sự hoạt động trên tất cả các phiên bản ios hiện được hỗ trợ (5,6 và 7).

Trong mã, chỉ cần loại bỏ ImageView của nút (bằng cách đặt hình ảnh của nút thành null) sau khi lấy hình ảnh (điều này cũng sẽ tự động căn giữa văn bản, được bọc nếu cần thiết). Sau đó khởi tạo ImageView của riêng bạn với cùng kích thước khung hình và hình ảnh và đặt nó ở giữa.

Bằng cách này, bạn vẫn có thể chọn hình ảnh từ trình tạo giao diện (mặc dù nó sẽ không được căn chỉnh trong IB như trong trình giả lập, nhưng một lần nữa, các giải pháp khác không tương thích trên tất cả các phiên bản ios được hỗ trợ)


0

Tôi đã vật lộn để thực hiện điều này bởi vì tôi không thể có được kích thước hình ảnh và độ rộng văn bản trên hàm tạo của khung nhìn. Hai thay đổi nhỏ trong câu trả lời của Jesse có tác dụng với tôi:

CGFloat spacing = 3;
self.titleEdgeInsets = UIEdgeInsetsMake(0.0, - image.size.width, - (image.size.height + spacing), 0.0);
CGSize titleSize = [name sizeWithAttributes:@{NSFontAttributeName:self.titleLabel.font}];
self.imageEdgeInsets = UIEdgeInsetsMake(- (titleSize.height + spacing), 0.0, 0.0, - titleSize.width);

Sự thay đổi là:

  • Sử dụng [NSString sizeWithAttributes]để có được chiều rộng văn bản;
  • Nhận kích thước hình ảnh trực tiếp UIImagethay vìUIImageView

0

Điều này hoạt động tốt với tôi, đối với một số nút, với chiều rộng hình ảnh khác nhau và độ dài tiêu đề khác nhau:

Phân lớp UIButton

override func layoutSubviews() {
    super.layoutSubviews()

    if let image = imageView?.image {

        let margin = 30 - image.size.width / 2
        let titleRect = titleRectForContentRect(bounds)
        let titleOffset = (bounds.width - titleRect.width - image.size.width - margin) / 2


        contentHorizontalAlignment = UIControlContentHorizontalAlignment.Left
            imageEdgeInsets = UIEdgeInsetsMake(0, margin, 0, 0)
            titleEdgeInsets = UIEdgeInsetsMake(0, (bounds.width - titleRect.width -  image.size.width - margin) / 2, 0, 0)
    }

}

0

Hoạt động tốt cho kích thước nút 80x80 pixel.

[self.leftButton setImageEdgeInsets:UIEdgeInsetsMake(0, 10.0, 20.0, 10.0)];    
[self.leftButton setTitleEdgeInsets:UIEdgeInsetsMake(60, -75.0, 0.0, 0.0)];

0

Tôi đã thực hiện một số điều chỉnh để làm cho hình ảnh được căn chỉnh ở giữa ngang:

// the space between the image and text
        let spacing = CGFloat(36.0);

        // lower the text and push it left so it appears centered
        //  below the image
        let imageSize = tutorialButton.imageView!.frame.size;
        tutorialButton.titleEdgeInsets = UIEdgeInsetsMake(
            0, -CGFloat(imageSize.width), -CGFloat(imageSize.height + spacing), 0.0);

        // raise the image and push it right so it appears centered
        //  above the text
        let titleSize = tutorialButton.titleLabel!.frame.size;
        tutorialButton.imageEdgeInsets = UIEdgeInsetsMake(
            -CGFloat(titleSize.height + spacing), CGFloat((tutorialButton.frame.width - imageSize.width) / 2), 0.0, -CGFloat(titleSize.width));

0

Có bắt buộc phải sử dụng các cạnh trong không? Nếu không, bạn có thể cố gắng tôn trọng quan điểm của phụ huynh

extension UIButton 
{
    func centerImageAndTextVerticaAlignment(spacing: CGFloat) 
    {
        var titlePoint : CGPoint = convertPoint(center, fromView:superview)
        var imageViewPoint : CGPoint = convertPoint(center, fromView:superview)
        titlePoint.y += ((titleLabel?.size.height)! + spacing)/2
        imageViewPoint.y -= ((imageView?.size.height)! + spacing)/2
        titleLabel?.center = titlePoint
        imageView?.center = imageViewPoint

    }
}

Câu hỏi yêu cầu rõ ràng về việc sử dụng imageEdgeInsets và titleEdgeInsets để có thể bắt buộc
Tibrogargan 17/8/2016

0

Bạn cần di chuyển hình ảnh sang phải theo chiều rộng của văn bản. Sau đó di chuyển văn bản sang trái theo chiều rộng của hình ảnh.

UIEdgeInsets imageEdgeInsets = self.remoteCommandsButtonLights.imageEdgeInsets;
imageEdgeInsets.left = [button.titleLabel.text sizeWithAttributes:@{NSFontAttributeName:[button.titleLabel font]}].width;
imageEdgeInsets.bottom = 14.0;
button.imageEdgeInsets = imageEdgeInsets;

UIEdgeInsets titleEdgeInsets = self.remoteCommandsButtonLights.titleEdgeInsets;
titleEdgeInsets.left = -button.currentImage.size.width;
titleEdgeInsets.top = 20.0;
button.titleEdgeInsets = titleEdgeInsets;

Sau đó điều chỉnh các phần trên và dưới để điều chỉnh trục Y. Điều này có thể cũng có thể được thực hiện theo chương trình, nhưng không đổi đối với kích thước hình ảnh của bạn. Trong khi đó, các phần tử trục X sẽ cần thay đổi dựa trên kích thước của nhãn văn bản trong mỗi nút.


0

Thêm mã này trong phần mở rộng Swift 4.2

 func moveImageLeftTextCenter(imagePadding: CGFloat = 30.0){
    guard let imageViewWidth = self.imageView?.frame.width else{return}
    guard let titleLabelWidth = self.titleLabel?.intrinsicContentSize.width else{return}
    self.contentHorizontalAlignment = .left
    imageEdgeInsets = UIEdgeInsets(top: 0.0, left: imagePadding - imageViewWidth / 2, bottom: 0.0, right: 0.0)
    titleEdgeInsets = UIEdgeInsets(top: 0.0, left: (bounds.width - titleLabelWidth) / 2 - imageViewWidth, bottom: 0.0, right: 0.0)
}
func moveImageRIghtTextCenter(imagePadding: CGFloat = 30.0){
    guard let imageViewWidth = self.imageView?.frame.width else{return}
    guard let titleLabelWidth = self.titleLabel?.intrinsicContentSize.width else{return}
    self.contentHorizontalAlignment = .right
    imageEdgeInsets = UIEdgeInsets(top: 0.0, left:0.0 , bottom: 0.0, right: imagePadding - imageViewWidth / 2)
    titleEdgeInsets = UIEdgeInsets(top: 0.0, left:0.0 , bottom: 0.0, right:(bounds.width - titleLabelWidth) / 2 - imageViewWidth)
}

0

Chỉ cần ném 2 xu của tôi vào, điều này làm việc cho tôi:

extension UIButton {
  public func centerImageAndTextVertically(spacing: CGFloat) {
    layoutIfNeeded()
    let contentFrame = contentRect(forBounds: bounds)
    let imageFrame = imageRect(forContentRect: contentFrame)
    let imageLeftInset = bounds.size.width * 0.5 - imageFrame.size.width * 0.5
    let imageTopInset = -(imageFrame.size.height + spacing * 0.5)
    let titleFrame = titleRect(forContentRect: contentFrame)
    let titleLeftInset = ((bounds.size.width - titleFrame.size.width) * 0.5) - imageFrame.size.width
    let titleTopInmset = titleFrame.size.height + spacing * 0.5
    imageEdgeInsets = UIEdgeInsets(top: imageTopInset, left: imageLeftInset, bottom: 0, right: 0)
    titleEdgeInsets = UIEdgeInsets(top: titleTopInmset, left: titleLeftInset, 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.