Since Xcode 8 and iOS10, views are not sized properly on viewDidLayoutSubviews

前端 未结 13 1977
北海茫月
北海茫月 2020-11-29 16:48

It seems that with Xcode 8, on viewDidLoad, all viewcontroller subviews have the same size of 1000x1000. Strange thing, but okay, viewDidLoad has n

相关标签:
13条回答
  • 2020-11-29 17:37

    I know this wasn't your exact question, but I ran into a similar problem where as on the update some of my views were messed up despite having the correct frame size in viewDidLayoutSubviews. According to iOS 10 Release notes:

    "Sending layoutIfNeeded to a view is not expected to move the view, but in earlier releases, if the view had translatesAutoresizingMaskIntoConstraints set to NO, and if it was being positioned by constraints, layoutIfNeeded would move the view to match the layout engine before sending layout to the subtree. These changes correct this behavior, and the receiver’s position and usually its size won’t be affected by layoutIfNeeded.

    Some existing code may be relying on this incorrect behavior that is now corrected. There is no behavior change for binaries linked before iOS 10, but when building on iOS 10 you may need to correct some situations by sending -layoutIfNeeded to a superview of the translatesAutoresizingMaskIntoConstraints view that was the previous receiver, or else positioning and sizing it before (or after, depending on your desired behavior) layoutIfNeeded.

    Third party apps with custom UIView subclasses using Auto Layout that override layoutSubviews and dirty layout on self before calling super are at risk of triggering a layout feedback loop when they rebuild on iOS 10. When they are correctly sent subsequent layoutSubviews calls they must be sure to stop dirtying layout on self at some point (note that this call was skipped in release prior to iOS 10)."

    Essentially you cannot call layoutIfNeeded on a child object of the View if you are using translatesAutoresizingMaskIntoConstraints - now calling layoutIfNeeded has to be on the superView, and you can still call this in viewDidLayoutSubviews.

    0 讨论(0)
  • 2020-11-29 17:38

    Are you using rounded corners for your button ? Try calling layoutIfNeeded() before.

    0 讨论(0)
  • 2020-11-29 17:38

    If you need to do something based on your view's frame - override layoutSubviews and call layoutIfNeeded

        override func layoutSubviews() {
        super.layoutSubviews()
    
        yourView.layoutIfNeeded()
        setGradientForYourView()
    }
    

    I had the issue with viewDidLayoutSubviews returning the wrong frame for my view, for which I needed to add a gradient. And only layoutIfNeeded did the right thing :)

    0 讨论(0)
  • 2020-11-29 17:41

    My issue was solved by changing the usage from

    -(void)viewDidLayoutSubviews{
        [super viewDidLayoutSubviews];
        self.viewLoginMailTop.constant = -self.viewLoginMail.bounds.size.height;
    }
    

    to

    -(void)viewWillLayoutSubviews{
        [super viewWillLayoutSubviews];
        self.viewLoginMailTop.constant = -self.viewLoginMail.bounds.size.height;
    }
    

    So, from Did to Will

    Super weird

    0 讨论(0)
  • 2020-11-29 17:42

    If the frames are not correct in layoutSubViews (which they are not) you can dispatch async a bit of code on the main thread. This gives the system some time to do the layout. When the block you dispatch is executed, the frames have their proper sizes.

    0 讨论(0)
  • 2020-11-29 17:43

    Solution: Wrap everything inside viewDidLayoutSubviews in DispatchQueue.main.async.

    // swift 3
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
    
        DispatchQueue.main.async {
            // do stuff here
        }
    }
    
    0 讨论(0)
提交回复
热议问题