15
votes

I have a view with a couple of buttons, various sizes. All of them has a title and an image. I wanted to have the image on top, the title on the bottom, centered. This works fine with the following code:

- (void)centerAlignImageAndTextForButton:(UIButton*)button
{
    CGFloat spacing = 5;
    CGSize imageSize = button.imageView.frame.size;
    button.titleEdgeInsets = UIEdgeInsetsMake(0, -imageSize.width, -(imageSize.height + spacing), 0);
    CGSize titleSize = button.titleLabel.frame.size;
    button.imageEdgeInsets = UIEdgeInsetsMake(-(titleSize.height + spacing), 0, 0, -titleSize.width);
}

However, I notice a little pause before entering this view. Time Profiler in Instruments shows, that 99% of the time spent in viewDidLoad is caused by calling my method for the buttons:

283.0ms  -[XViewController viewDidLoad]
282.0ms    -[XViewController centerAlignImageAndTextForButton:]
141.0ms      -[UIButton imageView]
 98.0ms        -[UIView(Hierarchy) layoutBelowIfNeeded]
141.0ms      -[UIButton titleLabel]
103.0ms        -[UIView(Hierarchy) layoutBelowIfNeeded]

Various layout related things are called within [UIView(Hierarchy) layoutBelowIfNeeded], like [UIView(AdditionalLayoutSupport) recursiveLayoutEngineDidChange] or [NSLayoutConstraint addToEngine].

I know it would be possible to set these insets in Interface Builder, but not only that is a tedious work (need to 'guess' the width of the text); the app will also be localized, so the insets that are good for one language, won't be for another where text size differs (I'm using base localization for storyboards, and .strings files instead of duplicating the whole storyboard for each language). So for me it seems setting the insets programmatically is the way to go, but the lag it causes isn't really acceptable.

Any thoughts how this could resolved?

2
Why aren't you setting them on layoutSubViews?orkoden

2 Answers

6
votes

Moving all the centerAlignImageAndTextForButton calls from viewDidLoad to viewDidLayoutSubviews reduces the loading time from the previous 283 millisecs to 18 millisecs (1 millisecs for viewDidLoad and 17 millisecs for viewDidLayoutSubviews).

However, I'm a bit puzzled, why is it so expensive to set those insets from viewDidLoad...

0
votes

Had a similar problem with a slow call to titleLabel from awakeFromNib in custom UIButton subclass. Moving the code to view controller was undesirable due to a large number and/or nontrivial life-cycle of the buttons. Moving the code to layoutSubviews did not help. Eventually I moved the code to didMoveToSuperview and it solved the problem.

- (void)didMoveToSuperview
{
    [super didMoveToSuperview];
    self.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
    self.titleLabel.numberOfLines = 2;
    self.titleLabel.textAlignment = NSTextAlignmentCenter;
}