There are a couple of questions asking this on SO but none have asked that properly.
I am using a custom progress bar that is a UIView
based class with this implementation.
@implementation MCProgressBarView {
UIImageView * _backgroundImageView;
UIImageView * _foregroundImageView;
CGFloat minimumForegroundWidth;
CGFloat availableWidth;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
// [self bounds] is 1000x1000 here... what?
if (self) [self initialize];
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) [self initialize];
return self;
}
- (void) initialize {
UIImage * backgroundImage = [[[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"progress-bg" ofType:@"png"]]
resizableImageWithCapInsets:UIEdgeInsetsMake(0.0f, 10.0f, 0.0f, 10.0f)];
UIImage * foregroundImage = [[[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"progress-bg" ofType:@"png"]]
resizableImageWithCapInsets:UIEdgeInsetsMake(0.0f, 10.0f, 0.0f, 10.0f)];
_backgroundImageView = [[UIImageView alloc] initWithFrame:self.bounds];
_backgroundImageView.image = backgroundImage;
[self addSubview:_backgroundImageView];
_foregroundImageView = [[UIImageView alloc] initWithFrame:self.bounds];
_foregroundImageView.image = foregroundImage;
[self addSubview:_foregroundImageView];
UIEdgeInsets insets = foregroundImage.capInsets;
minimumForegroundWidth = insets.left + insets.right;
availableWidth = self.bounds.size.width - minimumForegroundWidth;
[self adjustProgress:0.0f];
}
I have created a UIView
on interface builder to be 200x20 points and assigned that view to be of this custom class MCProgressBarView
I am using. When the app runs, initWithCoder
runs and creates a progress view with a size of 1000x1000 points, disregarding the size that the view has on IB.
How do I force initWithCoder to run with the proper size assigned on IB?
NOTE: I don't want to hard wire that to 200x20 and I don't want to set this by code at run time. Is there a way to make this work?
TL;DR: Move your frame/bounds logic to viewDidLayoutSubviews
.
initWithCoder:
is not safe for performing frame logic. You should use viewDidLayoutSubviews
for that. Apple uses 1000x1000 bounds starting with iOS 10. It is unclear if bug or intentional, but it seems to have a net positive outcome - people come here and ask about it. ;-)
In fact, initWithCoder:
has never been safe for such logic. In the past, you'd have a view whose bounds would be those of iPhone 5's screen because that was what you used in IB, but then the view would grow to iPhone 6 Plus size or iPad size, and it would cause visual issues.
Now, Apple just sets the bounds to 1000x1000, which is incorrect for all cases. The bounds are corrected once a layout pass is performed on the view.