Simple goal, but it demonstrates situations where Auto Layout gives me a headache. I want to have a stack view with a width of 256pt and a dynamic height based on layout (I shouldn't have to manually specify the height).
Inside it should be an image view sized 64pt x 64pt, which should also be centered horizontally as well as constrained 8pt from the superview's top. Note that the image view isn't the only child, hence why the stack view's height must be sized dynamically.
Auto Layout now tells me there's a conflict between the 256pt width constraint of the stack view and the 64pt width constraint of the image, as well as some mysterious "leading = Image.leading" and "trailing = Image.trailing" conflict which I can't even delete nor find.
Am I missing out something here regarding Auto Layout? I expect all logic to be contained in the interface builder, so no code should be required.
Running Xcode 9.1
Layout image
There is nothing to confuse. iOS clearly telling you the issue.
StackViews take size based on the size of child components (this is called implicit size) unless its been overriden manually as in your case which is 256pt.
Because stackView is just a container for multiple childViews stacked either horizontally or vertically, now because you have added only one imageView to it, it adds the leading and trailing constraint to it which makes absolute sense because you added a single view to the stack of view's , now what should stackView do? stretch childView (in your case imageView) to its own size.
But then you did not allow it because you added width constraint to imageView now when it tries to increase the imageView's width imageView's constraint wont allow it.
Hence it is complaining that there are too many conflicting constraints. Thats all :)
some mysterious "leading = Image.leading" and "trailing =
Image.trailing" conflict which I can't even delete nor find.
You cant delete them because, imageView is the only view inside stackView. Because there is only one child view to stack, stackView will start from left side (leading) to right side (trailing). Because now stackView has its own width it tries to change the width of imageView to reflect the same! But images width constraint prevents it from happening.
What are you trying to achieve with imageView added to stackView. If there is only one view in stackView, adding stackView does not make any sense. Reconsider what you are doing.
Finally, when you have only one childView in stack view, adding horizontal center does not make any sense (no matter vertical/horizontal stackView).
Related
Swift 5/Xcode 12.4
My app uses a UIScrollView to zoom its child UIImageView (which is filled through code). This part works but the ScrollView keeps complaining in XIB that it's missing constraints for the x/y position or width/height.
As you can see, the ScrollView has a top, leading and trailing constraint and the width is set to equal the height. From my understanding it knows its dimensions and position because of these constraints but apparently it's not enough. I do NOT want to center the ScrollView vertically and as the app has to support both iPhones and iPads, I also can't just set a fixed "bottom space to" constraint for the ScrollView.
I already tried to set the ScrollView's width to the ChildView's width and center it horizontally in it but the warning still didn't go away.
I've seen suggestions to put the ScrollView's contents inside another UIView but in my case it's only a single image and if I embed it inside a view, then the zooming breaks.
Why is the ScrollView still missing constraints and how do I fix it (without changing the overall layout of this ViewController)?
A UIImageView (without an image) has no Intrinsic Content Size.
If you're going to be setting its Width and Height constraints via code, but you want to get rid of the IB layout complaints, give the image view a Placeholder:
Edit
If you want the view(s) to be sized in IB, but you're going to change the constraints at run-time, you can either:
Connect the constraint to an #IBOutlet and change the .constant via code (more problematic to change the multiplier if needed, so probably not the way to go)...
Or...
Give the view constraints in IB but set them as Placeholders by checking "Remove at build time":
I am creating a UIScrollView from xib, in which 3 view are there 2 UIViews and in middle an UIImageView. when I am setting constraints Xcode asked to set Y position constrains. But the problem is Y position constraint is blocking Scrollview to scroll down and automatically adjusting the views which looks ugly in landscape mode.
when I am delete that constraint it ask to fix height of subview. I searched a lot but I am new in autolayout so not understanding many of solutions. any help would be great.
You have to set all the height constraints in the content view.
But you also want the height of the Content to be proportional to the screen size.
To do this assign the height constraint of the imageview [equal|proportional|a-computation-of] to the view containing the UISCrollView.
It seems weird to skip levels of herarchy when assigning constraints between two views whose are not direct ancestor/sibling of each other but within a scrollview (at least) it is perfectly acceptable.
You are basically telling the scrollview that it's content has a known size and at same time setting this content to adapt dinamically to the screen size (if the constraints of the root uiview are set correctly)
UIView1
|---UIScrollView
|---UIView2
|---UIImageView [heightConstr.constant=UIView1.height-UIView2.height-UIView3.height-margins]
|---UIView3
This is the basic idea, to be done programmatically, then you can explore other solutions.
Unfortunately the constraint system in ios pretty much sucks when it's up to more complex equations involving more views for a single constraint.
UIScrollViewcan be tricky when adding constraints. You should always add a subView that will behave as the content view for your UIScrollView and all your subsequent views will go inside this content view.
UIView1
|---UIScrollView
|---UIContentView
|---UIView2
|---UIImageView
Set your UIScrollViewconstraints as you would normally but set your content view to have leading, trailing, top and bottom to the UIScrollView but also add two more constraints which will be equal width and equal height to the viewController.view but will have a low priority (So that whichever direction your content will increase in, that constraint will break and automatically increase the content size of the scroll view by taking in the inferred height of the content view). Now go on and add constraints on all your subview as you normally would. Which i'm assuming would be:
Your topmost view will have top and leading and trailing to its superView and also a fixed height.
Your bottom view will have leading, trailing and bottom to its superView and also a fixed height.
Your UIImageViewwill have a leading, trailing and top to top most view and bottom to the bottom view.
Edit:
Here is the screenshot just in case (To show the view hierarchy with the content view's constraints in the inspector)
Hi I am developing small IOS application in which I am using scrollview with auto-layout.Inside scroll I am adding two more views. I am using IB and auto-layout constraints. I am adding two views in side one after another in vertical manner. I have added outer constraints like trailing, leading, top, bottom space. I also added height constraints for both views. Till this everything is working fine.
But my view1 has some dynamic content. For that reason I want to make height constraint greater than equal to instead of equal to.
So how to resolve this problem. Need some help. Thank you.
You should follow the approach below.
First of all, here are some important things about scroll Views which are important for auto layout:
UIScrollView changes its bounds automatically.
UIScrollView needs a content View(in UI) for getting content size for scrolling which works smoothly for auto layout.
UIScrollView's top and bottom constraint should connected to top and bottom layout guide (For most of the cases, not all).
As per your problem:
First Approach: You have UIScrollView, so just insert one UIView inside it and consider it as Content View. After that put your two UIViews inside the UIView (Content View).
So the Hierarchy is: MainView --> UIScollView --> UIView (ContentView) --> firstView & Second View. Now we are going to give constraints to all of them.
For UIScrollView, connect TOP and BOTTOM constraints to TOP & Bottom Layout Guide and LEADING and TRAILING to the Main View.
For UIView (Content View) it is very important to give constraints LEADING, TRAILING, TOP, BOTTOM to the UIScrollView and to give the explicit height (normal height constraint) to your contentView which is appropriate for scrolling (e.g 1200). Also make it horizontally center in container.
Now give constraints to your first view: LEADING, TRAILING, TOP to ContentView and give explicit height (normal height constraint). Don't try to modify it in greater than equal right now - we will do this later. Then, give constraint to second view Leading, Trailing to ContentView, Top to FirstView, Bottom to Content View and Explicit Height (what ever you want). Now, try to modify the Height constraint of first view from equal -> greater than equal - it will definitely work.
Second Approach (Easy and Simple): After giving constraint as per first point, for changing height of first View dynamically you can create an IBOutlet of height constraint of first view to your class and as per your requirement you can change the constraint's constant value (as per you want the height of first view) in any method or button action so it will change in run time. You can also consider it is a fine trick when you want to hide your views so just change their Height constraint's constant to 0 so it will hide and at the time of unhide, again set the constant value of same to desired value so you can also easily play with hide and unhide functionality of view which is little bit difficult in auto layout from other ways.
I would make this constraint as type equals and give it low priority. Then, during adding dynamic content, you may just add another constraint with higher priority.
If you load this UIView from xib just make sure you provide constraint with higher priority.
Suppose that I have the following view controller and this is how I want to see it on all iPhone:
If I run it on iPhone 6 it has the following look:
Here you can notice that UITableView not fit the whole screen and UIImageView doesn't placed at the bottom of the screen.
How can I achieve the required behavior via constraints in XCode 6? I thought that I need the following constraints:
Leading space and top space to container margin for UITableView
Bottom space and trailing space to container margin for UIImageView
Vertical Spacing between UITableView and UIImageView
But it doesn't work as expected even after auto-resolve constraints issues:
Thanks in advance.
Ok, a few things here:
Each view needs enough constraints to define it's x and y position, and it's width and height unambiguously. To start with, go back to Interface builder and delete all of your constraints and lay out the view as you would like it to look. You want to have control over every constraint added, don't let IB automatically resolve the issues, as in all likely hood it won't do what you want.
Do you have an image that is the size you want it to be on screen, once you've factored in #2x, #3x etc? If so, then your job will be easier, as the width and height of the image view can be defined by the width and height of the image (ie the image view's intrinsic content size).
In order to use Autolayout effectively, you need to think about your view holistically, and think about how you want your views to behave when the screen size changes, be clear in your head about the behaviour.
To achieve the layout you want, I would do the following:
Constrain the tableview's leading, top and trailing edges to the superview, with a constant value of 0. This means it can get wider and thinner with the device, it will stretch horizontally, but always stick to the top. This has defined the tableview's x and y position, as well as it's width (height still to go, but keep reading...)
Constrain the image view to match the horizontal centre of it's superview (x position defined) and constrain it's bottom edge to the superviews bottom edge (y position defined). If've you've got the right sized asset, then that will take care of the width and height too. If not, you could go ahead give it explicit width and height constraints.
Now we can constrain the tableview's bottom edge to the top of the image view, with a constant of 0 (ie touching). Note we haven't give the table view an explicit height constraint, so as the device screen grows vertically, the table view will stretch vertically.
Autolayout is hard at first. I'd recommended lots of reading to get over the initial hump, really get to know what a constraint is doing, it's limitations, and the way in which the system parses constraints to translate them into frames. This book is really good, and really helped me learn:
http://www.amazon.co.uk/Auto-Layout-Demystified-Mobile-Programming/dp/0321967194
Best of luck
First make sure you have selected the correct size class. The 'Compact Width | Regular Height' size class must be selected in the Interface Builder. Now add the Trailing space,Leading Space, Top space and Bottom space constraints to the table view. For the image view set the view mode to Aspect fit and add the constraints : Align Center Y ,Top space,Bottom space, Leading space, Trailing space and Aspect Ratio .
I have:
application, that should work in landscape and portrait mode.
view with full-size scroll view on top.
some view inside scroll view with fixed width and height. (with added H and W constraints)
set to view inside scroll view as horizontal centered in container. (added according constraint)
I have warning in interface builder "Has ambiguous scrollable content width".
The only way to fix this problem, that I know - is set trailing and leading constraints.
But for different iPhones (5.5", 4.7", 4") I need to set different trailing and leading constraints.
How can I eliminate this warning and still have centered horizontally view with fixed W and H for all iPhone sizes?
I create Github repo to illustrate this problem: ScrollViewAmbigous
This is not duplicate of UIScrollView Scrollable Content Size Ambiguity
, but it similar (and not answered although), but this question especially related to different sizes of iPhones.
In the morning with a fresh cup of coffee I figured out workaround for this issue!
So, here is the initial state for simplest case:
scrollView with 0 constraints to all edges
Button centered Horizontal and Vertical with fixed Width and Height
And, of course Has ambiguous scrollable content width and Has ambiguous scrollable content height annoying warnings.
All, that we have to do is:
Add 2 additional constraints, for example "0" for trailing and/or bottom space for our view (in my case - UIButton)
Important: you have to add trailing and/or bottom constraints. Not "leading and top" - it's not works!
You can check it in my example project, that demonstrating how to fix this issue: ScrollViewAmbigous
P.S.
I don't know why it works and how Xcode detect which constraint is more prioritised (because I'm not set priority for these constraints explicity), but I'll be thankful if someone explain, why it works in comments below.
Problem :
The warning is telling us that the the content size of the
scrollview depends on its subviews.
while your subview don't have any strict rule of position and size (no fixed constraints) it will confuse nib generated scrollview
content size.
Solution :
Make only one subview as a 'contentView' for scrollview.
Add strict (fixed) constraints to that 'contentView'.
Best practice : equal width and height to its scroll view.
Add all other subviews and constraints to your 'contentView'.
There seems to be a lot of confusion on this issue. My take is that a UIScrollView must have TWO trailing space constraints, an 'inner' one connecting it to one of it's subviews (so it can know its content width), and another 'outer' one connecting it to a sibling or superview so it knows its frame width.
Same principle applies for height, i.e. two bottom space constraints.