General approach for bottom layout guide and different screen formats - ios

So far most, if not all, screens i had developed had 2 kind of behaviours in the context of bottom layout guide:
The content is static and need to be anchored at the bottom:
In this case typically i create a bottom layout guide from the bottom-est vc subview to it's superview (the vc root view, in place of the vc bottom safe area). And this works in both cases if the background color of the bottom aligned view and the root/parent view in the edge of the iphoneX is the same.
the content is scrollable or has to "overflow" or the background color of bottom subview and vc differ. For example a bottom button or tabbar will have to be aligned to the safe area, but their backgrounds will have to "continue" in the iPhoneX to the non-safe-area.
In this case i either add a edge-inset/margin to the views that match the unsafe area (leading to an additional margin to normal squared screens) or i will have to adjust the constraint based on the device type.
In the light of these, is anybody aware of any technique to at generalize/simplify the 2nd point? If find it really ugly to have to add device dependent checks for almost every view controller.
This technically happens already with the "automatically adjust view controller edge insets" but only for scrollviews/tableviews.
TLDR; Is it possible to let a view "stretch" to the non-safe-area in the iphoneX? To obtain basically the same behaviour of the NavigationBar but for the bottom of the screen, without hardcoding anything?
Plus: if it is possible to generalize also including the 1st wanted behaviour with a flag or alike.

The way I typically do this:
Setup the bottom constraint like you have posted
Setup the top constraint relative to the bottom safe area (instead of the superview) with an offset for the height you want the view to be.
This way if there is safe area the view will stretch down naturally and retain its normal appearance on non-safe area phones, you just have to make sure to layout the contents of that view to stay between the safe area and the top. No device-specific code needed.

Related

iPhone X and bottom safe area inset

I am working on layout for iPhoneX and have an issue with action sheets and Safari browser. This is an extra space. I need that my action sheets display more at the bottom ? How can I do that? I guess I need to disable safe area but how can I do it?
The reason why it happens probably that I use 2 UIWindows: one for all content and the second to display banner at the bottom of the screen.
the space that is outlined - is something to hide my app from everyone :) It does not exist in the app!
I want the action sheet be closer to the bottom. It works well on all devices except iPhoneX. I have 2 UIWindows (one for all content and the second one for ad at the bottom). So, for iPhoneX the content window has probably some safe area inset and becaise of that action sheet shows not from the bottom edge
You don't have to disable the Safe Areas for the View Controller. They are still useful for the top, trailing and leading edges.
To remove the grey bar all you need is to set the bottomAnchor or your containing view to your superView instead of the bottom safe area with a constant of what you need.
You can also further adjust safe area insets with additionalSafeAreaInsets. https://developer.apple.com/documentation/uikit/uiviewcontroller/2902284-additionalsafeareainsets That allows you to leave them as is for the edges that you don't want to modify.

iPhone X: unsafe area doesn't get color of the nearst view

my app is built with custom top bar instead of the UINavigationBar to satisfy some requirements that cannot be done easily with the default UINavigationBar, however, after reading the answers of this question, and changing the 3 constraints of this top bar (top, leading & trailing) to be relative to the safe area instead of the superview, I couldn't get the safe area to color itself automatically to match the color of the top bar, here is what it looks like now:
the first answer in the link above suggests to manually color the status bar but unfortunately this is unreasonable and it uses a private API to do so. and the second answer suggests to provide a background color for the main view but it's also unreasonable because I need a different color for the main view.
how can I let the unsafe area to color itself according to the nearest view which is connected to it with auto layout constraints ?
Here is an example of the safe area layouts on your iPhone X.
https://1.bp.blogspot.com/-nKGb8plVgd8/Wdir-wLm6tI/AAAAAAAADmo/z3G6lWBlomASUtRJ6COYYKPdPC6KdMlBACLcBGAs/s1600/safe_area.png
If you want that your "unsafe top area" gets the same color of the nearest view color, you have two logical choices.
By using two views, one on the top with a top constraint related to the superview (according to the SafeAreaLayout activation on your storyboard) and the other just beyond the latter with its top constraint linked to the top safe area. After that, link them to your viewcontroller and manage the color into your viewDidLoad method.
The other choice is probably easier by using only one view. Set its top, leading and trailing constraints to the Superview (and not the safe area layout). To avoid an interface builder error, set a fixed height to your view.
It should work properly now.

Auto layout space between elements relative to screen height [duplicate]

I understand the old Struts and Springs method of aligning, sizing and distributing views in Interface Builder. However, I cannot seem to figure out how to evenly distribute views using auto layout with Xcode 5. There was a way to do it using Xcode 4, but that option is gone.
I have 7 buttons arranged in a vertical stack. On a 3.5" layout, it looks great. When I preview the screen in the 4" layout, all of the buttons remain tightly packed and there is a large amount of space below the last button.
I want them to stay the same height, but I want the space between them to be able flex so they can spread out across the screen.
I've been able to get the height of the buttons to flex and fill the space, but that is not my desired behavior. I would like to learn how to use Auto Layout to replace my old Springs behavior, but I can't seem to find any way to do it through Interface Builder.
I'm ok with the top button either being a fixed space from the top edge or a proportional space from the top edge, likewise for the bottom button and the bottom edge. Those are less important to me, I'm good with either.
But I really need to figure out how to evenly distribute the extra space between each of the items in the view.
EDIT Note that in iOS 9 this technique will become unnecessary, because a UIStackView will perform distribution automatically. I'll add another answer explaining how that works.
How to Perform Even Distribution Using Autolayout
The simplest way to do this in Interface Builder alone (rather than constructing constraints in code) is to use "spacer" views:
Position the top and bottom buttons absolutely.
Place spacer views between all the buttons. Use constraints to position them horizontally (centering them horizontally is simplest) and to set their widths.
Make constraints between each button and the spacer view above and below it, with a Constant of 0.
Now select all the spacer views and set their heights to be equal.
The first screen shot shows me setting this up in IB:
I have deliberately not corrected for the "misplaced views" because I want you to see what it looks like while I'm designing the constraints. Here's the result on both a 4 inch and a 3.5 inch screen:
I have left the spacer views black, just to show you how this technique works, but of course in real life you would make them transparent and hence invisible! So the user sees just your buttons, evenly distributed on either height of screen.
The reason for the use of this technique is that although the notion of equality performs the distribution of values you are asking for, constraints can apply equality only between aspects of views; thus we need the extra views (the spacer views) so that we have things we can make equal to other things (here, the heights of the spacer views).
Other Approaches
Obviously, a more flexible approach is to assign the constraints in code. This may sound daunting, but there's a lot of third-party code out there to help you, such as this sort of thing.
For example, if we have a (possibly invisible) superview whose height acts as a boundary to dictate maximum vertical distribution of our four buttons, we can pin their tops to the vertical center of that superview with a constant of 0 but a multiplier of 0.000001, 0.666667, 1.33333, and 2.0 respectively (if we have four buttons); now the buttons will stay vertically distributed even as the superview changes size in response to screen height or whatever. [In Xcode 5.1, it will be possible to set that up in Interface Builder, but in earlier versions of Xcode it is not possible.]
In iOS 9 / Xcode 7 this problem will be trivially solved in IB. Simply select the buttons (or whatever it is you want to distribute vertically) and choose Editor > Embed In > Stack View. Then you simply configure the stack view:
Provide constraints that position and size the stack view itself. For example, pin the four edges of the stack view to the four edges of its superview.
Set the stack view's attributes. In this case we want Vertical axis, Fill alignment, Equal Spacing distribution.
That's all! However, you may be curious about how this works, because it is still possible to do the same thing manually in code. A stack view performs distribution, not by inserting spacer views, but by inserting spacer guides. A guide (a UILayoutGuide) is a lightweight object that behaves like a view for purposes of layout constraints, but is not a view and therefore doesn't have to be made invisible and doesn't carry any of the overhead of a view.
To illustrate, I'll do in code what the stack view is doing. Presume we have four views to distribute vertically. We assign them constraints for everything but their distribution:
They all have absolute height constraints
Their left is pinned to the superview's left, and their right is pinned to the superview's right
The top view's top is pinned to the superview's top, and the bottom view's bottom is pinned to the superview's bottom
Now, presume we have references to the four views as views, an array. Then:
let guides = [UILayoutGuide(), UILayoutGuide(), UILayoutGuide()]
for guide in guides {
self.view.addLayoutGuide(guide)
}
NSLayoutConstraint.activateConstraints([
// guide heights are equal
guides[1].heightAnchor.constraintEqualToAnchor(guides[0].heightAnchor),
guides[2].heightAnchor.constraintEqualToAnchor(guides[0].heightAnchor),
// guide widths are arbitrary, let's say 10
guides[0].widthAnchor.constraintEqualToConstant(10),
guides[1].widthAnchor.constraintEqualToConstant(10),
guides[2].widthAnchor.constraintEqualToConstant(10),
// guide left is arbitrary, let's say superview margin
guides[0].leftAnchor.constraintEqualToAnchor(self.view.leftAnchor),
guides[1].leftAnchor.constraintEqualToAnchor(self.view.leftAnchor),
guides[2].leftAnchor.constraintEqualToAnchor(self.view.leftAnchor),
// bottom of each view is top of following guide
views[0].bottomAnchor.constraintEqualToAnchor(guides[0].topAnchor),
views[1].bottomAnchor.constraintEqualToAnchor(guides[1].topAnchor),
views[2].bottomAnchor.constraintEqualToAnchor(guides[2].topAnchor),
// top of each view is bottom of preceding guide
views[1].topAnchor.constraintEqualToAnchor(guides[0].bottomAnchor),
views[2].topAnchor.constraintEqualToAnchor(guides[1].bottomAnchor),
views[3].topAnchor.constraintEqualToAnchor(guides[2].bottomAnchor)
])
(Obviously I could make that code cuter and shorter using loops, but I have deliberately unrolled the loops for clarity, so that you can see the pattern and the technique.)

Auto Layout Not So Auto

I have the most basic set up possible. See pic 1:
Believe it or not this is my first project using AutoLayout, I have created everything prior programatically. This basic set up is literally a UIWebView with 1 custom UIView positioned at the bottom. Previously I was using a tool bar that handled everything for me and had no issues with constraints whatsoever. However, the tool bar created discrepancies for event handling when adding a UILongPressGesture to the subview of the UIBarButtonItem so I decided to convert the tool bar to a UIView (Even inserting a UIView into a tool bar, it naturally converts to a button item) for easier handling. But run-time, the view gets pushed off screen by half of the UIView size (48px) See Pic 2. Then when I add buttons, it just gets worse:
I have reviewed the documents and the support HERE with no results. I've spent about 24 hours in total researching it and learned a lot, so my efforts aren't in vein. I KNOW by 'Adding Missing Constraints', the constraints are just recommendations based on the current set up, they aren't concrete in all cases, so I did try to create my own with control drag after reviewing the documents but my set up concluded with no different results, and exponentially more sloppy. So I decided to include the populated constraints by Xcode below :
UIWebView Constraints
Custom UIView (toolBar) Constraints
Any solid starting point recommendations? Does Intrinsic Size have anything to do with it?
EDIT : WORKING CONSTRAINTS I didn't realize you could simply omit a constraint. It seems the culprit was adding one to the top layout guide.
Just for answerer #Matt :
Constant 0 result : there are small gaps at edges
-16 for leading space/trailing space results as a true toolbar emulation in my case with no outstanding warnings or issues. Thanks
Let's talk about the view at the bottom of your interface and how you would use auto layout to position and size it the way a toolbar would be positioned and sized.
To use auto layout, you need to supply sufficient info to determine both position and size. This view is a subview of the view controller's main view. The main view will be resized depending on the screen, so we want to use auto layout to resize the subview ("toolbar") as well. This is what auto layout is for!
So constrain subview leading edge to the leading edge of the superview, and constrain subview trailing edge to the trailing edge of the superview, both with a constant of 0. Now the right and left edges match the superview!
That takes care of horizontal position and size.
Now let's talk about vertical position. The position should be the bottom. So constrain subview bottom edge to the bottom layout guide of the view controller, again with a constant of 0. Now the bottom of the view is at the bottom!
The only thing we don't know is the top of the subview. This, in our situation, is the same as knowing its height. So give the subview a height constraint, set its constant to a reasonable value like 40, and you're done.

Centering View between neighbors using autolayout in IOS

I am attempting to layout a screen design using constraints (auto-layout) from interface builder and hope to avoid coding constraints, but I would accept an answer for either case I guess.
I have two subviews in a contained in a top level view. I want to fix vertical distance of the first subview to the top of the screen (I was able to accomplish that - common use case for a vertical space constraint). I want the second subview to float (vertically) in the center of the distance between the bottom of first subview and the bottom of the screen. The idea is that the design responds somewhat to iPhone 3.5" vs 4" dimensions.
I am having trouble defining a constraint or set of constraints that would accomplish this.
I have tried setting inequalities on the vertical spacing constraints between the second subview and the top of it's neighbor (the first subview) and the bottom of the superview, and playing with the priorities of those constraints.
An idea that was suggested in a few other related posts on SO is to use a container that is pinned to the bottom of the screen, as well as the pinned to the bottom edge of the first subview, and then center my second subview in the container. I was hoping to avoid complicating the view hierarchy if possible, but maybe that isn't easily avoided.
Any thoughts or suggestions?
UPDATE
This answer describes how to do this in Xcode 6.0 (and probably works in Xcode 5.0 and 5.1).
ORIGINAL
If you want to do this with auto layout, you have to add at least one spacer view. It doesn't have to be the superview of the vertically-centered view (or any other view), so it has a pretty minor effect on your view hierarchy.
I recommend not trying to set up constraints in Interface Builder in Xcode 4.6.3. It's just too painful. But if you really must, this is doable.
Create a spacer (a plain old UIView) from the bottom edge of the top-hugging view to the bottom edge of superview. Set it to hidden. Give it constraints to the top-hugging view and the left and bottom edges of the root view, and pin its width. My spacer width is 10:
Add your middle view (here, a button). Give it a “Horizontal Center in Container” constraint. Then select both the middle view and the spacer view and give them a “Vertical Centers” constraint:
Note that if you drop the middle view when IB is showing the correct guidelines, it will set these constraints for you.
You can test by enabling resizing for descendents (but not siblings & ancestors) and resizing the root view in IB. The middle view will remain centered between the top-hugging view and the bottom of the superview:

Resources