All posts by Jon

Tandem Views for any iOS Screen

This tutorial is designed to show you how to use Auto Layout to correctly position two UIViews so they appear stacked in portrait mode or side-by-side in landscape mode on any iPhone screen. They will also be stacked on any iPad screen, regardless of orientation (although I wish there was an iPad landscape size class).

It’s also meant to emphasize keeping layout constraints to a minimum. Auto Layout can get complicated and convoluted in a hurry if you’re not careful. This is only worsened by Xcode’s poor UI, which constantly wants to truncate panes and their content, making it hard to see constraints.

As guiding principles, consider the following:

  • Analyze your layout needs before you start building
  • Look for constraints that will never change – the “absolutes”
  • Use the fewest constraints and the fewest size classes possible

Analyze the layout before building

Ask yourself what will be the best and necessary ways to present content or features to your app’s users. Sketch out what needs to be shown. It’s best if you have a good grasp of all the potential views across screen sizes, screen orientation and recently introduced features like split-screen or slide over views. I found that it was not enough to look at the schematic visual definitions you get when choosing a size class. So I created a template document showing all the possible combinations and definitions for size classes so I could keep it all straight:

You can download this PDF size class chart and either print it out to sketch or scribble on or edit it in Illustrator.

Look for the constraints that will never change, regardless of screen size or orientation.

Working from those and working inward from larger elements to smaller really helps. Taking this example, we can see that the first (yellow) view will always have its top and left edges aligned with the top and left of the Super View. The second (blue) view will always have its bottom and right edges aligned with the bottom and right of the Super View. (The black triangles are just there to indicate the orientation of the views’ content).

Later on we’ll cover the details of setting these constraints. For now, just keep in mind that our aim is to use as few rules as possible to achieve a layout. It’s no fun wading through a ton of constraints to find what you think is the one causing an issue. It’s even less fun to update a layout and suddenly see it go kablooey because of unexpected interdependencies. Any individual constraint can have a number of variables. Additionally, sometimes constraints only apply to one size class, which can lead to further troubleshooting nightmares.

Use as few size classes as possible

In this example, only two size classes will be used. Again, the value of using the size classes template to analyze the screen layout can really help. After I drew all the variations I realized I only needed two size classes for this – the generic wAny hAny and the compact height class wAny hCompact:

In the end, there will be a mere 12 constraints. Only 4 of them will be in the wAny wCompact size class. That will be enough to accommodate 7 different layout modes and any size/resolution screen.

Here’s how the views will appear across a multitude of possibilities:

Getting Started

Start by creating a new project in Xcode. In this case, I named it TandemView. Make sure Devices is set to Universal. Once the project is created, select Main.storyboard from the Project navigator. Its size class should be set to wAny hAny:

With the storyboard in view, open the Utilities pane if it isn’t open already and select the Object Library icon. Type “view” in the Filter field at bottom:

Select the View object and drag it onto the storyboard. Don’t worry about position or size:

To make the View easier to work with, change the background color and the name. In my case, I chose Background > Other… and set the color to a hex value of FFCC33 and the opacity to 62% (this will really help you see if there are any overlap issues). I gave the View a name of FirstView:

Next, add a Label object (using the Filter field makes it easy to find in the Object
Library) to the new view (not its parent view). It’s not important to change the text, but if this sort of thing leaves you feeling incomplete, feel free to do so:

Note: Use Editor > Size to Fit Content (CMD=) to get the Label to show all the text.

Using Auto Layout

Note: Sometimes you will set a constraint on a parent and child item and it will be expressed in reverse, e.g. “top = FirstView.top”. I recommend switching these around so that the child is on the left-hand side of the equation. Semantically and logically this better shows the relationship between the two objects. To do this, click on either item’s pulldown in the Attribute Inspector and select Reverse First and Second Item.

