Designing a Step Goal Live Activity

Design Notes Diary

Today I was working on designing a steps focused live activity mode for Pedometer++.

In the initial version of workout tracking I focused on distanced based UI, which fit well with the overall structure of the way workout tracking functions. However, I quickly started to get lots of feedback that users would like to track their steps during their workouts…which makes a ton of sense for a step tracker!
Here is what the distance based workout mode live activity looks like:

The bar underneath shows your progress towards a chosen distance goal with the markings indicating mile/km markers along the way.

I’m very happy with this overall look and design language, so I wanted to adapt it to the context of tracking users goal towards their step goal.

Design Iterations

The first thing I tried was thinking through using the same two-tone colored look but using the goal based colors I used throughout the app currently: Red for below 50%, orange 50% - 100%, green above 100% (or the color blind friendly alternative colors).

I tried this out but very quickly discarded this look. It is just weird looking, worth trying but not the winner.

So instead I decided that I’d do something where the color of your current goal status fills the whole goal bar, with the remaining section left gray:

That works really well. I like how it follows the same overall look of the distance workout activity but in a way that cleanly shows your progress towards your step goal.

The question, however, remains of what to do once you reach your goal. In the distance goal case I just cap the graph at 100% and say “GOAL ACHIEVED!”. That didn’t feel like the right path here. You could easily be far exceeding your step goal and so I want to keep the live activity relevant here.

Instead I thought I could borrow the mile markers from the distance graph but here instead have them be multiples of your goal (1x, 2x, 3x, …). With the graph thumb staying pinned to the rightmost edge, but the line rescaling as you go.

I like were this is going. The graph stays relevant as it is surpassed, but it is always clear that you have reached your goal.

Something which didn’t quite feel right was reusing the rectangular markers for the milestones. In the Apple Watch app for Pedometer++ I include this screen:

In this ring display the chevron indicates how many times the user has met their goal (1x = 1 chevron, 2x = 2 chevrons, …). I wanted to mirror this feeling here, so rather than using rectangles I’m instead I tried placing chevrons at each of the milestone points.

Nice! This works really well for what I’m trying to communicate…but I very quickly hit a failure state for it. When I update the renderer to give multiple chevrons based on goal completion things can fall apart very quickly if you dramatically exceed your goal:

So that won’t do. Instead I shifted to swapping out chevrons for the fixed width numerical strings(“5x”, “6x”,…) once you reach a certain number of goal completions. Then switching them all once you get above 8x completions to save even more space.

Generally speaking reaching 10 times your goal would represent an extraordinary physical accomplishment (though of course users can set their goal very low, in which case it is more possible).

I feel pretty good with this setup. It is beautiful and coordinates with the Apple Watch app/complications for the typical goal completions and then has a reasonable fallback.

The next step was to incorporate it into the actual live activity UI.

I think it fits well here and is good enough to start testing with. The funny thing with doing a live activity which is step based is that it is harder to ‘simulate’ the users movement. With GPS based activity it is pretty straightforward to do in the iOS simulator, but for motion based step tracking there is no substitute for physical tracking. So I pulled out my desktop testing rig:

Which is always feels very fancy, if a bit silly.

Transparent Activities

The last thing I wanted to test out was how this Live Activity looks when the user chooses the “clear”/”frosted” look for their Live Activity. In this case I need to make sure that the graph bar still looks good.

All I needed to do was to switch over to using a .destinationOut blend mode when drawing the chevrons and outlining the graph thumb.

The result looks pretty good to my eye and overall I think this design works. As with all designs I’ll have to live with it for a week or so to be sure, but it is off to a promising start.

David Smith




Ghostly Corners with Overlaid Shapes

Design Notes Diary

A small coda for yesterday’s exploration of corners.

I recently was able to finally track down a bug which has been frustrating me for a long time. Widgetsmith’s interface includes several places where I displays previews of widgets. In these views I could occasionally see dark, ghostly outlines along the edges of the preview. I’ve exaggerated the effect here a bit to make it easier to see:

I tried all manner of things to fix this but was thwarted at finding a lasting solution until last week.

To solve this I created a super simple testing project to see if I could isolate the issue. A simple ZStack with two identical RoundedRectangle views on top of each other:

This results in the following view:

You can see the edges of the lower black round-rect peeking through the top rounded rectangle. Intuitively this makes absolutely no sense to me. The two shapes should be completely identical so overlaying them should fully occlude the lower shape…but that isn’t what happens.

I posted about this on Mastodon and was very helpfully pointed in the right direction as to the cause. Anti-aliasing.

The shapes aren’t in fact actually solid, instead when they are pixelated their edges will acquire small bits of transparency along an irregular grid to help smooth out their visual appearance. Which is then how the lower view can bleed through into the foreground.

This is more pronounced when using the .continuous shape style because more of the edge isn’t straight but it will also occur when .circular styles. In fact, the artifact is incredibly visible if I switch to a stack of Circle shapes.

The solution to avoiding this will vary based on your app’s needs. I bring this up mostly to illuminate this as a potential design need and something to look out for whenever you are stacking similar shapes. If you see weird ghostly outlines of hidden shapes, now you know why.

