Autolayout problems with iOS8 with code that works fine on iOS7 - ios

I'm in the midst of developing an app for the iPhone and iPad. It supports iOS6 and iOS7 and it uses auto layout exclusively.
This past week, when Apple announced that iOS8 was ready for prime-time, I upgraded one of my iPhones and an iPad both to iOS8. I also bumped my XCODE up to version 6. I have 2nd iPhone which I left at iOS7.
I generated new executables with Xcode 6 and I was distressed to see that their screen layouts were messed up when executed on my devices running iOS8 but still fine on iOS7. This is true on both my physical devices and on Xcode's emulators.
It took a lot of digging but I'm pretty clear now on what's happening though I don't know why.
Specifically, certain auto layout operations are failing for me on iOS8 but they are fine on iOS7.
Some examples involving a button which I am placing on an underlying view whose size is equal to the size of the screen:
(1) If I ask auto layout to position the button's horizontal center (CX) equal to the underlying view's horizontal center, the result is that the button's horizontal center is placed on the underlying view's left edge.
(2) If I ask auto layout to to make the width of the button equal to 50% of the width of the underlying view, it gives it no width at all.
I am able to work around these issues as follows:
(1) I ask auto layout to position the button's center equal to the underlying view's left edge plus 50% of the screen's width.
(2) I ask auto layout to make the button's width equal to 50% of the screen's width.
I am slowly clawing my way, with workarounds like these, back to auto layout code that works for me on both iOS7 and iOS8. But I am really wondering what's going on here.
It looks like auto layout cannot determine the size of the underlying view and so auto layout calculations that require that information fail. But it does know where the top and left edges of the view are so calculations based on those data succeed.
This is a large app and I've written many hundreds of lines of auto layout code for iOS6 and iOS7 that work perfectly for me.
I've been tweaking and trying things now with iOS8 for three days and I'm no wiser than I was when I began.
Anyone have any suggestions or thoughts as to what might be the issue here?

#robmayoff has a great answer for this: https://stackoverflow.com/a/26066992/1424669
Essentially, in iOS8 you can no longer call setNeedsUpdateConstraints and setNeedsLayout on a view and expect the constraints of subviews to update.
You must call these methods on the view whose constraint is changing. This is backwards compatible to iOS7.
EXAMPLE:
Suppose you have a ViewController with root view self.view and a subview called containerView. containerView has a NSLayoutConstraint attached to it that you want to change (in this case, top space).
In iOS7 you could update all constraints in a VC by requesting a new layout for the root view:
self.containerView_TopSpace.constant = 0;
[self.view setNeedsUpdateConstraints];
[self.view setNeedsLayout];
In iOS8 you need to request layouts on the containerView:
self.containerView_TopSpace.constant = 0;
[self.containerView setNeedsUpdateConstraints];
[self.containerView setNeedsLayout];

You might find the answers to this question helpful: UICollectionView cell subviews do not resize
In most cases the works in iOS7 but not on iOS 8 auto layout problems seem to stem from the root view not being sized correctly in iOS 8, particularly when we set translatesAutoresizingMaskIntoConstraints to NO. For my views I was able to set the root view's frame in layoutSubviews (or whichever appropriate initializer that does have the correct bounds) and this resolved the issue.
self.contentView.frame = CGRectInset(self.bounds, 0, 0);
As shown in the answer above, you could also do
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
and then turn translatesAutoresizingMaskIntoConstraints back to NO before you start setting your own constraints in code.
Definitely hate that so much of our time is taken with these annoying gotchas.

In my case, the problem related to constraints labeled UIView-Encapsulated-Layout-Width and UIView-Encapsulated-Layout-Height. When I removed them, everything behaved as though my view was of zero size, with everything centered on the upper left corner of the screen. When I left them in, new constraints worked as expected. I also retained the constraints labeled _UILayoutSupportConstraint.

Related

Auto Layout constraints for container view inside UITableView header broken for different devices

