iPhone UIButton - vị trí hình ảnh


116

Tôi có một dòng UIButtonchữ "Khám phá ứng dụng" và UIImage(>) Trong Interface Builderđó có vẻ như:

[ (>) Explore the app ]

Nhưng tôi cần đặt cái này UIImageSAU văn bản:

[ Explore the app (>) ]

Làm thế nào tôi có thể di chuyển UIImagesang phải?


Bạn cũng có thể sử dụng Trình tạo giao diện. Kiểm tra câu trả lời của tôi ở đây: stackoverflow.com/questions/2765024/ từ
n8tr

1
Chỉ cần sử dụng lớp con này với một vài hàng mã: github.com/k06a/RTLButton
k06a

Câu trả lời:


161

Giải pháp của tôi cho việc này khá đơn giản

[button sizeToFit];
button.titleEdgeInsets = UIEdgeInsetsMake(0, -button.imageView.frame.size.width, 0, button.imageView.frame.size.width);
button.imageEdgeInsets = UIEdgeInsetsMake(0, button.titleLabel.frame.size.width, 0, -button.titleLabel.frame.size.width);

4
Điều này làm việc tuyệt vời! Thật khó để hiểu khái niệm chèn cạnh. Bất cứ ý tưởng tại sao chúng ta cần phải đặt cả hai cạnh bên trái và bên phải? Về mặt lý thuyết, nếu tôi di chuyển tiêu đề sang trái và hình ảnh sang phải, thế là đủ. Tại sao tôi cần đặt cả trái và phải?
PokerIncome.com

3
GIẢI PHÁP TỐT NHẤT TÔI
ĐÃ

2
Câu trả lời này hoạt động hoàn hảo. Câu trả lời được chấp nhận là chính xác và trỏ đến các tài liệu API chính xác, nhưng đây là giải pháp sao chép và dán để thực hiện những gì OP yêu cầu.
mclaughlinj

Trở nên hơi phức tạp khi bạn bắt đầu thay đổi văn bản nút. Giải pháp phân lớp đã làm việc tốt hơn cho tôi trong trường hợp này.
Brad Goss

Cảm ơn bạn đã cho tôi thấy bạn phải áp dụng chiều rộng bằng nhau cho bên trái và bên phải, tôi không hiểu 100% cách thức hoạt động của nó (vì đôi khi nó sẽ ảnh hưởng đến kích thước cũng như nguồn gốc) nhưng tôi đã có thể giải quyết các vấn đề tương tự sử dụng phương pháp này.
Toby

128

Trên iOS 9 trở đi, dường như một cách đơn giản để đạt được điều này là buộc ngữ nghĩa của chế độ xem.

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

Hoặc lập trình, sử dụng:

button.semanticContentAttribute = .ForceRightToLeft

4
Nhiều như tôi thích câu trả lời của bạn (+1), tôi ghét phải nói rằng nó có thể không phải là cách "phù hợp" để làm điều này, nhưng đó là một trong những cách dễ nhất.
farzadshbfn

@farzadshbfn bạn nói đúng. Tôi đã thay đổi từ đó cho "đơn giản", có vẻ phù hợp hơn.
Alvivi

@farzadshbfn Tại sao điều này không phải là cách "thích hợp" để làm?
Samman Bikram Thapa

3
@SammanBikramThapa IMHO cách thích hợp sẽ là phân lớp UIButton và ghi đè semanticContentAttributebố cục Xem trước và tôn trọng logic logic bố cục của chúng tôi, thay vì semanticContentAttributetự thay đổi . (thay đổi cách tiếp cận ngữ nghĩa, sẽ không hoạt động tốt với quốc tế hóa)
farzadshbfn

@farzadshbfn hoàn toàn đúng. Bất kể câu trả lời này ghi được hầu hết các điểm đều không đúng - Nó có thể phá vỡ UX cho giao diện từ phải sang trái.
Pavel Yakimenko

