iOS layouts for web developers #4 - CSS properties replacements
This is the fourth post in the iOS layouts for web developers series. The previous ones were about the controls, control positioning and managing the appearance. This time something more lightweight, I hope. We’ll go through various visual aspects of the controls and see how we can set it up, compared to CSS. Let’s start with the basics - margin and padding.
Box model & controlling controls spacing
There was a lot of confusion in the past in the web world around the box model, i.e. whether the size of an element should be calculated including or excluding its padding and border sizes. Finally, by default, the HTML element size excludes padding and borders (unless we decide to change that using box-sizing CSS property). How does it work in iOS?
Paddings and margins are not useful at all within frame-based positioning - you place the content where you place it, full stop. In the constraints-based layouts (auto layout), though, paddings and margins make a lot of sense. In most cases in iOS these concepts are known as inset and outset and are actually a single value - we get the margin (outset) effect when we use negative value, padding (inset) for positive.
CSS padding <—> inset, -outset
CSS margin <—> outset, -inset
In the context of auto layout, most of the controls has some natural margins (insets) set that are used automatically whenever we’re constraining to the control’s leading space (contrary to the edge). From iOS 8, new UIView property was introduced to control the inset/outset value - layoutMargins. It allows to control the inset (or outset, if value is negative) for each side of the control separately.
CSS padding, margin <—>
UIView’slayoutMarginsproperty
Next thing in the box model is the border. There is no such property directly available on UIView (except for UITextField’s borderStyle property). In order to draw a border around the view, we need to rely on the lower level CALayer and use its borderWidth and borderColor properties.
CSS
border-color<—>layer.borderColor
CSS
border-width<—>layer.borderWidth
So, how about the box model? In iOS, the size comes from the frame property, either assigned or calculated with constraints, regardless of the inset (layoutMargins) values or border size. Both margins and borders are drawn within a frame, so the box model on iOS actually is the same like on IE6, not like in the web nowadays.
box-sizing: border-box<—> the default box model in iOS
the web’s default
box-sizing: content-box<—> N/A in iOS
There is one more thing specific to iOS layouts and controls sizing that should be mentioned here. Apart from the frame, the view has the sibling bounds property. While frame is about the control’s size and location within the superview’s coordinate system, bounds are the values within the control’s own system. It means that in most cases the control’s origin is (0,0), but it changes with rotation, scaling or translations. It makes sense to use the frame property from the context of superview when dealing with its children (when our code runs from the top - like from the view controller - looking down in the view hierarchy). On the other hand, it makes sense to use bounds when dealing with the control’s own view (for example when we’re writing the custom control), as bounds are decoupled from the specifics about where and how the view is rendered at the given point of time.
Other positioning aspects
Overflow is used in the web to control what happens when the content in the element is larger than the element’s expected dimensions. When no dimensions is set, the elements grow with the content, so no overflow applied. Otherwise, one of the tree options applies - either the excess content is visible outside the element’s dimensions (overflow: visible, the default), cut off and invisible (overflow: hidden) or the scrollbars are added to the side of a control to enable scrolling independent from the main window (overflow: scroll and overflow: auto). The two first options are represented directly in iOS with clipsToBounds property of UIView - when set to YES, the content is clipped (cut off), the default is NO, like in the web. There is no direct replacement for automatic scrolls - in case we need that, we need to take care of it manually, creating an additional UIScrollView.
overflow: visible<—>clipsToBounds = NO
overflow: hidden<—>clipsToBounds = YES
overflow: scrollandoverflow: auto<—>UIScrollView
Z-Index. The position of a control on the Z-axis is quite important when we use the absolute positioning (or frame-based positioning in iOS), as controls may arbitrarily cover and be covered by other controls and relying on the order of setting up the control is too brittle to be trusted. That’s why we use z-index value in CSS. In iOS, we have the same thing hidden in the CALayer as zPosition. To avoid the need to go to that level and deal with the fact that view hierarchy is actually not the same as layers hierarchy, we should be using [superview bringSubviewToFront:subview] and [superview:sendSubviewToBack:subview] to achieve the same in imperative, but more powerful manner.
z-indexCSS property <—>layer.zPosition, exposed throughbringSubviewToFrontandsendSubviewToBack
Most of the display CSS property, as known in the web, doesn’t have a lot of sense in iOS layouts, as there is no distinction between inline and block elements and there’s no document flow, as we’ve already learned. The only display value that makes any sense for iOS is none that allows to hide the element without actually removing it from the view. The UIView’s property for this purpose is conveniently called hidden.
display: none<—>hidden
Backgrounds
In iOS, there is concise backgroundColor property that encapsulates most of background styling, much like background in CSS. At a first glance, it seems to be simple and intended for setting plain-colored backgrounds. But it’s actually much more powerful than that. UIColor class can be initialized using colorWithPatternImage method that takes an UIImage instance and the “color” it creates is actually an image that can fill our view’s background with repetition - however strange it might sound. In case we need more control over stretching, repetition etc., we need to defer to “manual” way. We need to add UIImageView as a subview to our view and ensure it is displayed at the bottom on Z-axis.
background-colorCSS property <—>backgroundColorwith plain color
background-imageCSS property withbackground-repeat: repeat<—>backgroundColorwithUIColorcreated throughcolorWithPatternImage
background-imageCSS property with stretching or custom positioning <—>UIImageViewsubview
Moreover, for a reason unknown to me, UITextField has background property specialized for setting and stretching the background image for a text field, so there’s no need to fall back to adding a subview in this case.
background-imageCSS property with stretching on <input> <—>backgroundonUITextField
Text properties
As we discussed in the first post the textual content in iOS may appear only within specialized controls like UILabel or controls supporting NSAttributedString textual values.
NSAttributesString has plenty of options for customizing text appearance, ranging from colors, through underlines and strikethroughs, to effects such as strokes and shadows. Some of the properties are encapsulated within NSParagraphStyle available under NSParagraphStyleAttributeName key in the attributed string.
fontCSS properties combined <—>NSAttributedStringwithNSFontAttributeName
text-alignCSS property <—>alignmentproperty ofNSParagraphStyleAttributeNamevalue inNSAttributedString
text-indentCSS property <—>firstLineHeadIntentproperty ofNSParagraphStyleAttributeNamevalue inNSAttributedString
padding-leftCSS property <—>headIndentproperty ofNSParagraphStyleAttributeNamevalue inNSAttributedString
padding-rightCSS property <—>tailIndentproperty ofNSParagraphStyleAttributeNamevalue inNSAttributedString
line-heightCSS property <—>lineSpacingproperty ofNSParagraphStyleAttributeNamevalue inNSAttributedString
word-wrapCSS property <—>lineBreakModeproperty ofNSParagraphStyleAttributeNamevalue inNSAttributedString
colorCSS property <—>NSAttributedStringwithNSForegroundColorAttributeName
background-colorCSS property <—>NSAttributedStringwithNSBackgroundColorAttributeName
text-decoration: line-throughCSS value <—>NSAttributedStringwithNSStrikethroughStyleAttributeNameset toNSUnderlineStyleSingle
text-decoration: underlineCSS value <—>NSAttributedStringwithNSUnderlineStyleAttributeNameset toNSUnderlineStyleSingle
text-strokeCSS property (not widely supported yet) <—>NSAttributedStringwithNSStrokeColorAttributeNameandNSStrokeWidthAttributeName
text-shadowCSS property <—>NSAttributedStringwithNSShadowAttributeName
UILabel, the control dedicated for text display, supports attributedText, so can handle all of the features for NSAttributedString, but additionally exposes some properties that affect the whole label content, like textAlignment.
fontCSS properties combined <—>UILabel’sfont
colorCSS property <—>UILabel’stextColor
text-alignCSS property <—>UILabel’stextAlignment
word-wrapCSS property <—>UILabel’slineBreakMode
Fancy effects language: en
Some of the “fancy” text effects like text shadows or strokes are available through NSAttributedString we’ve just gone through. There are few more available, though. I think “fancy effects" is not a perfect heading, anyway, but I needed a place to describe few more properties that are available in iOS and are well-known in CSS but I couldn’t fit it anywhere else. So here we go:
opacityCSS property <—>alpha
animationCSS properties family <—> whole bunch ofUIView’s specialized methods
border-radiusCSS property for <input> <—>UITextField’sborderStyleset toUITextBorderStyleRoundedRect
border-radiusCSS property in general <—>layer.cornerRadius
That’s all I found useful and worth mentioning here, but of course the full list of styling-related features of UIKit, and especially when going down to CALayers is vast, likewise the full list of features in CSS, so I don’t think it’s even feasible to cover everything here.
Next time I’ll conclude the series with the post about events handling.
