Tự động thanh toán - kích thước nội tại của UIButton không bao gồm các phần tử tiêu đề


196

Nếu tôi có một UIButton được sắp xếp bằng cách sử dụng tự động thanh toán, kích thước của nó sẽ điều chỉnh độc đáo để phù hợp với nội dung của nó.

Nếu tôi đặt một hình ảnh là button.image, kích thước đầu ra một lần nữa dường như giải thích cho điều này.

Tuy nhiên, nếu tôi điều chỉnh titleEdgeInsetsnút, bố cục không tính đến điều này và thay vào đó cắt ngắn tiêu đề nút.

Làm thế nào tôi có thể đảm bảo rằng chiều rộng nội tại của nút chiếm tài khoản cho phần trong?

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

Biên tập:

Tôi đang sử dụng như sau:

[self.backButton setTitleEdgeInsets:UIEdgeInsetsMake(0, 5, 0, 0)];

Mục tiêu là để thêm một số tách biệt giữa hình ảnh và văn bản.


3
Bạn đã nộp nó như là một radar? Nó chắc chắn dường như là một lỗi trong tính toán kích thước nội tại của UIButton.
Ryan Poolos

1
Tôi đã sẵn sàng để nộp một radar, nhưng đây thực sự có vẻ là một hành vi dự kiến. Điều này được ghi lại trên các thuộc tính * EdgeInsets của UIButton : "Các phần tử bạn chỉ định được áp dụng cho hình chữ nhật tiêu đề sau khi hình chữ nhật đó có kích thước phù hợp với văn bản của nút. Do đó, các giá trị in dương có thể thực sự cắt đoạn văn bản tiêu đề. [...] nút không sử dụng thuộc tính này để xác định intinsicContentSize và sizeThatFits :. "
Guillaume Algis

7
@GuillaumeAlgis Tôi sẽ lập luận rằng mặc dù đây là hành vi đã nêu, nhưng đó không phải là điều mà người ta mong đợi sẽ xảy ra khi sử dụng tính năng tự động thanh toán. Tôi đã báo lỗi và cũng khuyến khích người khác nộp một lỗi.
memmons

Nếu bạn có thể liên kết với lỗi radar ở đây, chúng ta có thể nhấp vào nó và +1 trên nó không?
gprasant

1
từ titleEdgeInsettài liệu: The insets you specify are applied to the title rectangle after that rectangle has been sized to fit the button’s text. Thus, positive inset values may actually clip the title text. Vì vậy, bằng cách thêm inset, bạn đang buộc nút để cắt đoạn văn bản cho chắc chắn
Marco Pappalardo

Câu trả lời:


192

Bạn có thể giải quyết vấn đề này mà không phải ghi đè bất kỳ phương thức nào hoặc đặt ràng buộc độ rộng tùy ý. Bạn có thể làm tất cả trong Trình tạo giao diện như sau.

  • Chiều rộng nút nội tại được lấy từ chiều rộng tiêu đề cộng với chiều rộng biểu tượng cộng với các cạnh nội dung bên trái và bên phải .

  • Nếu một nút có cả hình ảnh và văn bản, chúng sẽ được đặt ở giữa thành một nhóm, không có phần đệm giữa.

  • Nếu bạn thêm một nội dung bên trái, nó sẽ được tính tương đối với văn bản, không phải biểu tượng văn bản +.

  • Nếu bạn đặt một hình ảnh bên trái âm tính, hình ảnh được kéo ra bên trái nhưng chiều rộng nút tổng thể không bị ảnh hưởng.

  • Nếu bạn đặt một hình ảnh bên trái âm tính, bố cục thực tế sử dụng một nửa giá trị đó. Vì vậy, để có được một -20 điểm bên trái, bạn phải sử dụng giá trị chèn bên trái -40 điểm trong Trình tạo giao diện.

Vì vậy, bạn cung cấp một nội dung bên trái đủ lớn để tạo khoảng trống cho cả phần bên trái mong muốn phần đệm bên trong giữa biểu tượng và văn bản, sau đó dịch chuyển biểu tượng sang trái bằng cách nhân đôi số lượng phần đệm bạn muốn giữa biểu tượng và văn bản. Kết quả là một nút có các nội dung bên trái và bên phải bằng nhau, và một cặp văn bản và biểu tượng được đặt ở giữa là một nhóm, với một số lượng đệm cụ thể giữa chúng.

Một số giá trị mẫu:

