Posts tagged with: ios

Material Design on iOS

Resources from my SyncMobile presentation at Odecee on 15/2/2016.

Introduction to Material Design
UI components
Meaningful motion

Auto Layout Tip #1: Multiplier

An Auto Layout constraint can be described in the form of a linear equation. For example, for an equality constraint:

FirstItem.Attribute1 = (SecondItem.Attribute2 * Multiplier) + Constant

Apple's Auto Layout documentation explains this concept in more detail. In this post we're going to focus on what is perhaps the most commonly misunderstood part of this equation, the multiplier.

Equal Widths constraint

I came across an interesting Auto Layout problem recently where I wanted to resize a button relative to the size of an image view above it.

My requirements were:

  • The button should fit its content without compression.
  • There should always be at least 20 points between the button and the left and right edges of screen.
  • The button should size itself to be slightly wider than the width of the image view above it (10% wider to be exact) if it has more horizontal space than it needs.

So, on wide screens the button will be 1.1 times the width of the image view. On narrow screens the width of the button will increase to fit its content, but only until it reaches 20 points from the edge of the screen (shown by the red guides in the below screenshot). At this point the button will be forced to start compressing its content.

Preview of button layout

The solution
  • Give the button a high horizontal content compression resistance priority (e.g. 900) and low content hugging priority (e.g. 100).
  • Add >= 20 constraints to the left and right edges of the button (priority 1000).
  • Add an Equal Widths constraint between the button and the image view with a multiplier of 1.1 (priority 950, constant 0). Edit the constraint and change the relation to Greater Than or Equal.

Remembering our equation from earlier, the last constraint can be expressed as follows:

button.width >= (imageview.width * 1.1) + 0

Button Greater Than or Equal constraint with multiplier

A common mistake

Often when you have a constraint with a multiplier, the constant is 0 (as it is here), but it doesn't have to be. If you're using a constant together with a multiplier, just remember the multiplier is applied to the attribute first and then the constant is added to the result. A mistake I sometimes come across is a constraint that is unintentionally offset with a constant of 1. For multipliers the default is 1, but for constants the default value is 0 (which makes sense when you think of a constraint in terms of a linear equation).

Aspect Ratio constraint

Multipliers can be decimals like 1.1 in the previous example, but they can also be ratios. The image view in that example has an Aspect Ratio constraint with a multiplier of 240:221. Regardless of what size the image is displayed on screen, this constraint makes sure the aspect ratio stays the same.

Aspect ratio constraint with ratio multiplier

Further reading

If you'd like to learn more about multipliers and test your Auto Layout skills, have a go at my recent Auto Layout Workshop.

Auto Layout Workshop

A couple of weeks ago at work I ran a workshop with our iOS team on auto layout skills and best practices. It was great to spend some time as a team discussing different approaches to solving challenging layouts and sharing Interface Builder tips and tricks.

The project

I created a sample project for the workshop and published it on GitHub. It consists of three screens in a storyboard with no constraints and instructions in the README on how the screens should adapt to different screen sizes. Everyone on the team was asked to spend roughly one hour laying out the screens to meet the given criteria, and then later in the week we would get together and discuss how it went and share our solutions.

Each screen in the project was designed to teach specific auto layout skills. The Profile screen is the most complex of the three screens, but also the most important. A developer with a good grasp of auto layout basics would be able to make a decent attempt at this screen, but it requires an understanding of some of the more advanced auto layout techniques to fully meet the criteria provided. These techniques include aspect ratio and equal width constraints, understanding how the multiplier affects constraints, using spacer and container views to improve the layout, inequality constraints, constraint priorities, and compression resistance and content hugging priorities.

Profile screen

The Choose Recent Photo screen is an easy one to lay out manually by dragging constraints between each of the photos, but that's time consuming and unnecessary. The hint I gave for this screen was that it should take under 30 seconds to complete. The point of this exercise was not to get the layout correct (that's easy), but to become more familiar with the tools Interface Builder has for adding multiple constraints at once. This screen can be fully laid out from one menu by adding 53 constraints and updating the frames in one go. The idea for this screen came from a demo in lecture 2 of the Stanford iTunes U course Developing iOS 8 Apps with Swift where Paul Hegarty lays out a screen of calculator buttons in the same way. Watching someone else use Interface Builder is a great way to learn auto layout. My advice for anyone who's new to auto layout is to watch lots of demos (like the Stanford course and the WWDC videos in the resources below) and pair with as many different developers as you can.