86

Đặt imageEdgeInsettitleEdgeInsetđể di chuyển các thành phần xung quanh trong hình ảnh của bạn. Bạn cũng có thể tạo một nút bằng các đồ họa có kích thước đầy đủ và sử dụng nút đó làm hình nền cho nút (sau đó sử dụng titleEdgeInsetsđể di chuyển tiêu đề xung quanh).


8
Nó chỉ đơn giản là ít mã hơn để thiết lập các phần tử hơn là thực hiện một lớp con. Đây là toàn bộ điểm của các miếng lót. Thao tác các khung cho chế độ xem phụ (mà bạn chưa tạo) cảm thấy giống như một vụ hack.
Kim André Sand

6
@kimsnarf, thật sao? Sẽ ít tốn công hơn (và ít bị hack hơn) để điều chỉnh các phần tử bất cứ khi nào bạn thực hiện một thay đổi nhỏ về kích thước của hình ảnh hoặc độ dài của tiêu đề?
Kirk Woll

54

Câu trả lời của Raymond W là tốt nhất ở đây. Phân lớp UIButton với bố cục tùy chỉnhSubview. Rất đơn giản để làm, đây là một triển khai layoutSubview phù hợp với tôi:

- (void)layoutSubviews
{
    // Allow default layout, then adjust image and label positions
    [super layoutSubviews];

    UIImageView *imageView = [self imageView];
    UILabel *label = [self titleLabel];

    CGRect imageFrame = imageView.frame;
    CGRect labelFrame = label.frame;

    labelFrame.origin.x = imageFrame.origin.x;
    imageFrame.origin.x = labelFrame.origin.x + CGRectGetWidth(labelFrame);

    imageView.frame = imageFrame;
    label.frame = labelFrame;
}

2
Cách này tốt hơn trong trường hợp bạn cần quản lý nhiều nút, nhưng tôi chỉ cần thay đổi một nút :)
Pavel Yakimenko

2
Nếu hình ảnh nút không có kết quả nhãn bị đặt sai vị trí, có thể do UIImageView không được chèn (Đã thử nghiệm trên iOS6.0). Bạn chỉ nên xem xét chỉnh sửa khung nếu imageView.image không phải là không.
Scakko

2
Tôi sẽ đề xuất cải tiến sau cho câu trả lời này để cả hai chế độ xem đều tập trung: 'CGFloat tích lũyWidth = CGRectGetWidth (imageFrame) + CGRectGetWidth (nhãnFrame) + 10; CGFloat overWidth = CGRectGetWidth (self.bound) - cumulationWidth; nhãnFrame.origin.x = overWidth / 2; imageFrame.origin.x = CGRectGetMaxX (nhãnFrame) + 10; '
i-konov

Điều này phá vỡ trên iOS 7 đối với tôi. Ai khác? Hoạt động tốt trên iOS 8.
rounak

1
Không hỗ trợ iOS7 chút nào và vấn đề của bạn sẽ biến mất. Bạn không nên supoprt nó anyway.
Gil Sand

30

Điều gì về phân lớp UIButtonvà ghi đè layoutSubviews?

Sau đó xử lý hậu kỳ các vị trí của self.imageView&self.titleLabel


4
Điều này dễ dàng hơn nhiều so với tất cả các lời khuyên khác (như lời khuyên ở trên) về việc cố gắng đặt mọi thứ chính xác bằng cách sử dụng các miếng lót điều chỉnh bằng tay.
Steven Kramer

13

Một cách đơn giản khác (không phải chỉ dành cho iOS 9) là phân lớp UIButton để ghi đè hai phương thức này

override func titleRectForContentRect(contentRect: CGRect) -> CGRect {
    var rect = super.titleRectForContentRect(contentRect)
    rect.origin.x = 0
    return rect
}