// Produces a button with the layout:
// |-20-icon-10-text-20-|
// AutoLayout intrinsic width works as you'd desire.
button.contentEdgeInsets = UIEdgeInsetsMake(10, 30, 10, 20)
button.imageEdgeInsets = UIEdgeInsetsMake(0, -20, 0, 0)

tại sao bố cục thực tế sử dụng một nửa giá trị chèn bên trái âm ?? Tôi đã gặp vấn đề tương tự!
Tony Lin

1
Thật tuyệt khi có một cách giải quyết, nhưng tôi hy vọng điều này không được sử dụng để biện minh cho hành vi kỳ lạ của UIButton.
func7

205

Bạn có thể làm cho điều này hoạt động trong Trình tạo giao diện (không cần viết bất kỳ mã nào), bằng cách sử dụng kết hợp các Tiêu đề và nội dung phủ định và tiêu cực.

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

Cập nhật : Xcode 7 có một lỗi trong đó bạn không thể nhập các giá trị âm vào trường RightInset, nhưng bạn có thể sử dụng điều khiển bước bên cạnh để giảm giá trị. (Cảm ơn Stuart)

Làm điều này sẽ thêm 8pt khoảng cách giữa hình ảnh và tiêu đề và sẽ tăng chiều rộng nội tại của nút bằng cùng một lượng. Như thế này:

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


2
Đó là sử dụng contentEdgeInsets (không phải lỗi) để cho phép tự động thanh toán để tăng chiều rộng nút. Và di chuyển nhãn đến không gian trống ở bên phải. Cách thông minh để khắc phục lỗi bên cạnh tiêu đề.
xấu xí

7
Thủ thuật này không còn hoạt động. Trình xây dựng giao diện không còn chấp nhận các giá trị âm trong Righttrường.
Joris Mans

7
@JorisMans Bạn không thể nhập các giá trị âm vào, nhưng nó hoạt động với tôi bằng cách sử dụng điều khiển bước ở bên phải của trường văn bản để chuyển xuống giá trị âm được yêu cầu ... đi!
Stuart

3
Đây phải là câu trả lời đầu tiên, tại sao nó lại ở đây? Tôi đã thử 5 cái còn lại trước khi tìm thấy cái này ...
Lord Zsolt

2
Tôi đã thực hiện Quyền nội dung trong phần 16 để căn giữa văn bản trong UIButton
coolcool1994

96

Tại sao không ghi đè intrinsicContentSizephương thức trên UIView? Ví dụ:

- (CGSize) intrinsicContentSize
{
    CGSize s = [super intrinsicContentSize];

    return CGSizeMake(s.width + self.titleEdgeInsets.left + self.titleEdgeInsets.right,
                      s.height + self.titleEdgeInsets.top + self.titleEdgeInsets.bottom);
}

Điều này sẽ cho hệ thống tự động thanh toán rằng nó sẽ tăng kích thước của nút để cho phép các phần trong và hiển thị toàn văn. Tôi không ở máy tính của mình, vì vậy tôi đã không kiểm tra điều này.


1
Các nút không nên được ghi đè theo như tôi biết. Vấn đề là mỗi loại nút được thực hiện bởi một lớp con khác nhau.
Sulthan

2
intrinsicContentSizelà một phương thức trên UIView, không phải UIButton, vì vậy bạn sẽ không bị rối trong bất kỳ phương thức UIButton nào. Apple không nghĩ đó là một vấn đề: "Ghi đè phương thức này cho phép chế độ xem tùy chỉnh giao tiếp với hệ thống bố cục với kích thước mà nó muốn dựa trên nội dung của nó." Và OP không nói gì về các nút khác nhau, chỉ một nút.
Maarten

1
Điều này chắc chắn hoạt động và là giải pháp tôi đã đi với. intrinsicContentSizethực sự là một phương thức trên UIView và UIButton là một lớp con của UIView nên tất nhiên bạn có thể ghi đè phương thức này; không có gì trong tài liệu của Apple nói rằng bạn không nên. Chỉ cần tạo một lớp con UIButton bằng phương thức ghi đè của Maarten và thay đổi UIButton của bạn trong Trình tạo giao diện thành loại YourUIButtonSub class và nó sẽ hoạt động hoàn hảo.
n8tr

37
Dường như với tôi intrinsicContentSizeđối với UIButton nên thêm vào tiêu đềEdgeInsets, tôi sẽ gửi một lỗi với Apple.
progrmr

6
Tôi đồng ý và tương tự cho imageEdgeInsets.
Ricardo Sanchez-Saez

87

