ChatGPT解决这个技术问题 Extra ChatGPT

How do I put the image on the right side of the text in a UIButton?

I don't want to use a subview if I can avoid it. I want a UIButton with a background image, text, and an image in it. Right now, when I do that, the image is on the left side of the text. The background image, text, and image all have different highlight states.

To add another "hack" to the growing list here: you could set the attributedTitle of the button to an attributed string containing your button title + a space + the image (as an NSTextAttachment). You might need to tweak the attachment's bounds to get it to align as you want (see stackoverflow.com/questions/26105803/…).

S
Sunil Targe

Simplest solution:

iOS 10 & up, Swift:

button.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
button.titleLabel?.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
button.imageView?.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)

Before iOS 10, Swift/Obj-C:

button.transform = CGAffineTransformMakeScale(-1.0, 1.0);
button.titleLabel.transform = CGAffineTransformMakeScale(-1.0, 1.0);
button.imageView.transform = CGAffineTransformMakeScale(-1.0, 1.0);

iOS 9 & up, Swift: (Recommended)

button.semanticContentAttribute = .forceRightToLeft

I used this for the navigation bar title view and there was a glitch. It's fine when it's first loaded, but when you push a view controller and pop it, the title gets flipped.
I found if this is causing complaints about autolayout constraint conflicts at runtime it may be fixed by adding this in layoutSubviews()
How can I put more space in between the text and the image?
@rohinb @jose920405 Try setting ImageEdgeInsets and ContentEdgeInsets for padding (keeping in mind they've been reversed). For example button.ImageEdgeInsets = new UIEdgeInsets(0, -leftPadding, 0, leftPadding); button.ContentEdgeInsets = new UIEdgeInsets(0, 0, 0, leftPadding);. That's in Xamarin, but should translate to Swift/Obj-C easily enough.
the fatal flaw with setting semanticContentAttribute is that it WILL break voiceover navigation. voiceover navigation uses the semantic content attribute to determine the direction to navigate through elements and force flipping the semantic means the user will reach the button, then their VO navigation is flipped around and they go back to the element they just visited from instead of the next one.
m
manmal

Despite some of the suggested answers being very creative and extremely clever, the simplest solution is as follows:

button.semanticContentAttribute = UIApplication.shared
    .userInterfaceLayoutDirection == .rightToLeft ? .forceLeftToRight : .forceRightToLeft

As simple as that. As a bonus, the image will be at the left side in right-to-left locales.

EDIT: as the question has been asked a few times, this is iOS 9 +.


I can't believe this answer was the accepted one. Nobody does localisations for their applications?
@pallzoltan: this answers the question (ie. "How do I put the image on the right side of the text in a UIButton?"). What has localisation got to do with this?
There are not many situations when you don't want your layout to be "flipped" in RTL languages. Directly setting semanticContentAttribute is just a hack/workaround, not a real solution.
My approach is that you don't know what the person asking the question is building, so it's always better count with flexibility for the layout.
How is this the accepted answer? It's not the simplest or even remotely close to being the correct anser. Apples documentation for UIUserInterfaceLayoutDirection.rightToLeft states: "The layout direction right to left. This value is appropriate when running with localizations such as Arabic or Hebrew that should have the user interface layout origin on the right edge of the coordinate system." UIButton has an imageEdgeInsets property that can be set in code, or IB. Designated specifically for this. That is the correct way to reposition a button's image.
Z
Zaporozhchenko Oleksandr

UPDATED FOR XCODE 9 (Via Interface Builder)

There's an easier way from the Interface Builder.

Select the UIButton and select this option in the View Utilities > Semantic:

https://i.stack.imgur.com/YMacE.png

OPTIONAL - 2nd step:

If you want to adjust the spacing between the image and the title you can change the Image Inset here:

https://i.stack.imgur.com/Cqap0.png

Hope that helps!


In Xcode 9.0 beta 5 (9M202q), you unfortunately only see the result at runtime—in the storyboard it still shows the image to the left. Also note that because of this it takes some trial and error to set the correct insets.
Please don't do it this way - this breaks localization for right-to-left languages.
B
Ben Baron

Subclassing UIButton is completely unnecessary. Instead you can simply set a high left inset value for the image insets, and a small right inset for the title. Something like this:

button.imageEdgeInsets = UIEdgeInsetsMake(0., button.frame.size.width - (image.size.width + 15.), 0., 0.);
button.titleEdgeInsets = UIEdgeInsetsMake(0., 0., 0., image.size.width);

It worked, but just remember that today with autolayout you have to do it on viewDidAppear and not on viewDidLoad
C
Community

I'm giving Inspire48 the credit for this one. Based on his suggestion and looking at that other question I came up with this. Subclass UIButton and override these methods.

@implementation UIButtonSubclass

- (CGRect)imageRectForContentRect:(CGRect)contentRect
{
    CGRect frame = [super imageRectForContentRect:contentRect];
    frame.origin.x = CGRectGetMaxX(contentRect) - CGRectGetWidth(frame) -  self.imageEdgeInsets.right + self.imageEdgeInsets.left;
    return frame;
}

- (CGRect)titleRectForContentRect:(CGRect)contentRect
{
    CGRect frame = [super titleRectForContentRect:contentRect];
    frame.origin.x = CGRectGetMinX(frame) - CGRectGetWidth([self imageRectForContentRect:contentRect]);
    return frame;
}

@end

UIButton is a class cluster and should not be subclassed.
That is not true, the documentation explicitly mentions subclassing and provides methods you should override for custom layout behaviour.
developer.apple.com/library/ios/documentation/uikit/reference/… buttonWithType If you subclass UIButton, this method does not return an instance of your subclass. If you want to create an instance of a specific subclass, you must alloc/init the button directly and backgroundRectForBounds Subclasses that provide custom background adornments can override this method and return a modified bounds rectangle to prevent the button from drawing over any custom content.` Neither mentions those specific methods, but I assume they don't mind subclasses.
Looks like this formula is better for mirroring image frame: frame.origin.x = CGRectGetMaxX(contentRect) - CGRectGetWidth(frame) - self.imageEdgeInsets.right + self.imageEdgeInsets.left - frame.origin.x; It works better for UIControlContentHorizontalAlignmentCenter and others...
@GwendalRoué Just because it's shorter doesn't mean it's better. It's a hackier way, and it makes the button ignore actual insets, and might break in right-to-left languages. With this answer you have full control of the layout
P
Piotr Tomasik

Just update the insets when the title is changed. You need to compensate for the inset with an equal and opposite inset on the other side.

[thebutton setTitle:title forState:UIControlStateNormal];
thebutton.titleEdgeInsets = UIEdgeInsetsMake(0, -thebutton.imageView.frame.size.width, 0, thebutton.imageView.frame.size.width);
thebutton.imageEdgeInsets = UIEdgeInsetsMake(0, thebutton.titleLabel.frame.size.width, 0, -thebutton.titleLabel.frame.size.width);

You might want to add [thebutton.titleLabel sizeToFit]; before. The width may be zero if you have't triggered a layout. Same goes for the image size (just use the UIImage.size instead of the imageView size)
@delrox good point. Can use titleWidth = [self.titleLabel sizeThatFits:CGSizeMake(CGFLOAT_MAX, self.bounds.size.height)].width; (or if you're concerned about the button frame not yet being established, use CGFLOAT_MAX for the height as well) and imageWidth = self.currentImage.size.width;
Works perfectly on viewDidLayoutSubviews
I had to place this in layoutSubviews in my UITableViewCell subclass but its working good. Thanks!
A
Alexander Vasenin

All of these answers, as of January 2016, are unnecessary. In Interface Builder, set the View Semantic to Force Right-to-Left, or if you prefer programmatic way, semanticContentAttribute = .forceRightToLeft That will cause the image to appear on the right of your text.


Sadly it doesn't support ios older than 9. Still nice answer, tho.
I am sad to report, that setting this on a UIButton that is then used for UIBarButtonItem did not result in any change.
As @Amelia mentioned, it doesn't work if you call UIBarButtonItem(customView: button), but will work if you wrap button inside some empty view
@tt.Kilew , using XCode 8.1 you get it work. I set the uiButton.semanticContentAttribute = .forceRightToLeft and provide let nextButton = UIBarButtonItem(customView: uiButton)
Do not use this if you have support for accessibility, because this brakes the order of the elements spoken out by VoiceOver on the screen.
G
Gennadiy Ryabkin

In interface builder you can configure options Edge Insets for UIButton, separately each of three parts: content, image, title

https://i.stack.imgur.com/KdBVa.png

Xcode 8:

https://i.stack.imgur.com/FQzaB.png


actually best answer in my view this stackoverflow.com/a/39013315/1470374 ))
J
Jean-Baptiste

Update: Swift 3

class ButtonIconRight: UIButton {
    override func imageRect(forContentRect contentRect:CGRect) -> CGRect {
        var imageFrame = super.imageRect(forContentRect: contentRect)
        imageFrame.origin.x = super.titleRect(forContentRect: contentRect).maxX - imageFrame.width
        return imageFrame
    }

    override func titleRect(forContentRect contentRect:CGRect) -> CGRect {
        var titleFrame = super.titleRect(forContentRect: contentRect)
        if (self.currentImage != nil) {
            titleFrame.origin.x = super.imageRect(forContentRect: contentRect).minX
        }
        return titleFrame
    }
}

Original answer for Swift 2:

A solution that handles all horizontal alignments, with a Swift implementation example. Just translate to Objective-C if needed.

class ButtonIconRight: UIButton {
    override func imageRectForContentRect(contentRect:CGRect) -> CGRect {
        var imageFrame = super.imageRectForContentRect(contentRect)
        imageFrame.origin.x = CGRectGetMaxX(super.titleRectForContentRect(contentRect)) - CGRectGetWidth(imageFrame)
        return imageFrame
    }

    override func titleRectForContentRect(contentRect:CGRect) -> CGRect {
        var titleFrame = super.titleRectForContentRect(contentRect)
        if (self.currentImage != nil) {
            titleFrame.origin.x = CGRectGetMinX(super.imageRectForContentRect(contentRect))
        }
        return titleFrame
    }
}

Also worth noting that it handles quite well image & title insets.

Inspired from jasongregori answer ;)


This solution worked for me, however my image needed some space around it so I added the following code: self.contentEdgeInsets = UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0)
I like this way because you can add @IBDesignable to the class and see it flipped at design time.
I prefer this solution because it even works when put in navigation bar.
M
Mark Hennings

I decided not to use the standard button image view because the proposed solutions to move it around felt hacky. This got me the desired aesthetic, and it is intuitive to reposition the button by changing the constraints:

extension UIButton {
    func addRightIcon(image: UIImage) {
        let imageView = UIImageView(image: image)
        imageView.translatesAutoresizingMaskIntoConstraints = false

        addSubview(imageView)

        let length = CGFloat(15)
        titleEdgeInsets.right += length

        NSLayoutConstraint.activate([
            imageView.leadingAnchor.constraint(equalTo: self.titleLabel!.trailingAnchor, constant: 10),
            imageView.centerYAnchor.constraint(equalTo: self.titleLabel!.centerYAnchor, constant: 0),
            imageView.widthAnchor.constraint(equalToConstant: length),
            imageView.heightAnchor.constraint(equalToConstant: length)
        ])
    }
}

https://i.stack.imgur.com/abObc.png


This does not response to taps, the text dims but the image does not
Check your extension on small devices as well.
t
tt.Kilew

If this need to be done in UIBarButtonItem, additional wrapping in view should be used This will work

let view = UIView()
let button = UIButton()
button.setTitle("Skip", for: .normal)
button.setImage(#imageLiteral(resourceName:"forward_button"), for: .normal)
button.semanticContentAttribute = .forceRightToLeft
button.sizeToFit()
view.addSubview(button)
view.frame = button.bounds
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: view)

This won't work

let button = UIButton()
button.setTitle("Skip", for: .normal)
button.setImage(#imageLiteral(resourceName:"forward_button"), for: .normal)
button.semanticContentAttribute = .forceRightToLeft
button.sizeToFit()
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: button)

The issue with UIBarButtonItem literally drove me crazy for hours until I found this answer. Kudos to you.
N
Nazmul Hasan

Do Yourself. Xcode10, swift4,

For programmatically UI design

https://i.stack.imgur.com/d7k3y.png

 lazy var buttonFilter : ButtonRightImageLeftTitle = {
    var button = ButtonRightImageLeftTitle()
    button.setTitle("Playfir", for: UIControl.State.normal)
    button.setImage(UIImage(named: "filter"), for: UIControl.State.normal)
    button.backgroundColor = UIColor.red
    button.contentHorizontalAlignment = .left
    button.titleLabel?.font = UIFont.systemFont(ofSize: 16)
    return button
}()

Edge inset values are applied to a rectangle to shrink or expand the area represented by that rectangle. Typically, edge insets are used during view layout to modify the view’s frame. Positive values cause the frame to be inset (or shrunk) by the specified amount. Negative values cause the frame to be outset (or expanded) by the specified amount.

class ButtonRightImageLeftTitle: UIButton {

    override func layoutSubviews() {
        super.layoutSubviews()

        guard imageView != nil else { return }

        imageEdgeInsets = UIEdgeInsets(top: 5, left: (bounds.width - 35), bottom: 5, right: 5)
        titleEdgeInsets = UIEdgeInsets(top: 0, left: -((imageView?.bounds.width)! + 10), bottom: 0, right: 0 )

    }
}

for StoryBoard UI design

https://i.stack.imgur.com/q0Y1D.png


is there a way to do it more elegant?
Do u have any idea how to remove gap before title?
V
Vitalii Gozhenko

Here is solution for UIButton with center aligned content. This code make image right aligned and allows to use imageEdgeInsets and titleEdgeInsets for precious positioning.

https://i.stack.imgur.com/lEAm4.png

Subclass UIButton with your custom class and add:

- (CGRect)imageRectForContentRect:(CGRect)contentRect {
    CGRect frame = [super imageRectForContentRect:contentRect];
    CGFloat imageWidth = frame.size.width;
    CGRect titleRect = CGRectZero;
    titleRect.size = [[self titleForState:self.state] sizeWithAttributes:@{NSFontAttributeName: self.titleLabel.font}];
    titleRect.origin.x = (self.frame.size.width - (titleRect.size.width + imageWidth)) / 2.0 + self.titleEdgeInsets.left - self.titleEdgeInsets.right;
    frame.origin.x = titleRect.origin.x + titleRect.size.width - self.imageEdgeInsets.right + self.imageEdgeInsets.left;
    return frame;
}

- (CGRect)titleRectForContentRect:(CGRect)contentRect {
    CGFloat imageWidth = [self imageForState:self.state].size.width;
    CGRect frame = [super titleRectForContentRect:contentRect];
    frame.origin.x = (self.frame.size.width - (frame.size.width + imageWidth)) / 2.0 + self.titleEdgeInsets.left - self.titleEdgeInsets.right;
    return frame;
}

Also you can add IBDESIGNABLE to the class header to watch it in the storyborad yadi.sk/i/fd6Si-BJqzCFD
a
akashlal.com

iOS 15 brought an update where you can now handle image placements in buttons in a simpler non messier way, ie. without insets.

https://i.stack.imgur.com/HE8YT.png

https://i.stack.imgur.com/h7wzd.png

This is not a backward compatible feature, and will work only in iOS15+ as was demonstrated in WWDC '21 - https://developer.apple.com/videos/play/wwdc2021/10064/?time=236

Developer documentation: https://developer.apple.com/documentation/uikit/uibutton/configuration?changes=_4


This is the best answer for anyone in a modern project.
c
cnotethegr8

Being that the transform solution doesn't work in iOS 11 I decided to write a new approach.

Adjusting the buttons semanticContentAttribute gives us the image nicely to the right without having to relayout if the text changes. Because of this it's the ideal solution. However I still need RTL support. The fact that an app can not change it's layout direction in the same session resolves this issue easily.

With that said, it's pretty straight forward.

extension UIButton {
    func alignImageRight() {
        if UIApplication.shared.userInterfaceLayoutDirection == .leftToRight {
            semanticContentAttribute = .forceRightToLeft
        }
        else {
            semanticContentAttribute = .forceLeftToRight
        }
    }
}

M
Musa almatri

Extension Way

Using extension to set image on the right side with custom offset

   extension UIButton {
    func addRightImage(image: UIImage, offset: CGFloat) {
        self.setImage(image, for: .normal)
        self.imageView?.translatesAutoresizingMaskIntoConstraints = false
        self.imageView?.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: 0.0).isActive = true
        self.imageView?.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -offset).isActive = true
    }
}

F
Freeubi

Swift -Extend the UiButton and put these lines

    if let imageWidth = self.imageView?.frame.width {
        self.titleEdgeInsets = UIEdgeInsetsMake(0, -imageWidth, 0, imageWidth);
    }

    if let titleWidth = self.titleLabel?.frame.width {
        let spacing = titleWidth + 20
        self.imageEdgeInsets = UIEdgeInsetsMake(0, spacing, 0, -spacing);
    }

E
Erik van der Neut

Building on Piotr Tomasik's elegant solution: if you want to have a bit of spacing between the button label and image as well, then include that in your edge insets as follows (copying my code here that works perfectly for me):

    CGFloat spacing          = 3;
    CGFloat insetAmount      = 0.5 * spacing;

    // First set overall size of the button:
    button.contentEdgeInsets = UIEdgeInsetsMake(0, insetAmount, 0, insetAmount);
    [button sizeToFit];

    // Then adjust title and image insets so image is flipped to the right and there is spacing between title and image:
    button.titleEdgeInsets   = UIEdgeInsetsMake(0, -button.imageView.frame.size.width - insetAmount, 0,  button.imageView.frame.size.width  + insetAmount);
    button.imageEdgeInsets   = UIEdgeInsetsMake(0, button.titleLabel.frame.size.width + insetAmount, 0, -button.titleLabel.frame.size.width - insetAmount);

Thanks Piotr for your solution!

Erik


@lulian: I have been using Liau Jian Jie's solution instead recently (the accepted answer here), and that works brilliantly and is a very elegant solution.
That doesn't work for me either as it changes the text's alignment.
A
Ashutosh Mishra

Took @Piotr's answer and made it into a Swift extension. Make sure to set the image and title before calling this, so that the button sizes properly.

 extension UIButton {
    
    /// Makes the ``imageView`` appear just to the right of the ``titleLabel``.
    func alignImageRight() {
        if let titleLabel = self.titleLabel, imageView = self.imageView {
            // Force the label and image to resize.
            titleLabel.sizeToFit()
            imageView.sizeToFit()
            imageView.contentMode = .ScaleAspectFit
            
            // Set the insets so that the title appears to the left and the image appears to the right. 
            // Make the image appear slightly off the top/bottom edges of the button.
            self.titleEdgeInsets = UIEdgeInsets(top: 0, left: -1 * imageView.frame.size.width,
                bottom: 0, right: imageView.frame.size.width)
            self.imageEdgeInsets = UIEdgeInsets(top: 4, left: titleLabel.frame.size.width,
                bottom: 4, right: -1 * titleLabel.frame.size.width)
          }
        }
     }

C
Community

Subclassing and over-riding layoutSubviews is probably your best way to go.

Referenced from: iPhone UIButton - image position


There are absolutely no problems to subclass UIButton.
C
Chris

A swift option that does what you want without playing with any insets:

class RightImageButton: UIButton {

    override func layoutSubviews() {
        super.layoutSubviews()

        if let  textSize = titleLabel?.intrinsicContentSize(),
                imageSize = imageView?.intrinsicContentSize() {
            let wholeWidth = textSize.width + K.textImageGap + imageSize.width
            titleLabel?.frame = CGRect(
                x: round(bounds.width/2 - wholeWidth/2),
                y: 0,
                width: ceil(textSize.width),
                height: bounds.height)
            imageView?.frame = CGRect(
                x: round(bounds.width/2 + wholeWidth/2 - imageSize.width),
                y: RoundRetina(bounds.height/2 - imageSize.height/2),
                width: imageSize.width,
                height: imageSize.height)
        }
    }

    struct K {
        static let textImageGap: CGFloat = 5
    }

}

L
Lukasz

Solutions mentioned here stopped working, once I enabled Auto Layout. I had to come up with my own:

Subclass UIButton and override layoutSubviews method:

//
//  MIThemeButtonImageAtRight.m
//  Created by Lukasz Margielewski on 7/9/13.
//

#import "MIThemeButtonImageAtRight.h"

static CGRect CGRectByApplyingUIEdgeInsets(CGRect frame, UIEdgeInsets insets);

@implementation MIThemeButtonImageAtRight

- (void)layoutSubviews
{
    [super layoutSubviews];

    CGRect contentFrame = CGRectByApplyingUIEdgeInsets(self.bounds, self.contentEdgeInsets);

    CGRect frameIcon = self.imageView.frame;
    CGRect frameText = self.titleLabel.frame;

    frameText.origin.x = CGRectGetMinX(contentFrame) + self.titleEdgeInsets.left;
    frameIcon.origin.x = CGRectGetMaxX(contentFrame) - CGRectGetWidth(frameIcon);

    self.imageView.frame = frameIcon;
    self.titleLabel.frame = frameText;
}

@end

static CGRect CGRectByApplyingUIEdgeInsets(CGRect frame, UIEdgeInsets insets){

    CGRect f = frame;

    f.origin.x += insets.left;
    f.size.width -= (insets.left + insets.right);
    f.origin.y += (insets.top);
    f.size.height -= (insets.top + insets.bottom);

    return f;

}

Result:

https://i.stack.imgur.com/IH6V4.png


S
Sourabh Sharma

swift 3.0 Migration solution given by jasongregori

class ButtonIconRight: UIButton {
        override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
            var imageFrame = super.imageRect(forContentRect: contentRect)
           imageFrame.origin.x = super.titleRect(forContentRect: contentRect).maxX - imageFrame.width
        return imageFrame
        }

        override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
            var titleFrame = super.titleRect(forContentRect: contentRect)
            if (self.currentImage != nil) {
                titleFrame.origin.x = super.imageRect(forContentRect: contentRect).minX
            }
            return titleFrame
        }

r
rbaldwin

Xcode 11.4 Swift 5.2

For anyone trying to mirror the Back button style with the chevron like this:

https://i.stack.imgur.com/90vDn.jpg

import UIKit

class NextBarButton: UIBarButtonItem {

    convenience init(target: Any, selector: Selector) {

        // Create UIButton
        let button = UIButton(frame: .zero)

        // Set Title
        button.setTitle("Next", for: .normal)
        button.setTitleColor(.systemBlue, for: .normal)
        button.titleLabel?.font = UIFont.systemFont(ofSize: 17)

        // Configure Symbol
        let config = UIImage.SymbolConfiguration(pointSize: 19.0, weight: .semibold, scale: .large)
        let image = UIImage(systemName: "chevron.right", withConfiguration: config)
        button.setImage(image, for: .normal)

        // Add Target
        button.addTarget(target, action: selector, for: .touchUpInside)

        // Put the Image on the right hand side of the button
        // Credit to liau-jian-jie for this part
        button.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
        button.titleLabel?.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
        button.imageView?.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)

        // Customise spacing to match system Back button
        button.imageEdgeInsets = UIEdgeInsets(top: 0.0, left: -18.0, bottom: 0.0, right: 0.0)
        button.titleEdgeInsets = UIEdgeInsets(top: 0.0, left: -12.0, bottom: 0.0, right: 0.0)

        self.init(customView: button)
    }
}

Implementation:

override func viewDidLoad() {
    super.viewDidLoad()
    let nextButton = NextBarButton(target: self, selector: #selector(nextTapped))
    navigationItem.rightBarButtonItem = nextButton
}

@objc func nextTapped() {
    // your code
}

P
Phil Dukhov

With Xcode 13.3 I solved in the following few steps and as well adding padding to the image.

After creating the button then do this as listed below:

First define the image: let symbol = UIImage(named: "put name of your symbol here") Then in viewDidLoad where you created the button, initialise the above defined image in 1, to add the image to the button & set the properties: button.setImage(symbol, for: .normal) button.semanticContentAttribute = .forceRightToLeft button.configuration?.imagePadding = 2

And don't forget to add your button to the view.


A
Alexander Volkov

Swift 3:

open override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
    var frame = super.imageRect(forContentRect: contentRect)
    let  imageWidth = frame.size.width
    var titleRect = CGRect.zero
    titleRect.size = self.title(for: self.state)!.size(attributes: [NSFontAttributeName: self.titleLabel!.font])
    titleRect.origin.x = (self.frame.size.width - (titleRect.size.width + imageWidth)) / 2.0 + self.titleEdgeInsets.left - self.titleEdgeInsets.right;
    frame.origin.x = titleRect.origin.x + titleRect.size.width - self.imageEdgeInsets.right + self.imageEdgeInsets.left;
    return frame
}

open override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
    var frame = super.titleRect(forContentRect: contentRect)
    if let imageWidth = self.image(for: self.state)?.size.width {
        frame.origin.x = (self.frame.size.width - (frame.size.width + imageWidth)) / 2.0 + self.titleEdgeInsets.left - self.titleEdgeInsets.right;
    }
    return frame
}

r
rock workinghouse

How about Constraints? Unlike semanticContentAttribute, they don't change semantics. Something like this perhaps:

 button.rightAnchorconstraint(equalTo: button.rightAnchor).isActive = true

or in Objective-C:

[button.imageView.rightAnchor constraintEqualToAnchor:button.rightAnchor].isActive = YES;

Caveats: Untested, iOS 9+


j
jeet.chanchawat

After trying multiple solutions from around the internet, I was not achieving the exact requirement. So I ended up writing custom utility code. Posting to help someone in future. Tested on swift 4.2

// This function should be called in/after viewDidAppear to let view render
    func addArrowImageToButton(button: UIButton, arrowImage:UIImage = #imageLiteral(resourceName: "my_image_name") ) {
        let btnSize:CGFloat = 32
        let imageView = UIImageView(image: arrowImage)
        let btnFrame = button.frame
        imageView.frame = CGRect(x: btnFrame.width-btnSize-8, y: btnFrame.height/2 - btnSize/2, width: btnSize, height: btnSize)
        button.addSubview(imageView)
        //Imageview on Top of View
        button.bringSubviewToFront(imageView)
    }

S
Sushil Vyas

for this issue you can create UIView inside "label with UIImage view" and set UIView class as a UIControl and create IBAction as tuch up in side

https://i.stack.imgur.com/CDSht.png


R
Rashid Latif

Swift 4 & 5

Change the direction of UIButton image (RTL and LTR)

extension UIButton {
    func changeDirection(){
       isArabic ? (self.contentHorizontalAlignment = .right) : (self.contentHorizontalAlignment = .left)
        // left-right margin 
        self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
        self.titleEdgeInsets = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
    }
}

What's Utility ?
I just remove the Utility, It is a class in my code where I can check whether the selected language is Arabic or English