For iOS7, I used the UIButton
titleLabel.frame.size.width
property to determine the width of my button title under different localisations so I could position the title correctly using contentInset
on the UIButton
.
My UIButton
is setup with an image and title and I always want the combination of these two to be centred horizontally in the UIButton
for example:
[space-x (image) space-y (titleLabel) space-x]
Under iOS 7 and Xcode 5.1, the following code works perfectly (even running on iOS 8 GM when built in XCode 5.1):
CGSize buttonSize = button.frame.size;
CGSize titleSize = button.titleLabel.frame.size;
float contentInset =((buttonSize.width - (titleSize.width + 18 + 3)) / 2 );
float contentInsetRounded = roundf(contentInset);
[button setContentEdgeInsets:(UIEdgeInsetsMake(0, contentInsetRounded, 0, 0))];
(18 is the width of the image, and 3 is the number of points spacing between the image and titleLabel
or space-y in my example above)
Under iOS 8 and Xcode 6 GM, button.titleLabel.frame.size
is returning a CGSize
with zero width & height so my contentInset ends up centring the UIImage
in the UIButton
, causing the titleLabel
to truncate.
Any ideas? I tried setting the titleLabel
text immediately before this code in case it thought it was empty but that didn't help either.
Thanks in advance!
Set the title text, then make a sizeToFit for the title label, and try to get the titleLabel.frame.size.width
[myButton setTitle:@"My Title" forState:UIControlStateNormal];
[myButton.titleLabel sizeToFit];
I resolved it. App run on iOS8, build by Xcode 6, not update frame right after UIButton setTitle
, setTitleEdgeInsets
. It will wait for next UI update. So, if you get frame of titleLabel, you will get CGRectZero.
Solution:
Call below methods after set UI property, to update layout immediately:
[self setNeedsLayout];
[self layoutIfNeeded];
Or:
- Delay a second, and you can get
titleFrame
, use self.titleLabel.frame
.
Or:
- dispatch_async in main queue, to move code to next queue
If you are then setting an image next to a button after getting the frame, you can use
[theButton setTitle: @"theTitle" forState:UIControlStateNormal];
[theButton setImage: theImage forState:UIControlStateNormal];
[theButton setNeedsLayout];
[theButton layoutIfNeeded];
[theButton setImageEdgeInsets: UIEdgeInsetsMake(6, transactionsButton.titleLabel.frame.size.width + 40.0, 0, 0)];
And in Swift 3, this will do.
self.view.layoutIfNeeded()
Maybe this will not work for you, but I had the same problem when running my code in XCode 6 GM / iOS8 and could not get it to work. It worked randomly and I ended up using a different approach instead of an NSString I used an NSAttributedString as the title.
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:@"Button text"];
NSTextAttachment* textAttachment = [NSTextAttachment new];
textAttachment.image = [UIImage imageNamed:@"Arrows"];
NSAttributedString *finalString = [NSAttributedString attributedStringWithAttachment:textAttachment];
[attributedText appendAttributedString:finalString];
Only caveat is that it’s iOS7 and up, but seems to work flawless.
Turns out I had greatly over engineered myself into this problem. I took out all of my content inset code and therefore didn't need to find the frame.size. I now just centre the button titles and all is well!
Still seems there is an issue with getting the frame.size but for my needs, I can work around it.
So we can try this code :
func withTitleAndImageAlign(title:String,image:UIImage,RL_Space:CGFloat) {
setNeedsLayout()
layoutIfNeeded()
let buttonWidth = frame.size.width
let textWidth = titleLabel?.frame.size.width
let imageWidth = imageView?.frame.size.width
let lim = buttonWidth - (textWidth! + imageWidth! + (2 * RL_Space) )
imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: lim)
titleEdgeInsets = UIEdgeInsets(top: 0, left: lim, bottom: 0, right: 0)
}