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 butt
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);
}
}