I have an issue that makes no sense to me. I have the following setup:
| UITableView |
|| UITableView header ||
||| UIView |||
So inside my UITableView header I have a container view that has leading, trailing and top constraints to its superview. Everything is set up correctly for my test device size (which is iPhone 8). If I change the test device to iPhone 8 Plus I get a strange offset for my trailing constraint - 39pt to the right edge, which is exactly the difference between iPhone 8 plus width in points and iPhone 8.
When I switch between devices in Xcode and see that the trailing constraint is not correct I just make an adjustment myself (change trailing to 1 and then back to 0) and the problem goes away for the particular device.
Initially I thought it is bug in Xcode but when I tested on a device the problem is still there.
I tried setting up a new view controller and adding the same elements but with no effect.
I am attaching screenshots to make my issue clearer.
Before: Adjusting the desired constraints
After: Switching to a device with different size
Adding this as an answer because it's too much for a comment - even though it's really just a confirmation, not a solution.
OK - looked at your project.
I'd say it's an IB / Storyboard bug, which I've seen in other circumstances. If you change the View As... device, the frame does not update immediately.
However, if you change anything that would cause a layout update - such as temporarily changing the background color of a view or font size of a label - everything should snap into place.
You'll also notice that if you select an element and move it slightly, the Update Frames button / menu item becomes enabled... and that will also correctly update the frames.
Note: When I ran the app, regardless of how the layout looked in Storyboard, the constraints correctly sized the views at run-time.
At first I accepted DonMag's answer (thanks for your time) as this really seemed to be an Xcode Interface Builder bug. As I investigated further when having the scenario I mentioned auto layout constraints are not updating the layout when I need it hence not giving me the right view.bounds.
I tried getting it in viewDidLayoutSubviews() without success as well - it was still giving me a size that suits another device.
What did the trick was calling view.layoutIfNeeded() before working with view's bounds. What it does is to update the view's layout immediately. As a result you can access the desired view's bounds.

Xcode / Swift Scrollview Layout too wide

