2
votes

I have a custom UIView subclass which grabs all the subviews in layoutSubviews and alters their layout/size depending on some custom settings.

It all works fine if I create this view programatically however if I add this UIView subclass to a storyboard and add some subviews in the storyboard, the layout changes don't seem to persist.

I can see the following logic flow using breakpoints.

  1. The VC & custom View is initialised

  2. The VC viewDidLayoutSubviews is called. (I'm doing nothing in here just observing it is called using a breakpoint)

  3. The custom UIView, layoutSubviews is called.

    override func layoutSubviews() {
        for subView in self.subviews {
            var subview = subView as! UIView
            var frame = subview.frame
            //some arbritary frame adjustments.
            frame.origin.x = 10; frame.origin.y = 50;
            subview.frame = frame
            subview.setNeedsLayout() //tried with and without this line with no difference.
        }
    }
    

    I can see that the frames are being adjusted as I print all the subviews before adjusting them, and then again after via the debugger.

    po self.subviews
    
  4. VC viewDidLayoutSubviews is called again. However when I print the subviews here, I can see that they're back to their original frames again.

    po customView.subviews
    
  5. View appears and frames have not been adjusted.

If I call layoutSubviews again the subview frames do end up adjusting however only after the view had appeared which is too late.

override func viewDidLayoutSubviews() {
    customView?.layoutSubviews();
}

The flow for this is as follows:

  1. VC & View is initialised

  2. VC viewDidLayoutSubviews is called

  3. layoutSubviews is called twice in a row

  4. The View appears and the subviews have not been readjusted, even though they were in layoutSubviews when printed via the debugger.

  5. VC - viewDidLayoutSubviews is called again

  6. layoutSubviews is called again.

  7. You can see the subviews adjust but it's too late because the user would be able to see the subviews 'jump' into place.

Any ideas as to what I'm missing here? The subview frames are definitely being set correctly but they're just being reverted back before the view appears.

I just happen to be using swift for this but responses in Objective C are very welcome.

1
Have you tried calling subview.layoutIfNeeded() right after setNeedsLayout - ozgur
Yes I've tried that. Unfortunately nothing changes. - pbevis
This is probably due to auto layout. If you set the frames of any objects you created in the storyboard (with auto layout on), they will revert to their size and positions defined by their constraints whenever the view needs to be redrawn. - rdelmar
@rdelmar I've tried removing all their constraints and setting autoresizemask to none and setting autoresize subviews to false as well before setting their frames though. Wouldn't that remove the auto layout issues? - pbevis
Unless you de-selected the "Use Auto Layout" checkbox, auto layout is on. If you remove all the constraints, the system adds them for you. - rdelmar

1 Answers

2
votes

It does appear to be an issue with auto layout interfering with the subview frame changes. I found a way to get around it by setting translatesAutoresizingMaskIntoConstraints to false before setting the subview's frame. This way I can still allow auto layout for the rest of the view but it won't interfere with my custom layout changes.

subview.setTranslatesAutoresizingMaskIntoConstraints(false);
subview.frame = frame;