We were looking to build an app that would be used as an online pharmacy application. We really appreciated the high degree of integrity and technical savviness that Atimi brought to the table. We were also impressed by their portfolio – I mean they've done some of the most premium apps in the world. In addition, they're customer-centered and client-focused, and we feel really taken care of. We’re proud of our app that has a modern user interface that's easy to use and nicely designed. If you’re interested to work with the biggest and longest standing app company in Vancouver, look no further.
- M.M., Start-up, Pharmaceutical
We started a new mobile development project and needed people that had experience developing mobile apps and had a proven track record with some larger firms. The team at Atimi not only provided technical skills but they also provided guidance as to HOW we should build things in the industry. We're not technical people and really appreciated them providing transparency. They gave us a lot of insights and guidance in design and application. Our app was state of the art. We got tremendous feedback from our users. We even got nominated for an industry award. I would highly recommend Atimi to guide any organization in creating their app.
- A.D., , Fortune 500 Company, Finance
We were looking for someone who could execute on an app. This was a new area for us and we needed a team that could guide us as far as best practices, knowing the industry well, and having had good clients where they had been successful. We also wanted to work with a company on a long-term basis. With Atimi, we got a sense that they really knew what they were talking about, and they would be great partners to guide us down this new avenue. They gave great data to back up their perspective and they made great suggestions right off the bat. Their expertise and communication have been the biggest benefit in working with them. They've been very consistent in the project management, and they've kept us on track. If you’re looking for a partner that can deliver a long-term solution and can help you grow as technology evolves and changes, give Atimi a call – they're fantastic!
- E.T., Fortune 200 Company, Construction
Our IT team was looking for staff augmentation for mobile and web development and automated testing. It was to support a project we were working on to fill the gaps we were experiencing. Atimi had a good history of delivering based on their promises. We needed handholding and Atimi did that for us vs. others who did not. They contributed to our strategy development and improved our test coverage. They’re reliable, tech proficient, have the ability to work independently, and deliver great communication skills. They have helped us surpass the goals my management had promised our executives. For that, we are incredibly grateful.
- T.K., Fortune 50 Company, Retail
The best in the city! From day one, Atimi has seen through our vision and offered solutions that will allow us to disrupt the market. We want to scale faster than humans can scale - that's why we invested in building an app. We want to do MORE with what we have. Since we do not have programmers internally, we were looking for a reliable team to build an app that was effective for us. The Atimi team was not only been our partner, but they also challenged us on some of our thinking and as a result it has elevated the project. They want to see our project be successful as much as we do. It's worry-free to work with them. With Atimi, you're in good hands.
- J.F., Top 400 Growing Companies (Canada), Staffing
We were looking to build an app that would be used as an online pharmacy application. We really appreciated the high degree of integrity and technical savviness that Atimi brought to the table. We were also impressed by their portfolio – I mean they've done some of the most premium apps in the world. In addition, they're customer-centered and client-focused, and we feel really taken care of. We’re proud of our app that has a modern user interface that's easy to use and nicely designed. If you’re interested to work with the biggest and longest standing app company in Vancouver, look no further.
- M.M., Start-up, Pharmaceutical
We started a new mobile development project and needed people that had experience developing mobile apps and had a proven track record with some larger firms. The team at Atimi not only provided technical skills but they also provided guidance as to HOW we should build things in the industry. We're not technical people and really appreciated them providing transparency. They gave us a lot of insights and guidance in design and application. Our app was state of the art. We got tremendous feedback from our users. We even got nominated for an industry award. I would highly recommend Atimi to guide any organization in creating their app.
- A.D., , Fortune 500 Company, Finance
Cap Height Alignment for iOS Auto Layout
Many times graphic designs include the need to align text vertically at cap height (the top of the capitals) and baseline. For example, someone displaying a list of news articles might want to align the cap height of each article title and the baseline of the associated description with the thumbnail image (as shown in the figure below). iOS Auto Layout already supports baseline alignment for vertical (as well as horizontal) layout, but there is no built-in mechanism to deal with cap height. This article will show you how this can be done with a simple subclass of UILabel.
A Short lesson in iOS Typography
Before getting into implementation, let’s just run through the terminology that will be needed. The figure below is taken from Apple’s Text Programming Guide for iOS and shows the various vertical measures used in layout.
The important terms (along with their UIFont property names) for this discussion are:
• Line height (lineHeight) is the distance from one line of text to the next;
• Baseline is the vertical origin of the text, that is, the line upon which the characters stand;
• Ascent(ascender) is the distance from the baseline to the top of the text cell (including space for accents and the like);
• Descent(descender) is the distance below the baseline to the bottom of the text cell;
• Cap height(capHeight) is the distance from the baseline to the top of the capital letters.
Because of the additional space for accents and the like, a font’s ascent is typically higher than its cap height. Therefore, even when a UILabel (or other text view) is sized to hug its content, it will still have a gap between the top of the view and the top of the text.
Curiously, the diagram shows space below the descent for line gap (leading), but there is no corresponding property in UIFont—there is a “leading” property, but it is synonymous with the line height property. Having surveyed all the fonts currently available on iOS, the line gap appears to be universally zero and so will be ignored for the rest of this discussion.
Changing the Meaning of “Top”
It may come as a surprise, but when Auto Layout does alignment it does so against a view’s alignment rectangle and not simply its edges. By default, this alignment rectangle is the same as the view’s frame, but it can be different. For example, suppose a view displays an image within some custom border. It may be more appropriate to align layout against the image and not include the border.
We can use this feature on the labels for which we want cap height alignment. UIView defines a number of methods that can be used, but the simplest is alignment RectInsets, which returns a UIEdgeInsets specifying the insets of the content with respect to the frame. The default implementation returns UIEdgeInsets.zero but if we subclass UILabel we can override it as follows:
This simply defines the top margin to be the gap between the ascender and the cap height of the label’s font, causing Auto Layout to shift the meaning of “top alignment” into the view so it rests on the cap height instead.
This is all that is needed for basic functionality. Wherever cap alignment is required, just replace the UILabel with its subclass and Auto Layout top alignment will align with cap height instead—well, approximately so; see below for a more accurate calculation of cap height positioning.
This simply defines the top margin to be the gap between the ascender and the cap height of the label’s font, causing Auto Layout to shift the meaning of “top alignment” into the view so it rests on the cap height instead.
Of course, having to switch the class of the label whenever we want to change alignment could get annoying, particularly if a graphic design requires a dynamic switching from top to cap height. To support this, we can introduce a boolean property (alignCapHeight) to control the type of alignment. Our revised alignment RectInsets now looks like:
As the alignment method is only called during layout, we also need to ensure that Auto Layout is rerun whenever the property changes:
Working With IB
Great, but wouldn’t it be nice if the new vertical alignment showed up in IB? Well, that’s easily fixed: just add the @IBDesignable designation to the class definition as follows:
This tells IB that the class will co-operate with it to display correctly. In our case, this just means that IB will instantiate the label object and execute the alignmentRectInsets method so the positioning by IB’s Auto Layout matches the runtime.
Likewise, we can surface our align CapHeight property to IB using @IBInspectable
@IBInspectable var alignCapHeight: Bool = false
This makes the property accessible in the attributes inspector (per the screenshot below). This is pretty cool—as you change the Align Cap Height attribute, IB will automatically update the layout so you see the impact in realtime.
A More Accurate Cap Height
In the code above, we approximated the cap height position by calculating the rounded difference between the ascender and the cap height. However, this is a very naive approach, given how text is actually rendered, and so the method is only accurate to ±1 point. To understand why this is so, and how to calculate a more accurate position, we need to work through how the text is actually rendered in a UILabel.
Suppose we have a label that is using a 24pt Helvetica font. That font will have the following metrics:
The fractional part of each number is important. When the font engine renders the glyphs it uses anti-aliasing to simulate the fractions of pixels, as shown in the enlarged image below. The other point to note is that glyphs are always laid out with respect to the baseline, and so the baseline is always on a pixel boundary.
Starting with lineHeight, we can see that it is the sum of the ascender and descender. However, as baseline is always on a pixel boundary, the actual line height of the rendered font must be an integral number of pixels. Also, as fractional parts are simulated through anti-aliasing, the line height cannot be rounded to the nearest pixel without risking the last partial pixel being clipped. Therefore, the calculated line height will be ceil(lineHeight).
This same principle holds for ascender and descender. Each may have a fractional part that needs to always round up to the next pixel. However, there is a catch, ceil(lineHeight) does not always equal ceil(ascender) + ceil(descender). Sometimes the calculated line height is one pixel shorter than the sum of the calculated parts. In such cases, the descender is given precedence on the basis that many common glyphs have descenders, whereas only a few diacritical marks touch the top of the ascender.
To find the position of the baseline within the label, we should move down by the calculated line height and then back up by the calculated descender. (Point of detail, for UIFont the descender is always expressed as a negative value because the Y access is “up”—all calculations therefore need to take this into account.)
With the baseline calculated, we can move up using the capHeight. However, in this case we actually do want to round to the nearest pixel as the visual position will depend on whether the fractional pixel is more or less than 50% opacity.
This gives us a more accurate adjustment calculation of:
ceil(lineHeight) – ceil(-descender) – round(capHeight)
However, there is one more consideration. All the metrics are in points but the rounding occurs at the pixel boundary. Therefore, the final calculation needs to handle the screen scaling as follows:
(ceil(font.lineHeight * scale) – ceil(-font.descender * scale) –
round(font.capHeight * scale)) / scale
Here is the final version of the code:
Final Considerations
What has been presented here works for UILabels, but the technique can also be applied to UITextField and UITextView. Also, it could be extended to support UILabels that use attributed strings (though it would be difficult to achieve a general solution for multi-line, multi-font labels).
It does not handle labels where the bounds do not hug the content. Although this can be calculated, there seems little practical use as it implies constraints that align both the content and the frame itself.