For me, the lower shape is actually there to account for situations where the topmost view includes some transparency, so it doesn’t actually need to extend to the edge of the view completely. So I just add a .inset(1) modifier to the background shape and the problem goes away.

David Smith




Changing Corners

Design Notes Diary

One of the most challenging questions I find myself facing when doing visual design work is: “What is good enough?”

Design is an exercise in constantly balancing tradeoffs between simplicity and complexity. Seeking to find that elusive, but satisfying, point where the two balance each other out and you end up with something that is both beautiful and functional.

I recently ran into this in a very concrete way when I was working with what is perhaps the most common visual element in the modern iOS design language…the rounded corner.

You could easily think that the rounded corner is a pretty straight forward thing. Just take a rectangle and then smooth off its edges, how hard could that be? Well the answer is tremendously hard and has taken me on a bit of a journey both in terms of my own tolerance for settling for “good enough” and exploring some mathematical topics which go well over my head.

This design rabbit hole got started when I was working on an app for displaying your current heart rate as broadcast from your Apple Watch.

The visual design I settled on for this looks like this:

As you can see there are several rounded corners that I have incorporated into this design. If I didn’t I’d have a UI that looks like this:

Which isn’t ‘bad’ but just doesn’t quite fit in with the round all the things aesthetic which is popular on iOS these days (and I’m not a good enough designer to try and start my own new trend).

So I’m left to work out how to round the corners of this large green shape.

When you are just making regular old rounded rectangles this is very straightforward. SwiftUI includes a function for RoundedRectangle(cornerRadius: 16, style:.continuous) drawing beautifully smooth corners if you use the .continuous style option.

These continuous style corners are what you want to use if you want to fit in on iOS. They are used all over the place within iOS’ UI and system element, and really are just beautiful.

If you used the .circular option (in red) you’d end up with a quite serviceable look but one that just isn’t quite “as good”.

The corners have a small discontinuity in them which is super subtle but once you’ve trained your eye to see it you can’t unsee it.

Non Rectangular Corners

This is all well and good and works fantastically for 90% of your corners needs, but what do you do when you don’t want a rectangle? For example, in my above case I need to have variable corner radii and a shape which grows out of the top of another rectangle with a smoothly curving join.

Or alternatively, what if you just want a rectangle with different corner radii on each corner? For example, in my legibility background I use in Pedometer++’s Live Activity:

Over the years I have accumulated a number of tricks for creating smooth corners, which range from “kinda good enough” to “Chef’s Kiss”.

Kinda Good Enough

By far the easiest method I know of to create rounded corners is to use the addQuadCurve method on Path which lets you very easily draw a curve between two points.

In this case you draw your rectangle as you would normally but stop one “radius” distance from the corner, then draw a quad curve to a point one “radius” away from the corner along the next edge, using the corner itself as the control point.

This works shockingly well, and is super easy to think about. Minimal math is involved and it can easily be adapted to a variety of corner needs.

But if you look closely it has a discontinuity at the join and won’t match the corners of system . continuous shapes. The latter problem you can sort of fudge by increasing the “radius” of the corner a bit (around 8% usually does the trick).

As you can see this is super close to the corner of the system control (in black.). This is really quite good, and honestly if you stopped reading here only your most eagle eyed users would ever notice a difference.

But in the darkest watches of the night you’ll awake with the lurking suspicion that you could have done better.

Pretty Good

The obvious next place to try is to follow the example of the system controls and draw the corners using a circular approach.

Here we again draw our rectangle as usual but when we get to a corner we stop exactly one radius away from the corner and join the edges with a circle.

This results in a corner which is extremely close to the system shape, without the need to fudge the radius.

But the result actually has an even worse discontinuity from the Quad Curve approach, and personally I find that getting angles involved always leads to complexity and challenge.

Chef’s Kiss

Alright, this is part where things get well over my head.

Let me make a little confession, I don’t really understand Bézier curves. I understand the principle of them, but they always feel like this mystical black box where you input magic numbers and output beautiful curves. They are not intuitive at all, and if you get a value even slightly wrong then wild things can happen.

However, sadly if we want our curves to match the system ones then, as far as I know, we have to use them(😱). Which is suitably terrifying and something I avoided for a long time.

The best implementation of smooth corners that I could find was in this React Native project by Everdrone. It involves some serious magic values, and also this fascinating visualization of the curvature of corners based on this analysis.

All very cool, but way over my head. But I understand enough of what is happening here to follow that there are some magic values to determine a handful of Bézier curves which when combined together lead to the smooth, system corner I’m after.

The code is complete gobbledygook…

But the result is virtually identical, and without any discontinuities:

Visualizing the control points doesn’t really help me either:

I can sort of see what is happening, but not really how it is working. Though the reality with a situation like this is I’m not really sure how much that matters. Math is math, and so long as I can safely use the math which is behind this form it seems safe to use.

Conclusions

In my own design work I find myself shifting away from the simple to the ‘correct’ more and more. I’m not really sure if this change has a tangible impact on my users, but I do know that it has a tangible impact on me. I feel better about my work when it is ‘correct’ even if it is a bit slower to create. Though I also know very well that this is a trap into which I am placing my foot. I don’t want to get bogged down chasing perfection, but I also shouldn’t give up too quickly when facing difficult problems. The real skill in design is being able to consistently find that line.