I'm part of a team working on an app that uses auto-layout for the iPhone 6, but we just found out that we also need to support iPhone 4s and 5. Because the layout was designed for 6's larger screen, we decided to use ScrollViews.
I used this video as a tutorial of sorts:
UIScrollView and Autolayout - Xcode 6
For the most part, it works. I can scroll up and down.
The Problem:
Scrolling vertically works. But now I also have to scroll horizontally, which is not ideal / shouldn't happen.
Even with the simulator set back to iPhone 6, anything with the scrollview gets widened to the point that the user has to scroll sideways.
Any suggestions?
What I Tried:
Some parts of the app does work without a hitch as far as my eye can see, but I've yet to apply a scrollview to those, so it's hard to tell if it's the scrollview's fault or something else at work. I did find this, though:
Non-working (scrollviews used - can scroll down, but width gets blown up):
Working (no scrollviews used - can't scroll down, but UI matches screen width):
I have no idea if that actually makes a difference. I tried to set the size to any/any as well, but that screws up with existing auto-layout constraints.
I also tried doing it programatically, but there's some known issues regarding the system returning the wrong screen size (which screws up everything even worse).
When working with ScrollView & AutoLayout you should put all content in a content view(UIView) and pin it to scroll view. (I am assuming this you did)
Now, If you want to disable horizontal scroll(like in TableView) then you will need to add a equal width constraint between scroll view & content view.

ios autolayout issue on smaller screen

Originally I am working off of a view of size 4.7 inches in my storyboard and laying out everything from there, however I'm running an issue when running the app on a smaller screen.
The view in question is a view with a scrollview inside of it and a bunch of labels and text fields inside that. When running on the iPhone 4s for example, or even the 5, the scrollview appeared to be pinned at the bottom and I was wondering why, I had tried playing around with insets and offsets but nothing worked.
Looking at my storyboard, when I switched the fixed size of the view to 3.5 inches, it looked like all my top elements were outside of the view altogether.
I'm not exactly sure how to go about this situation.
Any help would be appreciated.
Can't help too much without more information but it sounds like you don't have constraints set properly. You should have constraints set for the scrollview to match the views size and be centered as well the labels have constraints to the scrollview, other wise when the screen sizes change there will be issues. When working in storyboards you should try using the inferred size instead of a certain screen size, that way you don't try to design the interface for a certain screen size.
In code you could do something like this to set proper constraints
scrollView.frame = view.frame //Or I could set the height, width, center contraints
label.translatesAutoresizingMaskIntoConstraints = false
label.centerXAnchor.constraintEqualToAnchor(scrollView.centerXAnchor).active = true
I would try to help with the storyboards but I would need a little more info. I tend to do my constraints in code.

UIButtons not working on certain devices

I've set up a view and constraints using the wAny hAny layout in Xcode 6:
When I run the app on a iPhone 5s and iPhone 6 the UI Elements are all visible but on the 5s the answer and the back buttons do not work - they do nothing.
On the iPhone 6 all the buttons work.
Here's the code for the `back' button:
- (IBAction)backFromDivision:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
The answer code is a bit longer and I won't post it unless necessary.
Does this have something to do with the constraints or should I be looking for answers elsewhere?
Well,
I clicked on "Resolve Auto Layout Issues" - "Reset to suggested constraints" (third from the left):
and that "solved" my problem. Everything is working on all devices. It seems to have removed some constraints that I put in place.
I'm going to have to learn more about why this worked.
I've had a similar issue before. It was the constraints that were the issue and the fact that the button was not in the view's bounds.
I've encountered this issue, and the problem was due to the button having:
buttonView.frame.height = 0. (it can also happen with width)
The problem is that even with no height the button text appears on screen so if you don't have a background the button visual appearance does not change.
To resolve this issue I played with the views Content Compression Resistance Priority(CCRP) of different views to chose which view should resize on smaller screen.
The default CCRP of views is 750, default contraint priority is 1000.
So to match your constraint ios may resize the height of your views

How to handle relative positioning in iOS automatically?

I want to position a sub UIView (sub_UIView) as a subview of parent UIView (parent_UIView). When the parent_UIView resizing/moving, the sub_UIView would stay the relative position of the parent_UIView. For example, the sub_UIView is at the bottom right corner of the parent_UIView. When the parent_UIView is moving, the sub_UIView would stay at the bottom right corner of the parent_UIView automatically.
I managed to do this manually by updating the frame of the sub_UIView when the parent_UIView's frame moving, but how to do this automatically in iOS? are there any properties for this? (similar to autoresizingmask for resizing subviews)
Updated old response, that only mentioned resizing masks
Autolayout (iOS 6)
In iOS 6, autolayout was added, albeit, kinda ugly to work with XCode in a storyboard/xib. Autolayout is way too big to explain, but the essence of it, is that it's a set of rules between views within the hierarchy. Thus, you can stick the x position of a view to the right border of a parent view. See the Auto Layout Programming Guide
Autoresizing Masks (iOS 2)
Take a look at the options in the Size Inspector:
This is the equivalent of doing
myView.autoresizingMask = UIViewAutoresizingMaskFlexibleTopMargin |
UIViewAutoresizingMaskFlexibleLeftMargin |
UIViewAutoresizingMaskFlexibleWidth |
UIViewAutoresizingMaskFlexibleHeight;
Notice that there's a difference between doing it by code and doing it through IB. The IB autoresizing settings for the borders work as struts, selecting the right one, for example, means, "my right border is now stuck to the superview's right border".
On the other hand, code does the opposite, where you need to specify which borders are not strut-ed, aka. they are flexible. Height and Width works normally.
Layout Subviews (iOS 2, but gotchas for iOS5+)
If everything fails, don't start doing manual positioning all over the place, I've been in that position, and it just leads to unmaintanable, spaghetti code, where three or more methods are messing with the same view properties.
Instead, prefer to do all your custom positioning on UIView -layoutSubviews. This method gets called as needed when a setNeedsLayout has been triggered, for example, when the width a view changes, the subviews get the layout subviews event. Also makes implementing rotating interfaces much easier, since this method gets called for the new width, to determine how the animation will look like.
Keep in mind that layout subviews works after Auto Layout/Auto-resizing masks have been performed.

Resources