That said, we’re finally ready to start using Auto Layout! The first task is to just get the new sub view centered inside its Super View. To do this, with the Label still selected, select the Align icon () from the bottom right of the Standard Editor. Check the boxes next to Horizontally in Container and Vertically in Container. Set Update Frames to Items of New Constraints and click Add 2 Constraints.

And, voila!

Wait a minute!

What the h*** just happened?

Welcome to Auto Layout my friend. Your new view just got sized down to the proportions of the label. If, like me, you are used to the general sensible nature of XAML or have spent much time with CSS, this will just boggle the mind. But fear not. Take a giant swig of whatever is keeping you sane and soldier on.

Select both the parent view and FirstView (by holding the CMD key down while clicking on each). Then select the TIE-Fighter-looking Pin icon () from the bottom right of the Standard Editor. Choose the following options:

Now you should see this:

If you’re curious to see what this looks like in the simulator, hit CMD-R and have a look. You should see completely yellow screen with the label centered in it. Using CMD with the arrow keys to toggle between portrait and landscape you can see the effects.

Back in Xcode, you may have already noticed there are a couple of issues. Both the horizontal and vertical positions of FirstView are “ambiguous”. The rendering engine in iOS did a fine job of drawing this single view to fit the screen. But it’s really going to help down the road if these issues are dealt with now (and it will make Xcode happy). Left unfixed, these sorts of things have a nasty way of making portions of the layout disappear when updating frames.

Harking back to our first principle of looking for absolutes, we had already determined that this view’s top and left edges will adhere to the Super View‘s top and left, regardless of orientation, screen size or any other factor. Auto Layout offers a couple ways to set this, such as using the margins. In this case, I prefer to align the top and leading edges of the views. To do this, CMD-select both FirstView and its parent again. Select the Align icon () and make the following changes:

Note: Selecting All Frames in Container will head off any further errors, such as the Label being reported out of alignment.

You won’t see any difference in the simulator but the errors should be gone. More importantly, the view is going to be placed correctly even after we make further changes to it.

Now it’s time to alter one of the constraints. We need to change things so that FirstView‘s height is only half the height of the Super View. Under the scene, select FirstView.height = height from the Constraints. (You may need to click on the arrow to the left of the Constraints heading to expand them).

Over on the right-hand side of the editor, in the Utilities pane, the Size Inspector should be visible. Edit the values shown like this:

Note: The Priority value is lowered is that we will be adding another constraint that will override it when a phone is placed in landscape mode, matching the wAny hCompact size class.

The layout should now look like this:

Run the simulator and you should see screens like this:

Obviously, since we’ve thus far only set up constraints in the generic wAny hAny size class the view is going to look strange in landscape mode. Time to fix that. But first, one other edit. Just as with the FirstView.height constraint, we also need to set its width constraint’s priority to 750. Select that constraint and in the Size Inspector change the value.

Switch to wAny hCompact and CMD-select both FirstView and its parent view.

Hey, guess what? We’re back to this…at least in this size class…and when run in the simulator in landscape mode:

Now if that ain’t progress I don’t know what is! But fear not. Auto Layout is just doing what it was told to do. We’re going to make a quick edit to the new width constraint and will be seeing real progress in a moment. Switch back to wAny hAny. This will make it easy to see the two new constraints because they will be faded out. However, they are still selectable and editable. Select FirstView.width = width and in the Size Inspector change the Multiplier value to 0.5. Now if you switch back to wAny hCompact or run the simulator and toggle between portrait and landscape you’ll see that the view is now transforming correctly:

Note: Because the width constraint set under the wAny hCompact size class has a higher Priority it overrides the width constraint set under wAny hAny. Apple, of course, generally sets these priorities at 250, 750 or 1000 – depending on the purpose of the constraint. You can set them at any value and whichever constraint affecting the same parameters has the higher value will take precedence. This is all the more reason to plan ahead and anticipate necessary changes. The fewer the better!

Creating the Second View