In this case by putting in the effort to perfect my corners, I can now nest the top tabs against system rounded rectangles and the result is a perfect fit.

David Smith




Speedrun Design: Heart Rate Zone View in SwiftUI

Design Notes Diary

Recently I’ve been doing a lot of heart rate based training. This involves doing some activity (rowing, biking, running, etc…) with the goal of getting my heart rate into specific range and keeping it here. I find this is a really helpful way to improve my cardiovascular health, while putting minimal strain on my body more generally.

Heart rate zone tracking was added into the main Workouts app in watchOS 9.

This works great if I’m doing an activity where checking my wrist is straight forward (like running), but I found it really inconvenient to use while doing activities like rowing or air bike where my wrist is constantly moving.

What I wanted was a method to project my current heart rate onto my iPhone which I can then put somewhere in my line of sight. I looked around for an existing app which did this but I couldn’t really find any, so of course, I made one.

This app is ridiculously simple. It is just a display for my current heart rate. I don’t really expect to turn this into a product, it is just something for me to use. I could imagine all manner of additional things I could add to such an app, but for now this is just a remote display for my heart rate.

I wanted to build a design which made visual references to the watchOS heart rate zone stuff (it is just so beautifully designed), but that design doesn’t really scale up well to a full iPhone screen.

My core goal was to make something which is incredibly clear about what zone I am in, even when only quickly glancing at it or seeing it in my peripheral vision.

I recorded my design evolution as one of my “Speedrun Design” videos (speed up 20x):

This is the final result:

The screen is essentially all the color of the current heart rate zone. I found that I can very easily see this green from across the room or out of the corner of my eye. So I can easily know if I’m in the right zone or not.

The design actually works pretty well as an Apple Watch app too:

This little design exercise was a lovely way to start off my week. If you are looking for a little project to get your creativity flowing, I’d recommend giving it a go. Set a timer for 60 minutes and see where your design instincts will lead you.

David Smith




Stretched

One of the strange ‘benefits’ of having been doing my current job for nearly 15 years now is that I can see the cyclical patterns inherent in the life of an independent developer. The last few weeks were a reminder of one of the more challenging one of these patterns.

Stretched.

There is a fabulous passage in the The Fellowship of the Ring where Bilbo tells Gandalf that he feels “Stretched…like butter that has been scraped over too much bread”. That turn of phrase has really resonated with me recently. It is an eloquent summation of how I was feeling at the start of April.

The beginning of March saw the launch of a major, total upgrade to Pedometer++ and then a few weeks later the recognition and launch of my 100 Million Download Update to Widgetsmith. These two updates have wholly consumed my working mind for months now, and I’m very pleased with how they both turned out.

The challenge I find is that once I finish a major project I often end up with that stretched feeling. Like am all out of butter and the basket of previously backlogged bread[tasks] is just too much to handle.

I don’t think it is just overwork, though perhaps partially that. Particularly the interesting thing I notice is that the feeling only emerges after the launch and not right before. Something changes in the moment I hit ‘Release’ which radically shifts my mindset.

As I experienced this feeling with these updates I’ve tried to really interrogate the feeling and see if I could find out where it was coming from.

The best analogy I could come up with is that of a rubber band.

Imagine pulling out a rubber band to its full extent. In that moment the rubber band is being rightfully employed in its primary function, it is living its best life, it is right in the groove of what makes a rubber band a rubber band. Then imagine releasing the rubber band and letting it fall loose again. Now the rubber band finds itself without direction and is aware that in the process of its useful employ it has been altered slightly, that it is now slightly looser.

I think this is the root of the stretched feeling I so often experience after the release of the major update. The period of time leading up to the launch is filled with productive energy, excitement and focus. However, the moment it is released that sense of direction and purpose almost instantly disappears, replaced with only the awareness of the mental and physical impact those labors have accrued.

Like I said, the benefit of having experienced this feeling enough times is that I’m no longer surprised and alarmed by it. I know it is an old (if not completely welcomed) friend, and so I now have my strategies to deal with it.

My first strategy, like Bilbo, is to yearn for The Mountains. There are few things as therapeutic for my psyche as spending time surrounded by mountains. The distant hum of wind in the fells and of water coursing down their slopes finds resonance with my soul. So off to the mountains I went, on a weeklong family trip to the Lake District in north-west England.

While out there I tried to pay attention to when my time there had cleared out the cobwebs and I felt like myself again. For this trip it took six full days to feel restored to my typical self again. That is much longer than I would have guessed if you had asked me before I left.

My second strategy is patience. Not particularly dramatic but just the understanding that I won’t feel fully productive, engaged or focused at work for several weeks as I get into the next chapter of my work again. The build up to major updates is a hard act to follow, especially when the next chapter is angled towards smaller and less dramatic (though no less important) outcomes.

The true benefit of having now gone through this several times is to know that strategies do work. That I will again overcome that feeling of being stretched and instead gain a productive posture again. But it will take some time.

David Smith