简体   繁体   English

iPhone UIButton - 图像位置

[英]iPhone UIButton - image position

I have a UIButton with text "Explore the app" and UIImage (>) In Interface Builder it looks like:我有一个带有文本“探索应用程序”和UIImage (>) 的UIButtonInterface Builder它看起来像:

[ (>) Explore the app ]

But I need to place this UIImage AFTER the text:但我需要在文本之后放置这个UIImage

[ Explore the app (>) ]

How can I move the UIImage to the right?如何将UIImage向右移动?

My solution to this is quite simple我对此的解决方案非常简单

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

On iOS 9 onwards, seems that a simple way to achieve this is to force the semantic of the view.iOS 9之后,似乎实现此目的的一种简单方法是强制视图的语义。

在此处输入图片说明

Or programmatically, using:或以编程方式,使用:

button.semanticContentAttribute = .ForceRightToLeft

Set the imageEdgeInset and titleEdgeInset to move the components around within your image.设置imageEdgeInsettitleEdgeInset以在图像内移动组件。 You could also create a button using those graphics that is full size, and use that as the background image for the button (then use titleEdgeInsets to move the title around).您还可以使用这些全尺寸图形创建一个按钮,并将其用作按钮的背景图像(然后使用titleEdgeInsets移动标题)。

Raymond W's answer is best here. Raymond W 的回答在这里是最好的。 Subclass UIButton with custom layoutSubviews.使用自定义 layoutSubviews 子类 UIButton。 Extremely simple to do, here's a layoutSubviews implementation that worked for me:做起来非常简单,这是一个对我有用的 layoutSubviews 实现:

- (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;
}

What about subclassing UIButton and overriding layoutSubviews ?子类化UIButton和覆盖layoutSubviews怎么样?

Then post-processing the locations of self.imageView & self.titleLabel然后对self.imageView & self.titleLabel的位置进行后处理

Another simple way (that is NOT iOS 9 only) is to subclass UIButton to override these two methods另一种简单的方法(不仅仅是 iOS 9)是继承 UIButton 来覆盖这两个方法

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 is already taken into account by using super. contentEdgeInsets已经通过使用 super 考虑在内。

Forcing 'right-to-left' for the button is not an option if your app supports both 'left-to-right' and 'right-to-left'.如果您的应用程序同时支持“从左到右”和“从右到左”,则不能强制按钮“从右到左”。

The solution that worked for me is a subclass that can be added to the button in the Storyboard and works well with constraints (tested in iOS 11):对我有用的解决方案是一个子类,它可以添加到 Storyboard 中的按钮并且适用于约束(在 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)
        }

    }

}

Where 'padding' would be the space between the title and the image.其中“填充”将是标题和图像之间的空间。

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

Building off the answer by @split...通过@split 构建答案...

The answer is fantastic, but it ignores the fact that the button may have custom image and title edge insets that are set beforehand (eg in storyboard).答案很棒,但它忽略了一个事实,即按钮可能具有预先设置的自定义图像和标题边缘插入(例如在故事板中)。

For instance, you may want the image have some padding from the top and bottom of the container, but still move the image to the right side of the button.例如,您可能希望图像在容器的顶部和底部有一些填充,但仍将图像移动到按钮的右侧。

I extended the concept with this method:-我用这种方法扩展了这个概念:-

- (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);
}
// 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);

This creates a right-aligned button, like so:这将创建一个右对齐按钮,如下所示:

[           button label (>)]

The button doesn't adjust it's width according to the context, so space will appear on the left of the label.该按钮不会根据上下文调整其宽度,因此标签左侧会出现空格。 You could solve this by calculating the button's frame width from the buttonLabelSize.width and the buttonImageSize.width.您可以通过从 buttonLabelSize.width 和 buttonImageSize.width 计算按钮的框架宽度来解决这个问题。

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

Building on previous answers.建立在以前的答案的基础上。 If you want to have a margin between the icon and the title of the button, the code has to change a little to prevent floating of the label and icon above the bounds of intrinsically sized buttons.如果您想在图标和按钮标题之间留出边距,代码必须稍作更改,以防止标签和图标浮动在固有大小的按钮边界之上。

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)

The last code line is important for the intrinsically content size calculation for auto layout.最后一行代码对于自动布局的内在内容大小计算很重要。

Single line solution in Swift : Swift 中的单行解决方案:

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