override func imageRectForContentRect(contentRect: CGRect) -> CGRect {
    var rect = super.imageRectForContentRect(contentRect)
    rect.origin.x = CGRectGetMaxX(contentRect) - CGRectGetWidth(rect)
    return rect
}

contentEdgeInsets đã được tính đến bằng cách sử dụng siêu.


Cảm ơn! Tôi thích điều này hơn rất nhiều so với các câu trả lời phân lớp và ghi đè lên layoutSubview () :)
Joe Susnick

1
Cách tốt nhất để giải quyết vấn đề này theo quan điểm khiêm tốn của tôi :)
DrPatience

9

Buộc 'từ phải sang trái' cho nút không phải là một tùy chọn nếu ứng dụng của bạn hỗ trợ cả 'từ trái sang phải' và 'từ phải sang trái'.

Giải pháp hiệu quả với tôi là một lớp con có thể được thêm vào nút trong Storyboard và hoạt động tốt với các ràng buộc (được thử nghiệm trong iOS 11):

class ButtonWithImageAtEnd: UIButton {

    override func layoutSubviews() {
        super.layoutSubviews()

        if let imageView = imageView, let titleLabel = titleLabel {
            let padding: CGFloat = 15
            imageEdgeInsets = UIEdgeInsets(top: 5, left: titleLabel.frame.size.width+padding, bottom: 5, right: -titleLabel.frame.size.width-padding)
            titleEdgeInsets = UIEdgeInsets(top: 0, left: -imageView.frame.width, bottom: 0, right: imageView.frame.width)
        }

    }

}

Trong đó 'phần đệm' sẽ là khoảng trắng giữa tiêu đề và hình ảnh.


Tất nhiên .forceRightToLeftlà một lựa chọn! Chỉ cần sử dụng giá trị ngược lại ( .forceLeftToRight) nếu UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft.
manody

5

Trong Swift:

override func layoutSubviews(){
    super.layoutSubviews()

    let inset: CGFloat = 5

    if var imageFrame = self.imageView?.frame,
       var labelFrame = self.titleLabel?.frame {

       let cumulativeWidth = imageFrame.width + labelFrame.width + inset
       let excessiveWidth = self.bounds.width - cumulativeWidth
       labelFrame.origin.x = excessiveWidth / 2
       imageFrame.origin.x = labelFrame.origin.x + labelFrame.width + inset

       self.imageView?.frame = imageFrame
       self.titleLabel?.frame = labelFrame  
    }
}

4

Xây dựng câu trả lời bằng @split ...

Câu trả lời là tuyệt vời, nhưng nó bỏ qua thực tế là nút có thể có các hình ảnh cạnh tiêu đề và cạnh tiêu đề được đặt trước (ví dụ như trong bảng phân cảnh).

Ví dụ, bạn có thể muốn hình ảnh có một số phần đệm từ trên cùng và dưới cùng của hộp chứa, nhưng vẫn di chuyển hình ảnh sang bên phải của nút.

Tôi mở rộng khái niệm với phương pháp này: -

- (void) moveImageToRightSide {
    [self sizeToFit];

    CGFloat titleWidth = self.titleLabel.frame.size.width;
    CGFloat imageWidth = self.imageView.frame.size.width;
    CGFloat gapWidth = self.frame.size.width - titleWidth - imageWidth;
    self.titleEdgeInsets = UIEdgeInsetsMake(self.titleEdgeInsets.top,
                                            -imageWidth + self.titleEdgeInsets.left,
                                            self.titleEdgeInsets.bottom,
                                            imageWidth - self.titleEdgeInsets.right);

    self.imageEdgeInsets = UIEdgeInsetsMake(self.imageEdgeInsets.top,
                                            titleWidth + self.imageEdgeInsets.left + gapWidth,
                                            self.imageEdgeInsets.bottom,
                                            -titleWidth + self.imageEdgeInsets.right - gapWidth);
}

