43

I need to create button programatically with an image for normal and highlighted state as well text. I cannot build it using Interface Builder, because I need to create buttons over a UIScrollView. Here is the code I have so far:

- (void)loadView {
    CGRect fullScreenRect=[[UIScreen mainScreen] applicationFrame];
    scrollView=[[UIScrollView alloc] initWithFrame:fullScreenRect];
    scrollView.contentSize=CGSizeMake(320,960);

    UIImageView *tempImageView2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"image.jpeg"]];

    UIImage * buttonImage = [UIImage imageNamed:@"contentlist_active.png"];

    self.view=scrollView;
    [scrollView addSubview:tempImageView2];

    btn = [UIButton buttonWithType:UIButtonTypeCustom];
    btn.frame = CGRectMake(22, 100, 277, 32);

    [btn setImage:buttonImage forState:UIControlStateNormal]; 

    [btn setTitle:@"hello world" forState:UIControlStateNormal];
    [btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];

    [scrollView addSubview:btn];

}

But the text on the button is not showing. If I comment out the setImage for button, then text shows perfectly, otherwise not. Can I have both text and an image at the same time?

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Haris Hussain
  • 2,531
  • 3
  • 25
  • 38

7 Answers7

80

UIButtons setImage sets the image above the title so you will not be able to see the text below it. So try to set image to your button with setBackgroundImage.

Objective-C:

[btn setBackgroundImage:buttonImage forState:UIControlStateNormal]; 

Swift:

myButton.setBackgroundImage(buttonImage, forState: .normal)
ipraba
  • 16,485
  • 4
  • 59
  • 58
  • 1
    Can you tell me how can i align the text, either left or Right, if i am to place arabic text for example. – Haris Hussain Mar 27 '12 at 10:52
  • 1
    [yourButton.titleLabel setTextAlignment:UITextAlignmentLeft]; – ipraba Mar 27 '12 at 10:55
  • My images are still not fitting in the button if I run the app in iPad air 2 because they have low resolution.Any suggestion how I can stretch them to fill in and also images are not applied at background. – tryKuldeepTanwar Jun 01 '16 at 12:00
  • Just wanted to point out that the UIControlState is unnecessary if you want a slightly swifter syntax – H4Hugo Aug 26 '16 at 09:38
24

You've made a mistake there, you're doing

[btn setBackgroundImage:buttonImage forState:UIControlStateNormal]; 

instead of

[btn setImage:buttonImage forState:UIControlStateNormal]; 

This will work fine.

Maen
  • 10,603
  • 3
  • 45
  • 71
Saroj Kumar ojha
  • 485
  • 4
  • 15
6

Swift 4

  • You can set the image and text together as a attribute text and you can do this by implementing below code:
  • here i have customised function in which you can pass the title string and button image it will returned you an attributed text and you can set on UIButton/UILabel title as a attributed title/text.

- if you want to display an image prefix with title

func AttributedTextwithImagePrefix(AttributeImage : UIImage , AttributedText : String , buttonBound : UIButton) -> NSMutableAttributedString
{
    let fullString = NSMutableAttributedString(string: "   ")
    let image1Attachment = NSTextAttachment()
    image1Attachment.bounds = CGRect(x: 0, y: ((buttonBound.titleLabel?.font.capHeight)! - AttributeImage.size.height).rounded() / 2, width: AttributeImage.size.width, height: AttributeImage.size.height)
    image1Attachment.image = AttributeImage
    let image1String = NSAttributedString(attachment: image1Attachment)
    fullString.append(image1String)
    fullString.append(NSAttributedString(string: "  " + AttributedText))
    return fullString
}

this is how you can use this :