This solution works iOS 7 and above此解决方案适用于 iOS 7 及更高版本

Just subclass UIButton只是 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

Usage (Somewhere in your class):用法(在你班级的某个地方):

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

Here is my own way to do the thing, (after about 10 years)这是我自己做这件事的方式, (大约 10 年后)

  1. Subclass from UIButton (Button, as we're living in Swift era) UIButton 的子类(按钮,因为我们生活在 Swift 时代)
  2. Put an image and a label in a stack view.将图像和标签放在堆栈视图中。
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
        }
    }
}

I tried the solution, and works, but it centers the title + image.我尝试了解决方案,并且有效,但它以标题 + 图像为中心。 On my approach I need the text centered on navigation bar and the image on the right side.在我的方法中,我需要以导航栏为中心的文本和右侧的图像。

I implemented this Custom View:我实现了这个自定义视图:

class CenteredViewWithImage: UIView {
    
    // MARK: - Vars
    private let marginBetweenElements: CGFloat = 10.0
    private let imageViewWidth: CGFloat = 20.0

    private weak var spaceView: UIView?
    private weak var titleLabel: UILabel?
    private weak var imageView: UIImageView?
    
    var title: String? {
        willSet {
            self.titleLabel?.text = newValue
        }
    }
    
    // MARK: - LifeCycle
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.commonSetup()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        self.commonSetup()
    }
    
    // MARK: - Setup
    private func commonSetup() {
        let spaceView = UIView.init()
        self.spaceView = spaceView
        self.addSubview(spaceView)
        
        let titleLabel = UILabel.init()
        self.titleLabel = titleLabel
        self.titleLabel?.text = nil
        self.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
        self.titleLabel?.lineBreakMode = .byTruncatingTail
        self.titleLabel?.textAlignment = .center
        self.addSubview(titleLabel)
        
        let imageView = UIImageView.init()
        self.imageView = imageView
        self.imageView?.image = UIImage.init(named: "image_name")
        self.imageView?.contentMode = .scaleAspectFit
        self.addSubview(imageView)
        
        self.addConstraints()
    }
    
    // MARK: - Helper
    private func addConstraints() {
        guard let spaceView = self.spaceView,
              let titleLabel = self.titleLabel,
              let imageView = self.imageView else { return }
        
        let guide = self.safeAreaLayoutGuide
        
        self.spaceView?.translatesAutoresizingMaskIntoConstraints = false
        self.spaceView?.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true
        self.spaceView?.leadingAnchor.constraint(equalTo: guide.leadingAnchor).isActive = true
        guide.bottomAnchor.constraint(equalTo: spaceView.bottomAnchor).isActive = true
        self.spaceView?.widthAnchor.constraint(equalTo: imageView.widthAnchor).isActive = true
        
        self.titleLabel?.translatesAutoresizingMaskIntoConstraints = false
        self.titleLabel?.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true
        self.titleLabel?.leadingAnchor.constraint(equalTo: spaceView.trailingAnchor, constant: self.marginBetweenElements).isActive = true
        guide.bottomAnchor.constraint(equalTo: titleLabel.bottomAnchor).isActive = true
        
        self.imageView?.translatesAutoresizingMaskIntoConstraints = false
        self.imageView?.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true
        self.imageView?.leadingAnchor.constraint(equalTo: titleLabel.trailingAnchor, constant: self.marginBetweenElements).isActive = true
        guide.trailingAnchor.constraint(equalTo: imageView.trailingAnchor).isActive = true
        guide.bottomAnchor.constraint(equalTo: imageView.bottomAnchor).isActive = true
        self.imageView?.widthAnchor.constraint(equalToConstant: self.imageViewWidth).isActive = true
        self.imageView?.heightAnchor.constraint(equalTo: imageView.widthAnchor).isActive = true
    }
}

To use it:要使用它:

 let centeredView = CenteredViewWithImage.init()
 self.centeredView = centeredView
 self.centeredView?.title = "text centered"
 let tapGesture: UITapGestureRecognizer = UITapGestureRecognizer.init(target: self, action: #selector(self.centeredViewHasBeenPressed))
 self.centeredView?.addGestureRecognizer(tapGesture)
 self.navigationItem.titleView = self.centeredView
    @objc
    private func centeredViewHasBeenPressed() {
        debugPrint("do something")
    }

How looks:看起来如何:

文本以导航栏为中心

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM