I am trying to achieve a layout of buttons very similar to Apple's inbuilt Calculator app:
I am using autolayout to position the buttons, and drawing a 0.5px border on each button, aiming for a 1px gap between buttons (like the calc app, closeup showing pixels above, from a retina device).
Border applied by :
btn.layer.borderWidth=0.5f;
btn.layer.borderColor=[[UIColor blackColor] CGColor];
and the layout is set up using Interface Builder.
It almost works; but there is some variability in the gap between buttons - for example the gap between the "1,2,3,thru" row and the "4,5,6,and" row is 2 pixels, but the gap between the "7,8,9" and "cl,0,#" rows is only one pixel.
Constraints are :
Top black area has a fixed height
All buttons same height
In the numeric rows, numeric buttons are set to same width, THRU, AND, ALL OFF and ENTER buttons are set to same fixed width
Top row of buttons pinned to bottom of black area, bottom row pinned to bottom of superview
In IB the buttons are all positioned butting up against each other.
In interface builder and at runtime I don't see any autolayout errors; and a debugger dump of the autolayout info gives me:
po [[UIWindow keyWindow] _autolayoutTrace]
*<UIWindow:0x10908a560> - AMBIGUOUS LAYOUT
| *<UILayoutContainerView:0x109136140>
| | *<UINavigationTransitionView:0x10908ef80>
| | | *<UIViewControllerWrapperView:0x109138870>
| | | | *<UIView:0x1090e6d10>
| | | | | *<UILabel:0x1090d5bb0>
| | | | | *<UIButton:0x1090d27e0>
| | | | | | <UIButtonLabel:0x1091c9190>
| | | | | *<UIButton:0x1090844c0>
| | | | | | <UIButtonLabel:0x1091c7990>
| | | | | *<UIButton:0x109088b60>
| | | | | | <UIButtonLabel:0x1091c6190>
| | | | | *<UIButton:0x1090e2ce0>
| | | | | | <UIButtonLabel:0x1091c4990>
| | | | | *<UIButton:0x1090e4e50>
| | | | | | <UIButtonLabel:0x1091c3190>
| | | | | *<UIButton:0x1090db730>
| | | | | | <UIButtonLabel:0x1091c1990>
| | | | | *<UIButton:0x109068f50>
| | | | | | <UIButtonLabel:0x1091c0190>
| | | | | *<UIButton:0x10906db80>
| | | | | | <UIButtonLabel:0x1091be990>
| | | | | *<UIButton:0x1090d23a0>
| | | | | | <UIButtonLabel:0x1091bd190>
| | | | | *<UIButton:0x1090c8520>
| | | | | | <UIButtonLabel:0x1091bb990>
| | | | | *<UIButton:0x1090c9090>
| | | | | | <UIButtonLabel:0x1091ba190>
| | | | | *<UIButton:0x1090c5680>
| | | | | | <UIButtonLabel:0x1091b8990>
| | | | | *<UIButton:0x1090d7fa0>
| | | | | | <UIButtonLabel:0x1091b7190>
| | | | | *<UIButton:0x1090cc820>
| | | | | | <UIButtonLabel:0x1091b5990>
| | | | | *<UIButton:0x10909dda0>
| | | | | | <UIButtonLabel:0x1091b4190>
| | | | | *<UIButton:0x1090c8090>
| | | | | | <UIButtonLabel:0x1091b2990>
| | | | | *<UIButton:0x1090c8e30>
| | | | | | <UIButtonLabel:0x1091b1190>
| | | | | *<UIButton:0x1090d5390>
| | | | | | <UIButtonLabel:0x1091af990>
| | | | | *<UIButton:0x10d30d260>
| | | | | | <UIButtonLabel:0x1091ae190>
| | | | | *<UIButton:0x10909a6a0>
| | | | | | <UIButtonLabel:0x1091ac990>
| | | | | *<UIButton:0x1090cca40>
| | | | | | <UIButtonLabel:0x109161160>
| | | | | *<UIButton:0x10d30ea90>
| | | | | | <UIButtonLabel:0x109156460>
| | | | | *<UIButton:0x1090da9e0>
| | | | | | <UIButtonLabel:0x109177ad0>
| | | | | *<_UILayoutGuide:0x1090e6dd0> - AMBIGUOUS LAYOUT
| | | | | *<_UILayoutGuide:0x1090ce080> - AMBIGUOUS LAYOUT
| | <UINavigationBar:0x10907da60>
| | | <_UINavigationBarBackground:0x109087240>
| | | | <_UIBackdropView:0x1090899f0>
| | | | | <_UIBackdropEffectView:0x10908b110>
| | | | | <UIView:0x10908bbb0>
| | | | <UIImageView:0x1090877a0>
| | | <UINavigationItemView:0x10916bc30>
| | | | <UILabel:0x10917f810>
| | | <UINavigationItemButtonView:0x1090ee060>
| | | | <UILabel:0x1090ee5d0>
| | | <_UINavigationBarBackIndicatorView:0x109098c80>
Can anyone offer any hints on how to achieve a "pixel perfect" layout like this with Autolayout? Or should I be doing this programatically instead?
Layer borders are drawn inside the views bounds, so you aren't going to see a "gap" between the buttons, but the effect of two borders abutting each other. This isn't going to work in the non-retina simulator or devices, since it can't draw a half-pixel, and Autolayout doesn't align views on half-pixels.
For a layout like the one above I'd be giving a fixed height to the buttons (or to one button, and making the others equal it) and letting the black view take up the remaining space. It's always best to have at least one (ideally, exactly one) variable element in a full-screen layout basically to cover rounding errors.
It sounds at the moment like you have one fixed element (the black view) and the buttons are basically "take the remaining height and divide it between yourselves", which will only work if the remaining height divides perfectly.
I tried to set horizontal/vertical spaces between views equal to 0.5. The color of parent view acts as border color. It looks ugly in IB, it cannot layout views properly but resulting layout on device looks perfect.
In interface builder:
On simulator:
Constraints are a bit tricky. For each row:
set height for the 1st button
set equal height for all buttons
set equal width for all buttons (a bit different for rows with orange buttons)
align top edges of all buttons
align bottom edges of all buttons
set leading and trailing spaces between all buttons (including spaces to superview) to 0.5
set top space (to previous row) of 1st button to 0.5
You can apply constraints to multiple items at once. In order to apply leading/trailing spaces between buttons, place them without intersection in desired order before applying.
Related
Layout situation:
I have a UICollectionView with multiple cells (arranged as a pager).
A cell contains a full size UIScrollView (A) with content size is equal to it's frame size.
This scroll view contains a second UIScrollView (B) with a content size larger then its frame size, means it is scrollable.
The problem is that a UIPanGesture issued in the inner scroll view (B) does not issue paging on the UICollectionView when the pan gesture hits the bounds of the inner scroll view (B).
I already figured out that this is because of the scroll view (A) is not scrollable because its frame size equals the content size.
Any idea how to achieve paging/scrolling on the collection view when a pan gesture is issued on the inner scroll view (B)?
I cannot replace the scroll view (A) as the scroll view is needed when the device is rotated.
UICollectionViewCell
------------------------------------------------------------
| ----------------------------------- |
| | UIScrollView (A) | |
| | (ContentSize = FrameSize) | |
| | | |
| | ------------------------------- | |
| | | UIScrollView (B) | | |
| | | (ContentSize = FrameSize * 2) | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | ------------------------------- | |
| | | |
| | | |
| ----------------------------------- |
------------------------------------------------------------
I want help with my UIScrollView:
I have implemented my UIScrollView in my project and everything works fine but I have problems when Im trying to implement zoom, but this is not as the zoom when we are looking a picture. I am trying to zoom out. My app begins as this graphic when it launches. Lets suppose that I have a UIView in the screen which is bigger than my screen.
--------------------------------------------
| | | |
| | | |
| -----|----------------------|---------|
| | | | |<---------UIView
| | | |<------------------ iPhone Screen frame
| | | | |
| -----|----------------------|---------|
| | | |
| | | |<-------- ScrollView Content size
| | | |
| | | |
| | |<----------------- ScrollView Frame Size
| | | |
| | | |
| | | |
| | | |
| | | |
| ------------------------ |
| |
| |
| |
--------------------------------------------
And I want my screen look like this when I zoom out.
------------------------
| |
| ------------------|
UIView-------->| |
| | |
| ------------------|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
------------------------
How can I do it?
Set scrollView.minimumZoomScale = 0 then you can zoom out as much as you want. If you don't want to zoom out too much, give it a reasonable value.
I am trying to manage the views on my screen so that all views are updated in accordance with a text view that is growing depending on the text that is entered.
On my controller's view i have two subviews. Let's call them view1 and view2. View1 has a uitextview subview that is growing and shrinking depending on the text that is entered.
The issue I'm having is that view1 and view2 don't to grow and shrink whilst typing. The constraint constant for vertical spacing from the bottom on the textView has been set to 10px from it's superview. Vertical spacing between view1 and view2 is set to 0. Here is a visual representation of the view:
---------------------
| |
| ----------------- |
| | UITextView | |
| ----------------- |
| view1 |
---------------------
| |
| |
| |
| |
| view2 |
| |
| |
| |
| |
| |
---------------------
The result I'm getting whilst I add text to the textView is:
---------------------
| |
| ----------------- |
| | UITextView | |
| | | |
| | | |
---------------------
| |
| |
| |
| |
| view2 |
| |
| |
| |
| |
| |
---------------------
But the desired behavior would be something along the lines of:
---------------------
| |
| ----------------- |
| | UITextView | |
| | | |
| | | |
| | | |
| | | |
| | | |
| ----------------- |
| view1 |
---------------------
| |
| |
| view2 |
| |
| |
| |
---------------------
The strange thing is, once I've added the text and pressed submit (not added to diagram for simplicity), view1 grows to the correct size, as does view2, but not whilst typing.
Do I need to explicitly call setNeedsUpdateConstraints or updateConstraintsIfNeeded? If so, where? I have tried calling it from the text view delegate textViewDidChange: but nothing has helped.
Any help would be much appreciated.
I managed to figure this out.
For anyone reading this having the same issue, the way I did it was to write my own subclass of UITextView giving it a height constraint and a few delegate methods.
When changing the height of the text view, simply fire the delegate method (e.g. textViewDidChangeHeight:). Then, in the delegate call back implementation, I just called sizeToFit on the view.
I have a navigation view controller, this view has only main view and when I trace auto layout I see this:
*<UIWindow:0x8a9de40> - AMBIGUOUS LAYOUT
| *<UILayoutContainerView:0x8bb7a50>
| | *<UINavigationTransitionView:0x8bbbfd0>
| | | *<UIViewControllerWrapperView:0x8bc55c0>
| | | | *<UIView:0x8bc4670>
| | | | | *<_UILayoutGuide:0x8bc46d0> - AMBIGUOUS LAYOUT
| | | | | *<_UILayoutGuide:0x8bc4990> - AMBIGUOUS LAYOUT
| | <UINavigationBar:0x8a94480>
| | | <_UINavigationBarBackground:0x8a9c790>
| | | | <_UIBackdropView:0x8bb82c0>
| | | | | <_UIBackdropEffectView:0x8bb7b60>
| | | | | <UIView:0x8bba5d0>
| | | | <UIImageView:0x8a9ca90>
| | | <UINavigationItemView:0x8a98370>
| | | | <UILabel:0x8a987e0>
| | | <_UINavigationBarBackIndicatorView:0x8bc1d00>
Does somebody know why UILayoutGuide is ambiguous?
The layout is ambiguous because of timing. If you run the trace after the view is visible, the ambiguity is resolved. https://stackoverflow.com/a/19129807/568295
The underlying "why" is an implementation detail. I would guess they are returning UIViewNoIntrinsicMetric for the intrinsicContentSize for the placeholder views for the top and bottom layout guides until after the navigation bar is displayed.
I originally tried doing this with two UICollectionViews stacked on top of each other. However getting it to scale right was nightmare with Auto-layout (I'm a novice iOS developer so that is more a critique of my skill than Auto-layout). I'd like to stack two sections of UICollectionView cells on top of each other like this:
+----------------------+
|+-Section 0---------->|
| +-------+ +-------+ |
| | | | | |
| | | | | |
| |Cell 0 | |Cell 1 |+->
| | | | | |
| | | | | |
| +-------+ +-------+ |
+--Section 1---------->|
| +-------+ +-------+ |
| | | | | |
| | | | | |
| | | | | |
| |Cell 0 | |Cell 1 |+->
| | | | | |
| | | | | |
| +-------+ +-------+ |
| |
+----------------------+
I'm using a custom Flow Layout, but I'm not even sure where to begin breaking my current 2 sections to stack like this. Currently my sections are stacked like this:
+--------(Offscreen)
|
+
+--------xxxx--------+v
| Section 1 | Section 2
| +-----+ +-----+ | +-----+ +-------+
| | | | | | | | | |
| | | | | | | | | |
| | | | | | | | | |
| | | | | | | | | |
| +-----+ +-----+ | +-----+ +-------+
| |
| |
| +-----+ +-----+ | +-----+ +-------+
| | | | | | | | | |
| | | | | | | | | |
| | | | | | | | | |
| | | | | | | | | |
| +-----+ +-----+ | +-----+ +-------+
| |
| |
| xxxx |
+--------xxxx--------+
UICollectionView can scroll either vertical or horizontal not both. If you have only two sections like in the diagram you can use two collection view. But it better to think of a design like a table view contains collection views in each cells. So that the design will be flexible(Horizontal and vertical scrolling with any number of sections). Anyway the design is little bit complex, you should customize the table view cell to put collection view in it. Now the UITableView will give you vertical scrolling with the ability to scroll each rows horizontaly(UICOllectionView)