Bạn chưa chỉ định cách bạn đặt các phần tử, vì vậy tôi đoán rằng bạn đang sử dụng titleEdgeInsets vì tôi thấy hiệu ứng tương tự mà bạn nhận được. Nếu tôi sử dụng contentEdgeInsets thì nó hoạt động đúng.

- (IBAction)ChangeTitle:(UIButton *)sender {
    self.button.contentEdgeInsets = UIEdgeInsetsMake(0,20,0,20);
    [self.button setTitle:@"Long Long Title" forState:UIControlStateNormal];
}

Tôi thực sự đang sử dụng titleEdgeInsets. Tôi cần khoảng cách tiêu đề từ hình ảnh, không phải hình ảnh từ cạnh của nút. Có lẽ tôi chỉ nên sử dụng một hình ảnh với một số phần đệm trong đó? Có vẻ hacky mặc dù.
Ben Packard

Điều này hoạt động hoàn hảo kết hợp với autolayout, cảm ơn bạn!
Cal S

3
Đây là giải pháp tốt hơn, vì nó thực hiện chính xác những gì bạn muốn mà không cần chạm vào intinsicContentSize.
RyJ

28
Điều này KHÔNG trả lời câu hỏi khi sử dụng hình ảnh và cần điều chỉnh phần chèn giữa hình ảnh và tiêu đề!
Brody Robertson

23

Và đối với Swift đã làm việc này:

extension UIButton {
    override open var intrinsicContentSize: CGSize {
        let intrinsicContentSize = super.intrinsicContentSize

        let adjustedWidth = intrinsicContentSize.width + titleEdgeInsets.left + titleEdgeInsets.right
        let adjustedHeight = intrinsicContentSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom

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

Yêu U Swift


1
Mặc dù bạn không nên, nhưng tốt hơn là nên phân lớp trong trường hợp này bởi vì các tài liệu của Apple tuyên bố rõ ràng rằng kích thước nội tại không bao gồm titleEdgeInsets trong tính toán của nó và do đó, bằng cách sử dụng tiện ích mở rộng, bạn vi phạm không chỉ các kỳ vọng của Apple mà tất cả các nhà phát triển khác đã đọc các tài liệu.
Còi báo

18

Chủ đề này hơi cũ, nhưng tôi chỉ tự mình chạy vào đây và có thể giải quyết nó bằng cách sử dụng một bản in âm. Ví dụ: thay thế các giá trị đệm mong muốn của bạn ở đây:

UIButton* myButton = [[UIButton alloc] init];
// setup some autolayout constraints here
myButton.titleEdgeInsets = UIEdgeInsetsMake(-desiredBottomPadding,
                                            -desiredRightPadding,
                                            -desiredTopPadding,
                                            -desiredLeftPadding);

Kết hợp với các ràng buộc tự động thanh toán đúng, bạn kết thúc với nút tự động thay đổi kích thước có chứa hình ảnh và văn bản! Nhìn bên dưới với desiredLeftPaddingthiết lập là 10.

Nút có hình ảnh và văn bản ngắn

Nút có hình ảnh và văn bản dài

Bạn có thể thấy rằng khung thực tế của nút không bao gồm nhãn (vì nhãn được dịch chuyển 10 điểm sang phải, bên ngoài giới hạn), nhưng chúng tôi đã đạt được 10 điểm đệm giữa văn bản và hình ảnh.


1
Đây là giải pháp tôi đã sử dụng vì nó không yêu cầu phân lớp. Sẽ không hoạt động nếu nút của bạn có nền, nhưng đó thường không phải là vấn đề với iOS 7
José Manuel Sánchez

Điều này sẽ hoạt động với hình ảnh nền nếu bạn cũng đặt phần bù nội dung của nút (giá trị dương> = phần tiêu đề).
Ben Flynn

9

Đối với Swift 3 dựa trên câu trả lời của pegpeg :

extension UIButton {

    override open var intrinsicContentSize: CGSize {

        let intrinsicContentSize = super.intrinsicContentSize

        let adjustedWidth = intrinsicContentSize.width + titleEdgeInsets.left + titleEdgeInsets.right
        let adjustedHeight = intrinsicContentSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom

        return CGSize(width: adjustedWidth, height: adjustedHeight)

    }

}

xin chào. tôi muốn sử dụng nút mở rộng tùy chỉnh trong interacebuilder. Xin hãy giúp đỡ
kemdo

6

Tất cả ở trên không hoạt động cho iOS 9+ , những gì tôi đã làm là:

  • Thêm một ràng buộc về chiều rộng (đối với chiều rộng tối thiểu khi nút không có bất kỳ văn bản nào. Nút sẽ tự động chia tỷ lệ nếu văn bản được cung cấp)
  • đặt mối quan hệ thành Lớn hơn hoặc bằng

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

Bây giờ để thêm một đường viền xung quanh nút, chỉ cần sử dụng phương thức:

button.contentEdgeInsets = UIEdgeInsetsMake(0,20,0,20);

Tại sao không? Nó tự động chia tỷ lệ với nội dung, bạn chỉ cần đặt chiều rộng tối thiểu (có thể nhỏ hơn văn bản được hiển thị)
Oritm

Bởi vì bạn xác định chiều rộng tối thiểu. Toàn bộ ý tưởng của autolayout được thực hiện mà không đặt bất kỳ chiều rộng rõ ràng (tối thiểu) nào.
Joris Mans

Không phải về chiều rộng, bạn có thể đặt chiều rộng thành 1 nếu bạn thích, nhưng tự động thanh toán cần phải biết rằng chiều rộng có thể bằng hoặc hơn . Tôi đã cập nhật câu trả lời của mình
Oritm 7/12/2015

Bạn hoàn toàn không cần ràng buộc về chiều rộng, contentEdgeInset là khóa, bố cục tự động sau đó sử dụng kích thước đó cho kích thước nội dung nội tại.
Chris Conover

5

Tôi muốn thêm khoảng trắng 5pt giữa biểu tượng UIButton của tôi và nhãn. Đây là cách tôi đạt được nó:

UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeCustom];
// more button config etc
infoButton.contentEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 5);
infoButton.titleEdgeInsets = UIEdgeInsetsMake(0, 5, 0, -5);