You can either follow the initial steps used to create the first view or select it and copy it. If doing the latter, you will need to change its name to SecondView. Also, note that constraints are not copied along with the view (other than for internal items, such as the label). Regardless of whether you choose to start from scratch or copy, there are a few things that need to happen. The view needs a different name, as mentioned (whether changing it from the generic “View” or “FirstView”. It needs a different background color (although be sure to keep the opacity to 62%, so using the color picker is recommended). Also, the label’s value should be changed to “Second View”. Finally, constraints need to be added both in the wAny hAny size class and the wAny hCompact size class. These will be almost the same as those set for FirstView, with two critical differences – SecondView’s
Trailing and Bottom edges will be set to track the right and bottom of the Super View rather than its left and top.

Before adding any constraints, the layout should look more or less like this:

Note: You may see that four issues have been raised in Main.storyboard, related to the ambiguity of SecondView’s height, width and position. These should go away once the constraints are added.

First, ensure that the wAny hAny size class is selected. Select both the parent view and SecondView (by holding the CMD key down while clicking on each). Then select the Pin icon () from the bottom right of the Standard Editor. Choose the following options:

Now you should see this:

Two of the errors went away but of course the view has dutifully filled the entire screen. Additionally, while the horizontal and vertical position are still ambiguous, the layout engine has done its best to place the view. That doesn’t mean it’s okay to leave as is. Even if we didn’t need this view to adhere to the right and bottom edges of the screen, not setting those basic constraints can lead to confusion later.

The next step, obviously, is to select the SecondView.height = height constraint and edit its Priority to 750 and Multiplier to 0.5 in the Attributes Inspector. Don’t forget to also select the SecondView.width = width constraint and set its Priority to 750 as well.

Just to prove the point about about the necessity of setting basic constraints, click anywhere on the storyboard and then click on the Resolve Auto Layout Issues button () at lower right and select Update Frames under All Views in View Controller. On my screen the storyboard now looks like this:

Select both SecondView and the parent view. Then click on the Align Tool () and set the following constraints:

Running the simulator with an iPhone target and toggling between portrait and landscape mode shows that we’re most of the way there. However, in landscape mode SecondView needs to have its width and height constraints overridden in the wAny hCompact size class.

Switch to wAny hCompact and CMD-select both SecondView and its parent view.

Now, select the SecondView.width = width constraint and set the Multiplier to 0.5. (Switching back to wAny hAny will make it easier to see which constraint is which because the constraints applied under other size classes will be faded out. Select the faded out version of the constraint before applying the edit). You can verify that the desired effect has occurred either by switching back to wAny hCompact or, better yet, running the app in the simulator or an actual device. (This can be a good time to switch to other device profiles, such as an iPad model to see how the constraints apply).

On a phone you should be seeing this:

Sigils, Swastikas and Symbology

Came across sigil in some technical documentation (https://github.com/apple/swift-evolution/blob/master/proposals/0029-remove-implicit-tuple-splat.md)

https://www.google.com/search?q=sigil : “an inscribed or painted symbol considered to have magical power.”

On a related note, after reading The Long Ships I though the name of one of the characters – Sigrun – was kind of neat since in Old Norse it means “victory rune”. However, the rune itself was horribly co-opted by the Nazi SS (the all-too-familiar double lightning bolt design, featuring two of them).  That is hardly the only example.

Years ago, my aunt presented a slide show from her travels around Asia. It was surprising to see “backwards” swastikas prominently featured on Buddhist temples. She explained their long history in that part of the world. Despite Japan’s alliance with Nazi Germany in WWII these weren’t remnants of that time.

However, now there is a Japanese proposal to substitute another symbol on maps for tourists (prior to the 2020 Olympics): http://www.bbc.com/news/world-asia-35349619. Farther in the body of the article there are, indeed, symbols that might benefit from a change, such as the encircled H, which to many might suggest an emergency helipad rather than a hotel. However, I’d be inclined to keep the swastika since if far preceded use by the Nazis and runs the other direction. In a way, it symbolizes the opposite while reminding us of the negative aspects of human nature.

Sibling Rivalry

While working on a new responsive menu, I soon realized the need to be able to close any menu that might be open should the user choose another one. In a wide layout it would look particularly messy and confusing to have multiple menus shown. In a mobile layout that wouldn’t be so bad since there’s an accordion effect. However, the more real estate you can give back to the user the better!

Fortunately, jQuery has a handy selector that will grab all the li elements at the same level as the currently clicked/tapped element:

$(this).siblings().each(function() {
 
 // Animate close
 TweenLite.to($(this).find("ul > li"), speedFactor / 2, { height: 0, opacity: 0, ease: Power3.easeIn });
 TweenLite.to($(this).find(".menuArrow"), speedFactor / 2, { rotation: -90, ease: Power3.easeInOut });
 TweenLite.set($(this).find("ul > li"), { display: "none", delay: speedFactor / 2 });

 // Indicate menu is closed
 $(this).data("menuOpen", false);
});

Similar functionality can and should also be applied to the page as a whole so that pointing somewhere else would close all menus. Or, for that matter, when the user selects an item. Here are the results:

menu_landscape

menu_mobile

Proud to be Back

Run DMCTo borrow from Run DMC:

Listen party people here’s a serious song
It’s right not wrong, I should say right on
I gotta tell you somethin that you all should know
It’s not a mystery it’s history and here’s how it go

While session history management has been around for a few years, it’s not something I’ve needed until recently. Necessity dictates working both with AJAX/Web APIs and SEO as well as the ability to share links via email or social media. And it may well have been that prior to now there could have been gaps in browser coverage. According to http://caniuse.com/#search=Session%20history%20management adoption is widespread. So even an SPA (single-page app) can reasonably mimic traditional web navigation without you having to hang your keister out in the wind as a developer.

After dinking around with a couple HTML5  history navigational examples, and getting bogged down with some unexpected results in my AJAX/WebAPI project I, er, back-tracked and built my own bare-bones navigational example. This covers both real and virtual pages, navigation within and without the page holding the virtual content (e.g. pasting a link or clicking on it from an email or tweet). Feel free to play around and see if you spot any inconsistencies or unexpected behavior.

Navigating from another page (Click on the Virtual page link, then hit any of the buttons or the link back to real.html and the back/forward buttons in the browser. )

See update below!

http://ststates.azurewebsites.net/real.html

Accounts for linking from outside the site:

http://ststates.azurewebsites.net/virt.html?page=2

Looking at Developer Tools > console in your browser will report the history state and what triggered a change (replaceState, pushState, or popState). View the source (including comments) and you’ll see that all three of these are needed to cover the various navigational cases.

Update

Well, not so proud when I found out that just using the out-of-the-box HTML5 implementation of session history management had a nasty problem: Changing the title would also change it in the history. So holding down the back button, for instance, would show the same link for different URLs, e.g. “?page=1” and “?page=2” would share a title. That would truly suck in a production environment where the title is supposed to match a slug.

So I turned to history.js, which solved that problem and (theoretically should work with HTML4, although I don’t have a convenient means of testing that). Additionally, it took care of the problem with Safari popping an event on loading and shortened my code by some 30 lines, simplifying the logic considerably.

http://ststates.azurewebsites.net/virt2.html?page=2

Less is (almost always) more

This morning, I came across a tweet linking to a National Geographic article on data visualization and eye candy, which I emailed to a few friends (when we want to carry on a more in-depth conversation Twitter just doesn’t cut it):

http://news.nationalgeographic.com/2015/09/150922-data-points-visualization-eye-candy-efficiency/

“Find a metaphor, start simple, and build up from there.”

One, who works at an economics research organization, replied with:

“We have lots of tools that allow relatively informed users to explore data and answer questions that they have, but few of our visualizations actually stick to a single story line.  We’re trying to improve the balance of exploratory tools vs. simple, compelling stories.”

My response:

That’s highly similar to the approach often taken with interactive mapping interfaces – either attempting to duplicate desktop GIS functionality or show a particular facet of data with spatial attributes. Finding the balance between them is tricky. Generally, end users want to answer one or two questions though.

The trails web app I linked to recently – https://helenamontanamaps.org/Html5Viewer/?viewer=trails – is about as far as I’d ever go towards the GISFunc side of things anymore (there are a few gee-wiz, that’s cool features like mixed transparency for the base layers…but are they really necessary in most cases? No way).

http://mapbrief.com/2015/01/09/daring-to-build-a-citizen-centric-homepage-the-philadelphia-story/ is one of the best pieces I’ve read on user-focused functionality.

Incidentally, I read that NatGeo article on my phone and many of the visualizations were too small to be intelligible. For some reason, this one on landslides stood out to me as good on the phone (although on my desktop monitor the map background is barely visible):

landslides

A couple days ago, one of these correspondents sent me a link to a draft page showing all kinds of data related to load times, download sizes, CMSes used, and number of subscribers for a raft of news publications. I can’t share that page in its current state but will say that I wrote back encouraging him to make it way simpler. Just ’cause Tableau gives you a kitchen sink toolbox doesn’t mean you have to use it.

 

Power Generation Across the U.S.

NPR posted an interesting series of charts showing how the country and each state derives its mix of electric power generation: http://www.npr.org/2015/09/10/319535020/coal-gas-nuclear-hydro-how-your-state-generates-power us_power_gen One thing that stood out to me is that something we in the NW take for granted – hydro power – isn’t widely used:

%Hydro

US: 6
AK: 26
CA: 9 (dropped from 19!)
ID: 62 (dropped from 83)
ME: 31 (up from 20)
MT: 37
OR: 59
SD: 48
WA: 69

With the exception of the Missouri River dams in SD, I’d guess that it’s a combination of topography (suitable sites), the need to impound runoff for irrigation and flood control (and to a degree, navigation – Snake/Columbia), with a relatively decent amount of water.

Also interesting: contrasting two coal-producing states like WV and WY. The former depends almost entirely on coal. The latter, while still really high (89%), gets 11% from renewables and hydro. Incidentally, MT is getting close to half (44%) of its power generation from renewables and hydro. But OR at 63% and WA at 76% lead the country. (All you have to worry about is forest fire smoke).

ID is, despite its ultra-conservative mindset, is in the running for cleanest state. It gets 62% from hydro, 21% from renewables and 17% from natural gas (the latter being somewhat surprising. AFAIK, natural gas would have to be imported (presumably from WY, UT, and CO). VT gets all its power (with the exception of that portion of Renewables using biofuels) from non-carbon sources. However, 72% is nuclear. Presumably they’re not burying the waste in Bernie Sanders’ back yard.

On our last visit to Kauai, I was struck by how much photovoltaic generation there is now. However, renewables only make up 10% of the mix. HI is still heavily dependent on oil and coal. Midwestern states still love them some serious amounts of coal, yet another “quality” which doesn’t engender a desire to live there. AR actually increased its coal consumption!

In all, these graphs are nicely done and quite informative.

Hack that 4K Monitor

As in http://arstechnica.com/information-technology/2015/08/open-source-typeface-hack-brings-design-to-source-code/ – “Combine it with an HD monitor and you can comfortably work at 6 or 7px sizes.”

I’m somewhat skeptical of that for 40-year-old+ users…but still it looks like an interesting typeface. I have yet to find anything else besides this that would be equal to or better than Consolas. It may be a good option for Mac use. However, if you look at the image below, the lack of a line spacing option in Visual Studio makes it a wash. I could, in fact, use 9-pt Consolas and still find it usable, although the height savings is only a line or so in 30! Perhaps in an IDE with a line-spacing option Hack would make more sense (or someone who is inclined to futzing with typeface editors could fork the project and create a reduced-line-height version).

hack_v_consolas
(click to embiggen)

On my 1920×1200 monitor, reducing Hack below 8 pt (or Consolas below 9 pt) is just not comfortable. That said, it is a highly-legible and pleasing typeface. I’ll try it out in Xcode too and see if it make a difference there.

Windows 10 and Compatibility

Prior to upgrading to Windows 10 yesterday, I was graciously informed by the updater that my all-in-one printer – a Panasonic KX-MB271 – wasn’t compatible. I appreciated knowing this up front. I also was disappointed because the printer has seen low use and I have extra toner cartridges for it. Still, I chose to go with the upgrade (pluses outweighing this one minus).

Sure enough, I couldn’t print. However, I opened Device Manager and manually selected the drivers for the printer and scanner. This required drilling down into some specific folders, e.g. D:panasonicMFSDriversObjectPrinterWin7VistaXp_x64GDI for the printer. But it worked. The printer is back in business.

Although…when I rebuild my work desk I will probably find a new home for it and park a Wi-Fi/networkable printer in the nearby storeroom where my router and cable modem live.

Note: You can use http://www.microsoft.com/en-us/windows/compatibility/CompatCenter/Home to check for compatible devices. However, it definitely works best if you have the specific part number, e.g. kx-mb271 rather than kxmb271. And, as this post points out, never take it for granted that you’re SOL.

Javascript @media queries

On newer browsers, you can match @media states like “(min-width: 45em)” using javascript, then apply conditional code. I only found this out last night. Today, I came across http://wicky.nillia.ms/enquire.js/. It not only offers some additional functionality beyond the vanilla javascript. It can be made backwards compatible even with IE6.

One of the best use cases is only loading certain content (via AJAX) if the screen size warrants it, e.g. a sidebar or a higher resolution image.

The Beauty of Anonymity

No, not the kind commonly found on the Internet, which is often anything but. Rather, C#’s Anonymous types, introduced way back in 2007 with C# 3.0 and more often than not ignored by me until now.

I used to avoid JSON. Its syntax seemed just like every other delimited format I’d ever seen. In short, a headache to parse. XML is more verbose. Yet (at least for me) more humanly readable. That was until I found two things:

  1. XML parsing across different browsers is an even bigger headache
  2. With JSON I can create almost identical classes on both a C# server and a Javascript client

Later, the relationship was strengthened by the discovery of services like http://json2csharp.com/, which will create a C# class for you out of a JSON sample, as will more recent versions of Visual Studio. That’s great for dealing with someone else’s API output.

But sometimes a class seems like overkill. What if you have just one place where you really, really need to send just a bit of JSON to the client? For example, in one of my SignalR hubs, I need to send three things:

  1. brief message
  2. start time
  3. end time

SignalR, by the way, always sends JSON so all you need is some way to package (enclassulate?)  your data. Enter the Anonymous type, which if you use Linq you’ve probably already seen many times, e.g:

var productQuery =
    from prod in products
    select new { prod.Color, prod.Price };

foreach (var v in productQuery)
{
    Console.WriteLine("Color={0}, Price={1}", v.Color, v.Price);
}

With a var, the usage is basic: “var foo = new { someData = “something” };” Anonymous types support nesting too, so I was able to tidily separate my message data from my duration data. Here’s a sample method in the SignalR hub on the server:

public void AnonTypeDemo(string groupID)
{
    var startTime = DateTime.UtcNow;

    var endTime = startTime + TimeSpan.FromMinutes(10);

    // the anonymous type
    var data = new { message = "started", duration = new { startTime, endTime } };

    Clients.Group(groupID).anonTypeDemo(data);
}

The method sends a JSON string that looks like this:

{
    "message": "started",
    "duration": {
        "startTime": "2015-02-24T18:19:48.8008904Z",
        "endTime": "2015-02-24T18:29:48.8008904Z"
    }
}

On the client side, a Javascript method will be called by the SignalR client to process the received data:


myHub.client.anonTypeDemo = function (data) {

    console.log(data);

    var message = data.message;

    var startTime = data.duration.startTime;
    var endTime = data.duration.endTime;
}

And that’s all he wrote.