Choose Recent Photo screen

The Take Photo screen is an adaptation of a camera screen in an app our team was working on at the time. If you were able to complete the Profile screen, this screen should be straightforward. However, for those who struggled with the Profile screen this is another chance to practice constraint priorities in a simpler layout. The main purpose of this screen is to demonstrate how high priority inequality constraints can be paired with lower priority equality constraints to achieve the desired layout.

Take Photo screen

The workshop

A few days after giving the team this auto layout challenge, we met to discuss our solutions to the layout problems and talk more generally about auto layout best practices. Auto layout is a hard topic to talk about in any detail without having Interface Builder open in front of you. I definitely recommend running the workshop in a meeting room where the presenter can have the project up on a screen and walk through some of the more complex layouts step by step.

After the workshop I emailed out my solutions to the team (which is now in the GitHub repo here). I suggested they checkout the master branch and look over my storyboard, then go back to their project to refine their layout further or fix anything they couldn't get working (or give me feedback if their solution was better than mine!).

Auto layout resources

There are a lot of great WWDC sessions on auto layout, including:

I also recommend the objc.io article Advanced Auto Layout Toolbox, which covers topics such as intrinsic content size, the alignment rect, animation and debugging.

Adaptive Auto Layout by Tyler Fox is one of the best videos I've seen on adaptive layout and size classes.

I picked up a lot of useful Interface Builder tips and tricks from the demos in the Stanford iTunesU course Developing iOS 8 Apps with Swift. (The Choose Recent Photo screen in this workshop was inspired by the calculator demo in lecture 2.)

Brownbag: Designing Delightful User Experiences

Resources from my brownbag presentation at Odecee on 29/4/15.

Keynote demos
Videos

ShinobiControls with CocoaPods

ShinobiControls is a collection of UI Controls for iOS and Android. I used ShinobiCharts on a recent project where I wanted to add the framework via CocoaPods as we were doing for other third party dependencies.

Jeffrey Jackson wrote a helpful post on how to use ShinobiControls with Cocoapods by creating private github repos for each of the frameworks. I took a slightly different approach because I wanted to use a local podspec instead of a private repo.

Note: The below instructions are for ShinobiCharts, but the process is the same for the other frameworks.

Setup

Inside your project directory, create a Frameworks folder and copy the framework into a subfolder. I included VERSION.txt here too.

Frameworks/ShinobiCharts/ShinobiCharts/ShinobiCharts.framework
Frameworks/ShinobiCharts/ShinobiCharts/VERSION.txt

Create an empty podspec here:

Frameworks/ShinobiCharts/ShinobiCharts.podspec

Your directory structure should now look like this:

Podspec

The podspec:

Pod::Spec.new do |s|
  s.name         = "ShinobiCharts"
  s.version      = "2.7.3"
  s.summary      = "ShinobiCharts"
  s.license      = 'Private'
  s.homepage     = "http://www.shinobicontrols.com"
  s.author       = { "Your Name" => "youremail@example.com" }
  s.vendored_frameworks = 'ShinobiCharts/ShinobiCharts.framework'
  s.frameworks   = 'QuartzCore', 'OpenGLES', 'CoreText'
  s.library      = 'c++'
end

The ShinobiCharts quick start guide lists the other frameworks it needs:

  • Security.framework (Trial Version only)
  • QuartzCore.framework
  • OpenGLES.framework
  • CoreText.framework
  • libc++.dylib

If you're using the trial version, you should also add Security to s.frameworks.

Podfile

Add this line to your Podfile:

pod 'ShinobiCharts', :path => 'Frameworks/ShinobiCharts/'

Run pod install, and you're good to go. ShinobiCharts should now be listed as a Development Pod in your workspace.

Tests

If you try to use ShinobiCharts in your unit tests, you'll probably see this error:

'ShinobiCharts/ShinobiCharts.h' file not found

To fix this, add the below line to "Framework Search Paths" on the test target (and make sure you set it to "recursive"):

${SRCROOT}/Frameworks/