Cách contentEdgeInsets, titleEdgeInsets và imageEdgeInsets liên quan đến nhau đòi hỏi một chút cho và nhận từ mỗi phần tử. Vì vậy, nếu bạn thêm một số phần nhỏ vào bên trái của tiêu đề, bạn phải thêm phần phụ âm ở bên phải và cung cấp thêm một số khoảng trống (thông qua phần tử tích cực) ở bên phải nội dung.

Bằng cách thêm một nội dung phù hợp để phù hợp với sự thay đổi của tiêu đề, văn bản của tôi không nằm ngoài giới hạn của nút.


3

Tùy chọn này cũng có sẵn trong trình xây dựng giao diện. Xem phần khởi động. Tôi đặt trái và phải thành 3. Hoạt động như một lá bùa.

Ảnh chụp màn hình xây dựng giao diện


1
Vâng, như câu trả lời này giải thích, lý do nó hoạt động là vì bạn đang điều chỉnh Edge: Nội dung ở đây thay vì Edge: Title hoặc Edge: Image .
smileyborg

1

Giải pháp tôi sử dụng là thêm một ràng buộc về chiều rộng trên nút. Sau đó, một nơi nào đó trong khởi tạo, sau khi văn bản của bạn được đặt, hãy cập nhật ràng buộc về chiều rộng như sau:

self.buttonWidthConstraint.constant = self.shareButton.intrinsicContentSize.width + 8;

Trong đó 8 là bất cứ điều gì bạn đang đặt.


NútWidthConstraint là gì?
Alexey Golikov


1
Đây không phải là một giải pháp tuyệt vời, vì nếu kích thước nội dung bên trong của nút thay đổi, bạn cần cập nhật thủ công constantcác ràng buộc thành giá trị mới ... và biết khi nào kích thước nội dung bên trong của nút thay đổi là khó khăn mà không phân lớp nút.
smileyborg

Ayup. Tôi không sử dụng phương pháp này nữa. Ngạc nhiên là nó xứng đáng với một cuộc bỏ phiếu nhưng _ (ツ) _ /
Bob Spryn

Một cuộc gọi đến setNeedsUpdateConstraintscó thể được "thủ công" thực hiện sau khi cập nhật tiêu đề nút hoặc hình ảnh. Sau đó, bạn có thể ghi đè updateConstraintsvà tính lại buttonWidthConstrainthằng số từ đó. Đây không nhất thiết là cách tiếp cận tốt nhất nhưng nó hoạt động đủ tốt cho tôi. YMMV;)
Olivier

1

gọi sizeToFit () đảm bảo rằng contentEdgeInset có hiệu lực

extension UIButton {

   open override func draw(_ rect: CGRect) { 
       self.contentEdgeInsets = UIEdgeInsets(top: 10, left: 40, bottom: 10, right: 40)
       self.sizeToFit()
   }
}
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.