2
// Get the size of the text and image
CGSize buttonLabelSize = [[self.button titleForState:UIControlStateNormal] sizeWithFont:self.button.titleLabel.font];
CGSize buttonImageSize = [[self.button imageForState:UIControlStateNormal] size];

// You can do this line in the xib too:
self.button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;

// Adjust Edge Insets according to the above measurement. The +2 adds a little space 
self.button.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, -(buttonLabelSize.width+2));
self.button.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, buttonImageSize.width+2);

Điều này tạo ra một nút căn phải, như vậy:

[           button label (>)]

Nút không điều chỉnh độ rộng của nó theo ngữ cảnh, do đó, không gian sẽ xuất hiện ở bên trái của nhãn. Bạn có thể giải quyết điều này bằng cách tính chiều rộng khung của nút từ nútLabelSize. Thong và buttonImageSize. Thong.


1
button.semanticContentAttribute = UISemanticContentAttributeForceRightToLeft;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;

0

Giải pháp này hoạt động iOS 7 trở lên

Chỉ cần phân lớp UIButton

@interface UIButton (Image)

- (void)swapTextWithImage;

@end

@implementation UIButton (Image)

- (void)swapTextWithImage {
   const CGFloat kDefaultPadding = 6.0f;
   CGSize buttonSize = [self.titleLabel.text sizeWithAttributes:@{
                                                               NSFontAttributeName:self.titleLabel.font
                                                               }];

   self.titleEdgeInsets = UIEdgeInsetsMake(0, -self.imageView.frame.size.width, 0, self.imageView.frame.size.width);
   self.imageEdgeInsets = UIEdgeInsetsMake(0, buttonSize.width + kDefaultPadding, 0, -buttonSize.width); 
}

@end

Cách sử dụng (Một nơi nào đó trong lớp học của bạn):

[self.myButton setTitle:@"Any text" forState:UIControlStateNormal];
[self.myButton swapTextWithImage];

0

Dựa trên các câu trả lời trước. Nếu bạn muốn có một lề giữa biểu tượng và tiêu đề của nút, mã phải thay đổi một chút để tránh nổi nhãn và biểu tượng phía trên giới hạn của các nút có kích thước nội tại.

let margin = CGFloat(4.0)
button.titleEdgeInsets = UIEdgeInsetsMake(0, -button.imageView.frame.size.width, 0, button.imageView.frame.size.width);
button.imageEdgeInsets = UIEdgeInsetsMake(0, button.titleLabel.frame.size.width, 0, -button.titleLabel.frame.size.width)
button.contentEdgeInsets = UIEdgeInsetsMake(0, margin, 0, margin)

Dòng mã cuối cùng rất quan trọng đối với việc tính toán kích thước nội dung cho bố cục tự động.


0

Đây là cách riêng của tôi để làm điều đó, (sau khoảng 10 năm)

  1. Phân lớp từ UIButton (Nút, khi chúng ta đang sống trong thời đại Swift)
  2. Đặt một hình ảnh và nhãn trong chế độ xem ngăn xếp.
class CustomButton: Button {

    var didLayout: Bool = false // The code must be called only once

    override func layoutSubviews() {
        super.layoutSubviews()
        if !didLayout, let imageView = imageView, let titleLabel = titleLabel {
            didLayout = true
            let stack = UIStackView(arrangedSubviews: [titleLabel, imageView])
            addSubview(stack)
            stack.edgesToSuperview() // I use TinyConstraints library. You could handle the constraints directly
            stack.axis = .horizontal
        }
    }
}

0

Giải pháp một dòng trong Swift:

// iOS 9 and Onwards
button.semanticContentAttribute = .forceRightToLeft

Nó không tốt vì nó sẽ phá vỡ bố cục cho tất cả người dùng RTL. Bạn sẽ cần buộc trái sang phải cho họ. Bạn nên thử giải pháp khác từ đây.
Pavel Yakimenko
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.