self.btnprefix.setAttributedTitle(AttributedTextwithImagePrefix(AttributeImage: #imageLiteral(resourceName: "avtar"), AttributedText: " prefix avtar", buttonBound: self.prefix), for: .normal)

- if you want to display an image suffix with title

func AttributedTextwithImageSuffix(AttributeImage : UIImage , AttributedText : String , buttonBound : UIButton) -> NSMutableAttributedString
{
    let fullString = NSMutableAttributedString(string: AttributedText + "  ")
    let image1Attachment = NSTextAttachment()
    image1Attachment.bounds = CGRect(x: 0, y: ((buttonBound.titleLabel?.font.capHeight)! - AttributeImage.size.height).rounded() / 2, width: AttributeImage.size.width, height: AttributeImage.size.height)
    image1Attachment.image = AttributeImage
    let image1String = NSAttributedString(attachment: image1Attachment)
    fullString.append(image1String)
    fullString.append(NSAttributedString(string: ""))
    return fullString
}

this is how you can use this :

self.btnsuffix.setAttributedTitle(AttributedTextwithImageSuffix(AttributeImage: #imageLiteral(resourceName: "avtar"), AttributedText: " suffix avtar", buttonBound: self.btnsuffix), for: .normal)

OUTPUT WILL BE

enter image description here

Azharhussain Shaikh
  • 1,654
  • 14
  • 17
4

Have you tried

[btn setBackgroundImage:buttonImage forState:UIControlStateHighlighted];

It might solve your problem.

mdb
  • 52,000
  • 11
  • 64
  • 62
Rana Anees
  • 307
  • 2
  • 13
2

I think Azharhussain Shaikh's answer is not working with larger images so I've twisted it a little and converted to a Swift 4 extension to UIButton. There you go:

extension UIButton {
func setAttributedTextWithImagePrefix(image: UIImage, text: String, for state: UIControl.State) {
    let fullString = NSMutableAttributedString()

    if let imageString = getImageAttributedString(image: image) {
        fullString.append(imageString)
    }

    fullString.append(NSAttributedString(string: "  " + text))

    self.setAttributedTitle(fullString, for: state)
}

func setAttributedTextWithImageSuffix(image: UIImage, text: String, for state: UIControl.State) {
    let fullString = NSMutableAttributedString(string: text + "  ")

    if let imageString = getImageAttributedString(image: image) {
        fullString.append(imageString)
    }

    self.setAttributedTitle(fullString, for: state)
}

fileprivate func getImageAttributedString(image: UIImage) -> NSAttributedString? {
    let buttonHeight = self.frame.height

    if let resizedImage = image.getResizedWithAspect(maxHeight: buttonHeight - 10) {
        let imageAttachment = NSTextAttachment()
        imageAttachment.bounds = CGRect(x: 0, y: ((self.titleLabel?.font.capHeight)! - resizedImage.size.height).rounded() / 2, width: resizedImage.size.width, height: resizedImage.size.height)
        imageAttachment.image = resizedImage
        let image1String = NSAttributedString(attachment: imageAttachment)
        return image1String
    }

    return nil
}
}

and a extension to UIImage:

extension UIImage {

func getResized(size: CGSize) -> UIImage? {
    if UIScreen.main.responds(to: #selector(NSDecimalNumberBehaviors.scale)) {
        UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale);
    } else {
        UIGraphicsBeginImageContext(size);
    }

    self.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height));
    let newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
}

func getResizedWithAspect(scaledToMaxWidth width: CGFloat? = nil, maxHeight height: CGFloat? = nil) -> UIImage? {
    let oldWidth = self.size.width;
    let oldHeight = self.size.height;

    var scaleToWidth = oldWidth
    if let width = width {
        scaleToWidth = width
    }

    var scaleToHeight = oldHeight
    if let height = height {
        scaleToHeight = height
    }

    let scaleFactor = (oldWidth > oldHeight) ? scaleToWidth / oldWidth : scaleToHeight / oldHeight;

    let newHeight = oldHeight * scaleFactor;
    let newWidth = oldWidth * scaleFactor;
    let newSize = CGSize(width: newWidth, height: newHeight);

    return getResized(size: newSize);
}
}
1

Enumeration

 enum buttonImageDirection: Int {
                    case left = 0
                    case right
                    case top
                    case bottom
    }

Extension

extension UIButton{
     func setButtonWithTitleAndImage(fontSize : CGFloat = 15, fontType : String = "Poppins-Medium", textColor: UIColor, tintColor : UIColor, bgColor:UIColor = .clear, buttonImage: UIImage?, imagePosition:buttonImageDirection = .left, imageSizeHW: CGFloat = 30){
            if imageView != nil {
                let image = buttonImage?.withRenderingMode(.alwaysTemplate)
                self.setImage(image, for: .normal)
                self.titleLabel?.font = UIFont(name: fontType, size: fontSize)
                self.setTitleColor(textColor, for: .normal)
                self.tintColor = tintColor
                self.backgroundColor = bgColor
                
                switch imagePosition{
                case .left:
                    imageEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: (bounds.width - (imageSizeHW + 5)))
                    titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: (imageView?.frame.width)!)
                case .right:
                    imageEdgeInsets = UIEdgeInsets(top: 5, left: (bounds.width - (imageSizeHW + 5)), bottom: 5, right: 5)
                    titleEdgeInsets = UIEdgeInsets(top: 0, left: (imageView?.frame.width)!, bottom: 0, right: 0)
                case .top:
                    imageEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: (bounds.width - (imageSizeHW + 5)), right: 5)
                    titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: (imageView?.frame.height)!, right: 0)
                case .bottom:
                    imageEdgeInsets = UIEdgeInsets(top: (bounds.width - (imageSizeHW + 5)), left: 5, bottom: 5, right: 5)
                    titleEdgeInsets = UIEdgeInsets(top: (imageView?.frame.height)!, left: 0, bottom: 0, right: 0)
                }
            }
            self.layoutIfNeeded()
        }
}
  • Make sure button height and width must and should be greater the image

myButton. setButtonWithTitleAndImage(fontSize : 15, fontType : "Poppins-Medium", textColor: UIColor.red, tintColor : UIColor.red, bgColor: UIColor.white, buttonImage: UIImage(named: "Bell.png"), imagePosition: .left, imageSizeHW: 30)
Sai kumar Reddy
  • 1,751
  • 20
  • 23
0
       var menuButton:UIButton?

    override func viewWillAppear(animated: Bool) {
  menuButton =  UIButton(frame: CGRectMake(0,0,30,30))
        menuButton?.setBackgroundImage(UIImage(named: "menu.png"), forState: UIControlState.Normal)

    }
Alvin George
  • 14,148
  • 92
  • 64