Link: Custom Volume-Adjustment UI on iOS

The system-standard volume-adjustment UI on iOS is obvious and intuitive - but it’s also obnoxious: it blocks the center of the screen, and can’t be dismissed.

Me too, lil fella. Me too.

Me too, lil fella. Me too.

Snapchat has a custom volume-adjustment UI (up in the status bar area). It’s a critical feature: if you miss a video on there, you can’t replay it!

It’s the kind of little detail that’s especially nice in apps like Vine or Tweetbot, where videos tend to be short and sweet, so the volume overlay really torches the experience.

However, it’s not 100% clear how you could go about suppressing the system-standard volume-adjustment UI. Fortunately, Andrea Mazzini’s already done the hard work for us by creating SubtleVolume.

With the recent release of SubtleVolume by Andrea Mazzini, you can easily create this effect in your own app - or fork the implementation to implement your own design!

Creating Custom Focus Effects in tvOS

In my current side-project, I’m making a tvOS app. One of the screens features a handful of circular buttons. Unfortunately, when the tvOS Focus Engine renders these buttons, they get really awful shadows and focus states, that look a bit like this:

The system-standard UIButton looks awful with circular images.

The system-standard UIButton looks awful with circular images.

I had the good fortune of attending Apple’s tvOS Tech Talk in Seattle this winter - which meant I got to ask a few folks in the Q&A lab! They told me that, unfortunately, tvOS doesn’t allow non-roundrect focus effects: the system-standard focused state are pretty “simple” and aren’t clever enough (yet) to alter their shape depending on the alpha-channel of the image in the UIButton.

We needed to roll our own custom focus effect - but fortunately, it turned out to be pretty straightforward!

Here’s what the finished product looks like:

Ah, much better! (Apologies for the GIF compression; it's rasterizing the shadows.)

Ah, much better! (Apologies for the GIF compression; it's rasterizing the shadows.)

You can find the sample code for this project on GitHub - but read on to learn what’s under the hood!

What’s in a Focus Effect?

There’s a ton going on in the system-standard Focus Effect:

  • a scale transform that makes the button appear larger,
  • a shadow that makes the control appear to lift off the screen,
  • a white glare that reflects a “light source” across the surface, probably a masked blurry white circle,
  • a parallax tilt and shift that rotates the button in 3D, to provide continuous gestural feedback as you nudge your finger slightly on the trackpad,
  • and if the view contains a layered image, there’s a neat 3D parallax effect there, too.

Put that all together, and you get this effect:

These effects are important - they help you know what’s in focus, and they attempt to make the remote’s indirect manipulation of onscreen content feel a little more like direct (touch) manipulation.

Creating the Focus Effect

From the above list, we know we’re going to need some transforms, a shadow, a parallax tilt, a parallax shift, a parallax “white glare”, and a parallax layered image. (Note: for my project’s design, I don’t need the glare or layered image - but I’ll have a footnote below outlining what I might try if I were building those effects.)

Really, there are two subgroups here: - a “focused style” (“what does this view look like when it’s focused?”) - and a “parallax style” (“how does this view tilt and shift around when it’s focused?”)

FocusedStyle

Let’s start by creating a FocusedStyle:

public struct FocusedStyle { 
    let transform: CGAffineTransform 
    let shadowColor: CGColor 
    let shadowOffset: CGSize 
    let shadowRadius: CGFloat 
}

CustomFocusableViewType

We can then create a protocol that allows any view or control to render with this style:

public protocol CustomFocusableViewType { 
    var view: UIView { get }         
    var focusedStyle: FocusedStyle { get } 
}

public extension CustomFocusableViewType {
    func displayAsFocused(focused: Bool) {
        view.layer.shadowOpacity = focused ? 1 : 0
        view.transform = focused ? focusedStyle.transform : CGAffineTransformIdentity
    }
}

CustomFocusableButton

Now let’s create a CustomFocusableButton that conforms to our CustomFocusableViewType protocol. There’s a bit of code here to make the “select” animation work (when you click down on a button) - but outside of that, there’s not much here:

/// An implementation of CustomParallaxView,
/// implements a pressDown state when Select is clicked. 
/// Particularly useful for non-roundrect button shapes. 
public class CustomFocusableButton: UIButton { 
public let focusedStyle: FocusedStyle

public init(focusedStyle: FocusedStyle) {
        self.focusedStyle = focusedStyle

        super.init(frame: CGRectZero)

        view.layer.shadowColor = focusedStyle.shadowColor
        view.layer.shadowOffset = focusedStyle.shadowOffset
        view.layer.shadowRadius = focusedStyle.shadowRadius
    }

    required public init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    // MARK: Animating selection - buttons should shrink when clicked.

    public override func pressesBegan(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {
        super.pressesBegan(presses, withEvent: event)
        guard presses.count == 1 else { return } // If you press multiple buttons at the same time, that shouldn't trigger a pressDown() animation.

        for press in presses where press.type == .Select {
            pressDown()
        }
    }

    public override func pressesEnded(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {
        super.pressesEnded(presses, withEvent: event)

        for press in presses where press.type == .Select {
            pressUp()
        }
    }

    public override func pressesCancelled(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {
        super.pressesCancelled(presses, withEvent: event)

        for press in presses where press.type == .Select {
            pressUp()
        }
    }

    private func pressDown() {
        UIView.animateWithDuration(0.1,
            delay: 0.0, 
            usingSpringWithDamping: 0.9,
            initialSpringVelocity: 0.0,
            options: .BeginFromCurrentState,
            animations: { () -> Void in
                self.displayAsFocused(false)
            }, completion: nil)
    }

    private func pressUp() {
        UIView.animateWithDuration(0.2,
            delay: 0,
            usingSpringWithDamping: 0.9,
            initialSpringVelocity: 0,
            options: .BeginFromCurrentState,
            animations: { () -> Void in
                self.displayAsFocused(true)
            }, completion: nil)
    }
}

extension CustomFocusableButton: CustomFocusableViewType {
    public var view: UIView { return self }
}

ParallaxStyle

Next, let’s make a ParallaxStyle, which wraps our FocusedStyle along with the UIInterpolatingMotionEffects that compose into a parallax effect when you slide your thumb around.

/// Represents the tilting & shifting parallax effect when you nudge your thumb slightly on a focused UIView
public struct ParallaxStyle {
    /// The focused appearance for a view
    let focusStyle: FocusedStyle

    /// The max amount by which center.x will shift.
    /// Use a negative number for a reverse effect.
    let shiftHorizontal: Double

    /// The max amount by which center.y will shift.
    /// Use a negative number for a reverse effect.
    let shiftVertical: Double

    /// The max amount by which the view will rotate side-to-side, in radians.
    /// Use a negative number for a reverse effect.
    let tiltHorizontal: Double

    /// The max amount by which the view will rotate up-and-down, in radians.
    /// Use a negative number for a reverse effect.
    let tiltVertical: Double

    var motionEffectGroup: UIMotionEffectGroup {
        func toRadians(degrees: Double) -> Double {
            return degrees * M_PI_2 / 180
        }

        let shiftX = UIInterpolatingMotionEffect(keyPath: "center.x", type: .TiltAlongHorizontalAxis)
        shiftX.minimumRelativeValue = -shiftHorizontal
        shiftX.maximumRelativeValue = shiftHorizontal

        let shiftY = UIInterpolatingMotionEffect(keyPath: "center.y", type: .TiltAlongVerticalAxis)
        shiftY.minimumRelativeValue = -shiftVertical
        shiftY.maximumRelativeValue = shiftVertical

        let rotateX = UIInterpolatingMotionEffect(keyPath: "layer.transform.rotation.y", type: .TiltAlongHorizontalAxis)
        rotateX.minimumRelativeValue = toRadians(-tiltHorizontal)
        rotateX.maximumRelativeValue = toRadians(tiltHorizontal)

        let rotateY = UIInterpolatingMotionEffect(keyPath: "layer.transform.rotation.x", type: .TiltAlongVerticalAxis)
        rotateY.minimumRelativeValue = toRadians(-tiltVertical)
        rotateY.maximumRelativeValue = toRadians(tiltVertical)

        let motionGroup = UIMotionEffectGroup()
        motionGroup.motionEffects = [shiftX, shiftY, rotateX, rotateY]

        return motionGroup
    }
}

CustomFocusEffectCoordinator

So far, we’ve got FocusedStyle, ParallaxStyle, and a custom UIButton that implements CustomFocusableViewType and animates its UIControlState.Selected properly. Now we need a way to link up our UIMotionEffectGroup and FocusedStyle to the didUpdateFocusInContext(_:withAnimationCoordinator:) function in our UIView.

Enter the CustomFocusEffectCoordinator:

/// Manages the intersection of UIMotionEffects
/// and the tvOS Focus Engine, to provide a nice
/// parallax/focus effect on custom controls. 
public class CustomFocusEffectCoordinator { 
    private let views: Set<UIView>
    private let motionEffectGroup: UIMotionEffectGroup</uiview>

    public init(views: [UIView], parallaxStyle: ParallaxStyle) {
        self.views = Set(views)
        self.motionEffectGroup = parallaxStyle.motionEffectGroup
    }

    /// Call this function within your `didUpdateFocusInContext` method to create a parallax effect!
    public func updateFromContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
        coordinator.addCoordinatedAnimations({

            if let previousView = context.previouslyFocusedView as? CustomFocusableViewType {
                if self.views.contains(previousView.view) {
                    previousView.displayAsFocused(false)
                    previousView.view.removeMotionEffect(self.motionEffectGroup)
                }
            }

            if let nextView = context.nextFocusedView as? CustomFocusableViewType {
                if self.views.contains(nextView.view) {
                    nextView.displayAsFocused(true)
                    nextView.view.addMotionEffect(self.motionEffectGroup)
                }
            }

            }, completion: nil)
    }

    /// When you're ready to tear down the effect, call this function.
    public func removeMotionEffectsFromAllViews() {
        views.forEach { $0.removeMotionEffect(motionEffectGroup) }
    }
}

This class has the following responsibilities: - Links a set of CustomFocusableViewTypes to a ParallaxStyle, - Provides an easy way to update from a UIFocusUpdateContext and UIFocusAnimationCoordinator in the parent view. - Provides a way to tear down the effect.

Implementing in our UIView / UIViewController

Now for the fun & easy part: hooking it all together in the UIViewController or UIView!

class MyView: UIView {
    private var viewData: MyViewData

        private let customButtonOne = CustomFocusableButton(...)
        private let customButtonTwo = CustomFocusableButton(...)

        init(viewData: MyViewData) {
            self.viewData = viewData

            let focusedStyle = FocusedStyle(
            transform: CGAffineTransformMakeScale(1.1, 1.1),
            shadowColor: UIColor.blackColor().colorWithAlphaComponent(0.3).CGColor,
            shadowOffset: CGSize(width: 0, height: 16),
            shadowRadius: 25)

        let parallaxStyle = ParallaxStyle(
            shiftHorizontal: 4,
            shiftVertical: 4,
            tiltHorizontal: 10,
            tiltVertical: 10,
            focusStyle: focusedStyle)

            self.focusEffectCoordinator = CustomFocusEffectCoordinator(
                views: [customButtonOne, customButtonTwo],
                parallaxStyle: parallaxStyle)
        }

            public override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
        focusEffectCoordinator.updateFromContext(context, withAnimationCoordinator: coordinator)
    }
}

Ta-da! Now you have a nice Focus behavior on your custom views - and since it’s all composed by little protocols and style-structs, you can easily tailor the focus and parallax behavior to suit your needs.

One Last Thing

In my current project, we didn’t have a need for the white gloss or layered image aspects of the focused state. Using the above code, you could probably extend ParallaxStyle and CustomFocusableViewType to implement these behaviors.

But, a word of caution: if you’re going to implement these bits, spend extra time polishing ‘em, because it’s no good when custom UI attempts to mimic the native platform but gets stuck in the Uncanny Valley. If you don’t fully dial-in the gloss and layered image effects, you may find that your UI feels a bit out-of-place, like a non-native app running on iOS.

Enjoy!

Grading Your App with a "Weighted GPA"

In the last post, I wrote about how the standard “P1-P4” bug-management workflow has its perks, but often misses the forest for the trees. For that reason, it’s helpful to get a different perspective on our work. by measuring it against a different ruler.

A couple of years ago, my team was trying to figure out how to advocate for quality and polish in our app. The “design nitpick P4s” were piling up, and we felt that marred the legibility, usability, and branding of the product - but each individual bug was so minor, they tended to lose out when compared one-on-one with other issues.

We came up with a new metric: rather than debate the merits of each individual bug, what if we “graded” the components of the app?


Scoring Your App

Imagine each part your app as a course in school: each part has its relative weight (or "credits"), and a grade: an "A" gets a 4.0, an "A-" gets a 3.7, a "B+" gets a 3.3, a "B" gets a 3.0, and so on.

What’s important for your app? For example, if the first impression really matters, give extra weight to the empty and unauthenticated states.

To calculate the score for a given part, you multiply its credits times the grade earned. Then, sum up the weighted scores, and divide by the total number of credits. There’s your weighted GPA!

Here’s a little table to illustrate:

Component Grade Weight Weighted Grade
Recipe List: Empty State 3.0 7 21
Recipe List: Full State 2.7 6 16.2
Recipe List: Edit Mode 2.3 4 9.2
Recipe Details: Cooking Mode 3.7 6 22.2
Sign In: "Happy Path" 3.7 8 29.6
Sign In: Error State 4.0 7 28
Sign In: Forgot Password 3.3 4 13.2
Sign Up 3.7 7 25.9
Totals: 53 177.3
Weighted GPA: 3.35

Nice, right? You've now got an important-feeling score that quantifies how you feel about the overall experience of using the product. This score really helped my team explain how the small bugs were adding up.

Give Constructive Feedback

Be really thoughtful with how you score and share this information. Grades are a touchy thing, and if you tell somebody their part got an “F” you need to consider how to deliver that feedback in a constructive way. So, uh, do that! This isn't The Sanctioned Way Your Team Measures Things™, so don't be pushy.

Consider building this GPA with others - you could, for example, each assign credits and grades to a shared list of app components, and consolidate the collected scores - that way everyone can weigh in!

Make specific, addressible notes for each component's grade, that way you have a punchlist to work through! (Better yet, have links to the related bugs in whatever tracking system you use!)

Revisit It Over Time

Recalculate the grade every few months. We found it helpful (and motivating) to show the team how a little bit of polishing work went a long way in improving the overall look and feel of the product.

Eventually, Everybody’s An Edge Case

Let’s say you’re making A Thing For Humans To Use. It has Many Features, and that Sometimes, Bugs Are Discovered.

In many organizations, bugs are organized by priority:

  • P1 (“Showstopper”) - a critical issue that must be fixed before any P2’s. Often a terrible crasher or something that prevents beta-testing.
  • P2 (“Must-Have”) - an issue that must be fixed before the next release.
  • P3 (“Below-The-Line”) - an issue that, while important, shouldn’t hold up a release. These are often edge-case issues.
  • P4 (“Nice-To-Have”) - usually nit-fixes and pixel-nudges - aren’t prioritized over other bugs, and often aren’t prioritized over new features.

This prioritization is important - but as with any system, what gets measured is what matters, and sometimes, those P3’s and P4’s really add up. While individual bugs matter, your users experience the product as a cohesive whole, and many will abandon it after one or two bugs mar their experience.

It’s important that bugs represent individual units of work for developers and testers - so bugs often get broken down into small, specific components, and teams fix those components.

However, this breaking-down of bugs often means that design bugs are filed as P4 issues - they aren't crashing the app, and each individual design issue only affects one part of one screen.


How Do Small Bugs Add Up?

Let’s say bug1 appears, and it affects 10% of the users, so we decide it’s an “edge case”. This’d be a “P3” (or “Priority 3”) bug - not something worth delaying a release. Here’s what the odds of hitting bug1 look like for a user:

What happens if bug2 appears, and it also affects 10% of your users? Here’s our graph, now with two dimensions - your odds are now 19% that you’ll hit a bug:

What if bug3 shows up, and it also affects 10% of your users? At this point, we’d have to start drawing 3D diagrams, so Let’s Use Math Instead™:

oddsOfHittingABug = 1 - (bugProbability ^ bugCount)

What are the odds you'll hit a bug?

X-axis: Number of 10%-likelihood bugs

(Of course, this is an oversimplification - bugs are often not independently distributed. FWIW, I got the idea for this math from page 100 of The Beginning of Infinity.)


An Infestation of Small bugs

When I was a designer, I’d often find many design bugs filed as P4 - things that were too small to bother fixing with urgency. This was really frustrating! We’d spend time making pixel-perfect designs, and then wind up shipping a product with glaring typography, layout, animation, and copywriting bugs.

Putting our Product Manager Hats on, these bugs are often goofy little giblets. “Look”, you say, “We built an app that talks to the server, gets a result back, and renders it - that is so much! Why complain about these little nits?

At some point, all these little tiny bugs form this Nasty Hive that really mucks up the experience:

  • A mound of small bugs makes it harder for users to trust you with their data, their money, and their time.
  • Designer morale drops - why bother making pixel-perfect comps, style guides, animations, or filing bugs if they’re never going to get fixed?
  • Engineers, QA, and Product Managers want to build something beautiful, too! When your culture doesn't value design bugs, it means that engineering slacks on implementing the design, and QA stops looking for them. After all, if design bugs aren't important, why waste time on them?

Calling In An Exterminator

So, what can we do about this hive-of-a-thousand-P4-bugs?

  • Create a meta-bug, called “We Have Lots Of P4s”, give it a P2 or P3 priority, and let a few team members go off and tackle the bugs for a fixed amount of time. You’ll be surprised how much this can polish things up and boost morale.
  • Have an occasional “nit-fix” week (say, once a quarter?), where you let team members fix whatever bugs are bothering them.
  • Use tools that help avoid P4s in the first place, like a color / font palette in your code (more on this in a future post).
  • Involve the team in the bug-prioritization, so they understand and accept the rationale.
  • What's your Design GPA? How important is each screen? What is each screen's grade (A, B, C, etc)? Combining these two bits of information gives you a weighted "Design GPA" - a different measurement of your product that provides a different measurement of the design bugs in your app. Maybe those 5 P4 bugs on the checkout screen really are a collective problem!

The P1-P4 prioritization system works great for many reasons, but it disincentivizes teams from polishing up the design of a product. What Gets Measured Gets Done - take time to measure things differently every once in awhile.

JustTheMap

Sometimes, when you're designing an interface, you need a MKMapView to drop in to your screens - so you need a screenshot of a map. Except: Maps.app displays points of interest, buildings, and other things - and it has a status bar and a few other tidbits on the map.

Over the past few years, I've needed maps for a particular location, etc - so I've had this suuuper tiny little universal app that works on iPhone and iPad. It just shows a big ol' map, without any status bar, points of interest, buildings, or the user's location:

You can find the repository over on GitHub.

An Early Look at WatchKit

If you're interested in exploring WatchKit, check out @NatashaTheRobot's write-ups:

I. Creating a "Hello, World" app
II. Creating a Table
III. Creating a Paginated UI app
IV. Creating a Hierarchical UI app

It's interesting - there are so many constraints in creating Apple Watch apps. The constraints are well-chosen - they'll help enforce better ergonomics as we all adapt to designing for a new platform, and will prevent battery drains.

Try downloading the SDK and play around with it - yes, the constraints are pretty strict, so try and focus on your designs above and outside of the watch itself - where is this device being used? How? Why? That kind of larger design, along with an understanding of the constraints, will be super valuable as you approach designing for this new platform.

Tracking & Character Spacing in iOS

In tools like Photoshop and InDesign, tracking is the term that describes the relative amount of spacing between characters in a string of text.

A wonderful bit about tracking: pick a value, and your text will have that same visual style, regardless of the font size. It scales:

See how the spacing in between characters scales with the type size?

Unfortunately, tracking doesn't exist in iOS. What we *do* have available is character spacing:

See how the smaller type sizes are more spread out than their larger counterparts?

See how character spacing doesn't look the same when you change type size? Sketch is a great help with this - when you change a label's text size, the character spacing scales automatically - but this isn't helpful to your iOS dev: if there are 8 different type sizes in your app, then they've got 8 character spacing values to worry about, rather than just a single tracking value.

So: we need a way to represent tracking in code, and when we came up against this problem, I couldn't find a single implementation of this on the web - which meant we had to make one.

I created lots of PSDs and Sketch files, comping up different tracking values and character spacing values, and doing pixel comparisons to figure out what lined up. Once I found a match between tracking, character spacing, and font size, I put it in a spreadsheet and worked out some theories as to how the two were related - and after a bit of algebra, I found that it was actually a *very* simple relationship:

characterSpacing = fontSize * tracking / 1000

At first, I thought, "1000? Really? That's the magic number?" - but then this hypothesis totally worked on other yet-untested scenarios, so that's where we landed.

Here's how you can put it to use:

If you want an easy way to convert between character spacing and tracking, I have a handy Soulver document here

If you're a dev, and you want a category method that'll return a properly-tracked attributed string, you can find one over on GitHub here.

+ (instancetype) dvs_attributedStringWithString:(NSString *)string
 tracking:(CGFloat)tracking
 font:(UIFont *)font
{
CGFloat fontSize = font.pointSize;
CGFloat characterSpacing = tracking * fontSize / 1000;
  NSDictionary *attributes = @{NSFontAttributeName: font, 
  NSKernAttributeName: [NSNumber numberWithFloat:characterSpacing]};

  return [[NSAttributedString alloc] initWithString:string 
 attributes:attributes];
}

Here's how you'd use it in practice:

self.label.attributedText = [NSAttributedString dvs_attributedStringWithString:@"DEVSIGN"
tracking:200
  font:[UIFont systemFontOfSize:17.f]];

Look At Your Fish

I love this essay:

Fish

If you aspire to be really, truly great at creating things, you need to have an eye for what "great" means. There's no better way to build that skill than to take a long, hard look at the things you cherish, and stare at them over and over until you can start to pick apart the myriad decisions, constraints, and bits of craft that went into making something.

Look at your fish.

Look at it.

Go back, and look at it again.

You can learn a ton by copying the greats. (Don't go out there and claim the copy as your own, but consider it a kind of homework.) Hunter S. Thompson once re-typed The Great Gatsby and A Farewell to Arms, so he could more deeply understand Fitzgerald and Hemingway.

If you're just getting started with making apps? Find your favorite app, and try to duplicate its interface in Sketch, or try and replicate a part of its interface in Xcode. 

Look at your fish.

Prototyping, Part I: Static Prototypes

In my work, I tend to use two kinds of prototypes:

  1. Static (or "tappable") prototypes. These help to explore, explain, and test user flow concepts pretty quickly. They take anywhere from 5 minutes to an hour to create, and 
  2. Dynamic (or "animation study") prototypes. These work to describe the animations, easing curves, or custom transitions in your app. These take anywhere from 30 minutes to a day to create.

For this post, I'll focus on the first use case - creating static prototypes.

When to use a static prototype

Whether you've got sketches, wireframes, or visual designs, it's really helpful to create a walk-through to demonstrate a user flow.

I love using static prototypes in place of a huge wireframe deck - in the same way that a photograph is worth a thousand words, a simple prototype is worth a hundred design comps. You can express in a few minutes what would otherwise take a long, drawn-out meeting to walk through a wireframe deck.

Outside of presenting designs, static prototypes are a great, lightweight way to make your wireframes or visual comps interactive for usability testing.

How to make a static prototype

There are a ton of tools out there, and I've tried many of them (Flinto, Keynote, Briefs, among others) but by far, the best tool among 'em is Flinto. It's crazy-fast to build, install, demo, and update the prototypes - and just enough fidelity to get the point across without trying to be a replacement for your design or development tools.

First, you'll need some screens. Either sketch 'em out on paper, throw together some wireframes, or export your visual designs to flat image files.

Next, go to flinto.com, create a new prototype, and drop those screens in. I'll leave it to you to learn the tool - it's super intuitive.

A few tips to really make the prototype sing:

  1. Use transition animations. There's the standard UINavigationController-style push, pop, present, and dismiss - I'd skip the rotational-flip ones, unless you're some kind of monster - but the crossfade is often a nice choice if you're changing out the content while remaining in the "same" view.
  2. There's a timer function; you can have the prototype pause for a moment on a screen and then transition to the next screen. This is great for prototyping the loading state of a screen.
  3. Pay attention to the status bar style. It's a limitation of iOS Safari that you can only have a single status bar color for your fullscreen webapp - so you'll need to choose either light, dark, or a black background status bar for the entire prototype. Choose wisely.
  4. Make your lists scrollable. If you're creating a prototype with long UITableViews or UICollectionViews, use a tall image for the screen - say, 2000 pixels high - and Flinto will make that screen scrollable. Then, use the Top Slice and Bottom Slice tools to carve out any UINavigationBars or UIToolBars - those will remain fixed in place as you scroll the rest of the content.
  5. Test on-device and in-browser before you present. You might have status bars in your comps, and Flinto has some smart tools to try and work around it, but you'll want to test the prototype on-device and in-browser for any glaring issues before you present your work to a larger audience.
  6. Use the generic "back" transition. One of Flinto's real strengths is its generic "back" transition - essentially, it performs the inbound transition in reverse, back to whichever parent view you came from. (Very useful when you have, say, a user profile view that gets accessed from three different places - don't make three profiles, use one, with a back transition.)

Using the prototype

Use these static prototypes to get a feel for an idea before writing code, or to share a user flow far more effectively than you could with a set of wireframes.

When it comes to using your prototype, Flinto really sings:

  • If you open the prototype on your laptop, you'll get a fullscreen presentation mode, ready for sharing. If you open the prototype on your phone, it'll behave nearly as if it were a real app.
  • If you share the prototype with others, you can make updates, and all of the prototypes will be updated the next time they're launched. You can even nuke the URL, so that if you've changed the design significantly, there's not an old, crusty ghost-design floating around.
  • You can choose whether to show "hints" in the prototype - if you're doing usability testing, turn them off; if you're sending a prototype out to team members, turn them on.

So: if you haven't already, go check out Flinto, and enjoy! If you have any questions, Flinto's really quick to respond on their Twitter account - or you can shoot me a question there, too.

Sketch Tip: Default Styles

By default, a new object in Sketch looks like this:

sketch-default.png

For me, this is almost never what I want. In the end, most shapes in my design will have different styles applied to them, and they're never the default. When I'm using Sketch, I prefer my default shapes to be what I call "measuring rectangles".

A good measuring rectangle is partially transparent, and has a color that you wouldn't normally use in your design. This way, it'll allow you to see when something's hidden behind it as you lay out its frame, while standing out from other objects in your design so it doesn't get lost.

For me, this feels right if I do a 50%-opaque red shape as my default style:

sketch-red.png

To set your own default style, create a shape and give it a 50% red fill:

(Make sure that the fill color is 50% alpha - the layer opacity should remain at 100%. Otherwise, whenever you drag a photo into your designs, it'll show up as transparent - yuck!)

Then, with the shape selected, go to Edit > Set Style as Default, and you're done!

 

 

Animating with Springs

Before iOS 7, you'd perform many interface animations with something like the following:

[UIView animateWithDuration:0.3f
    animations:^{
        //animations here
    }];

Now, though, you've got something far more powerful - springs:

[UIView animateWithDuration:0.3f
    delay:0.f
    usingSpringWithDamping:0.7f
    initialSpringVelocity:20.f
    options:0
    animations:^{
        //animations here
    } completion:nil];

Springs make the interface feel alive - they allow for an animation to carry a sense of momentum with it. Most of the animations in iOS 7 use this method, and while it's a bit of a trick to find the right damping and velocity values, it's really easy to find 'em if you're using Tweaks.

Damping can range from 0 to 1 - it's like the "brakes" on the animation. A value of 0 will let the animation oscillate on its spring. A value of 1 will bring the animation to rest without oscillation.

Velocity is the initial speed of the animation - how hard the animation is "flicked" at the start. I'm still really fuzzy on what the right value is to use here, but again - hook this up to Tweaks and you'll find it in no time.

There's a wonderful talk from WWDC 2014, Session 236 "Building Interruptible and Responsive Interactions" that talks a lot about handing off between animation blocks, and choosing the right values for your damping and velocity values.

Using Facebook Tweaks

Facebooks' Tweaks framework is amazing. We used it heavily in the final stages of building Blixt, and it really helped bring the interface to life with iteration without requiring a ton of development to get there.

The magic of Tweaks is that you can drastically alter your app without requiring a developer to lift a finger - meaning that you can really sweat the details on animation timings, image manipulations, blur effects, layout spacing, opacities, and a thousand other things without needing to build and run.

See, when you're making an app, there are a bunch of little "magic numbers" that make the interface come to life. As an example, let's look at a typical animation:

//Animation 
[UIView animateWithDuration:0.32f
    delay:0.0
    usingSpringWithDamping:0.78f
    initialSpringVelocity:30.f
    options:0
    animations:^{
        self.signUpButton.alpha = 0.2f
        self.signUpButton.frame = ...
    } 
    completion:nil];

See all those numbers? Iterating to arrive at those values can take a lot of time if you're building and running for each small tweak.

Enter Tweaks: Just shake the phone, and if you're on a debug build, you'll see an interface appear to help you adjust these magic numbers.

There's a bit (but not much!) setup required; it will likely only take you a few minutes to get up and running.

Then, go through and replace your magic numbers with FBTweakValues, like so:

CGFloat duration = FBTweakValue(@"Sign Up", @"Animation", @"Duration", 0.32f);
CGFloat springDamping = ...
CGFloat springVelocity = ...
CGFloat signUpAlpha = ...
[UIView animateWithDuration:duration
    delay:0.0
    usingSpringWithDamping:springDamping
    initialSpringVelocity:springVelocity
    options:0
    animations:^{
        self.signUpButton.alpha = 0.2f
        self.signUpButton.frame = ...
    } 
    completion:nil];

Now shake away, play with the values, and you'll find yourself quickly arriving at an interface with just the right magic numbers to truly make it feel alive. Rinse and repeat.

Sketch

Photoshop is expensive, and has a very steep learning curve. It's also not focused on software design - which means that while it's powerful enough to do just about anything, it's not easy to use.

The problem with finding an alternative was that they were no good, either - they were either missing needed features, or their interfaces were too cumbersome.

Sketch 3 is the first great alternative that I've seen, and it's become our main design tool at work. There are a ton of things that make it wonderful; here are a few of mine.

Variable-height artboards. in Photoshop, I prefer using layer comps to hold multiple screen states in the same design file. Trouble is, some of those screens are taller than others (for example, maybe you have a long sign-up form, but a short forgot-password view). With Photoshop, you kinda have to choose one screen height. Sketch removes this limitation in a beautiful way with its artboards.

Easy grids. You can easily duplicate an object to make a grid - or if you're duplicating an artboard, it'll drop in at exactly the right place. 

Sketch Mirror. You should always, always, always preview your designs on-device. There are tools for doing this with Photoshop (Skala Preview and XScope) but neither works reliably for me - I often have to reset them every few minutes. Sketch Mirror, though? Works every time. 

Keyboard shortcuts. Zoom to a selection, zoom to canvas, rename selection, group, ungroup, toggle selection visibility, toggle selection lock - these keyboard shortcuts are your friend. Enjoy them.

Adjust the radius on a rounded rectangle. Yes, Photoshop technically has this, but it is far too finicky to be reliable. Sketch is great at this.

Easy asset exporting. I love using Slicy for exporting assets from Photoshop, but Sketch's built-in tools are vastly better.

Background Blur. This didn't exist in Sketch 2, which is partly why we continued to use Photoshop - with iOS 7’s blur effects, you need background blur to get your nav bars to look right, and while there is a clever workaround with Photoshop's Smart Objects, it's still relatively painful - Sketch makes it vastly easier to design with blur.

 Presentation Mode. Press CMD+. to toggle Presentation Mode. No need to export comps to an InDesign file to then generate a PDF - just press the key and you have a great way to handsomely present, right from the design file.

The whole team can use it. Sketch is vastly more affordable, and it's a native app, so developers and testers feel more comfortable jumping in to the files, rather than having to wait for a designer to pull color, opacity, typography, layout, and z-axis stuff out of the costly-and-hard-to-learn Photoshop. It will save you time.

---

Sketch certainly has its problems, too - for one, the performance can get pretty slow with a complex design file - but it's improving faster than Photoshop is, and is so tuned towards software design that it's been a complete delight to work in full-time.