0

I am using storyboard layouts to set up view layout.

I am supporting both iPhone and iPad layouts.

When the view is created with initWithCoder:, it is initially created with the frame size of the device I was last looking at in Interface Builder.

If I am designing with the iPhone X layout in interface builder and then build and run on an iPad, the view is initially created with iPhone X screen dimensions. Then viewDidLayoutSubviews: is called and it updates the screen dimensions to the correct iPad size.

  1. The subviews are using drawRect inside UIViews to draw the view graphics. I am doing this so I can change graphic colors via code. I change the color variable and then call setNeedsDispay on the view to redraw the view with new colors using CGGraphicsContext commands.

  2. It also allows me to draw any graphic image at any size. And with lots of graphics that means I don't have to include all the different images at 1x, 2x and 3x sizes in my bundle. It's all drawn dynamically.

  3. Some of these images are laid out when the view loads and not in Interface Builder. So I check the screen size and draw the button size and position accordingly.

What happens is, viewDidLoad is called and it draws the graphics based on the initial screen size.

Then viewDidLayoutSubviews is called and I have to update the drawing of the subviews I placed manually repositioning them based on new screen dimensions and then calling the drawRect on them. I feel like this is just unnecessary extra work for the device.

In addition to that, viewDidLayoutSubviews is called for other reasons then just resizing the view on initial load of the viewController. So then each time it's called it will go redraw the subviews, even if they don't need it.

And, if the device I am running on is the same as the device I was using in Interface Builder, it doesn't call the viewDidLayoutSubviews. I can't just let the view layout the subviews there because there is no guarantee it will be called.

My solution so far is creating a variable to track the screen width. I set the variable in viewDidLoad. If it creates the view at iPhone X size, my screenWidthTracker = 1125. When viewDidLayoutSubviews is called I compare the current screen size to screenWidthTracker.

if (self.view.frame != screenWidthTracker) {
     [[NSNotificationCenter defaultCenter] postNotificationName:@"updateView" object:self];
};

if the view has changed size, it sends a message to redraw views. Any views I have placed manually as subviews are registered to listen for @"updateView".

Is there a better way to manage this? Is there a method that gets called ONLY when screen dimensions change and not when its updating the position of other subviews? Should I be utilizing viewDidAppear? I feel as though that is too late in the chain and I don't want the user to see button size updates.

Mark A.
  • 1,065
  • 10
  • 15
  • You don't really mean you are calling `drawRect`, do you? That is absolutely illegal. Also do you really have to do all this work manually? Isn't this what autolayout is for? It feels like you're having a massive misunderstanding of how subview layout and drawing works on iOS, and thus doing far more work than you have to. Ideally you should not need _any_ code except your `drawRect` implementations (implementing it, not calling it). – matt Feb 04 '18 at 01:31
  • I suppose it’s possible I am having a massive misunderstanding of how sub view layout and drawing works. As far as calling drawRect I must have been unclear. I only meant drawRect was being called after viewDidLayoutSubviews. If I need it to update, I call setNeedsDisplay which I believe calls drawRect - unless this is where you believe my misunderstanding is. – Mark A. Feb 04 '18 at 23:48
  • This is only an issue in two views. In both of them I have an undetermined number of icons to devide into two rows and size them so they fit and are placed evenly in line. So depending on whether I have 6 or 12 size and position changes and so I found this easier then dealing with it in other ways. I suppose I could put in a stackView and add them to that instead. – Mark A. Feb 04 '18 at 23:56

1 Answers1

0

In general, from the description you've given, I'd say that at the very most, you should be doing as much size work as possibly by using autolayout (which you can configure entirely within the storyboard) and then implementing only drawRect and viewDidLayoutSubviews — and the latter only if you need to.

A very common strategy is to use a boolean flag to set up initial conditions in the first call to viewDidLayoutSubviews, and not use it thereafter. Screen size changes after launch (not at launch), such as rotation, are detected by implementing willTransition(to:with:) — and even then you should check to see that the old size and new size are not the same (180 degree rotation) and do nothing if they are.

matt
  • 447,615
  • 74
  • 748
  • 977
  • I am not concerned with rotation as I don’t support it. I like the idea of just setting a bool for this. I guess I just am parinoid that I am not thinking of or know of some situation where viewDidLayoutSubviews would be called and not do the complete resizing on the first time through. So as a little extra security I started tracking the actual size of the view that holds the sub views that begging with an undetermined number of elements. 99% of my views use auto layout. But thinking about it more in terms of auto layout you’ve made me think about adding them to a stackView. Thanks. – Mark A. Feb 05 '18 at 00:05