This was originally published to CocoaCovers.com in March 2014. That website is now deceased, so I am re-posting the content (this single post) to benpackard.org.
Tweetbot is a Twitter client by Tapbots. It is full of thoughtful, playful interactions. One of my favorite details is the zooming header image effect when you scroll a user’s profile:
It’s a neat way to handle the extra canvas exposed by the elasticity of the scrolling behavior, which can otherwise look a little awkward when the uppermost content is a photo or some other visually busy element. This is especially true when a toolbar is present – Safari is a good example of the default behavior:
There’s nothing egregious about this interaction; it’s just a little clumsy-looking. A big gray box, smack in the middle of the more artfully crafted toolbar and content. Tweetbot turns this same scenario into an opportunity to delight the user.
There are three silmultaneous animations occurring as the user scrolls. First, and most obviously, the image expands to fill the available space. Note that the image maintains its aspect ratio, though – it never stretches. Also, if the background image happens to be taller than the content, the hidden edges of the photo are fully revealed before any scaling takes place.
We also have the gradual fade-out of the user’s profile information, and the un-blurring of the image. If you look carefully, the image also brightens as it is revealed – presumably to ensure enough contrast with the white text in its resting state.
Let’s start by creating two main views inside a scrollview. The ‘profile view’ will hold the circular photo and all of the user info. I’ve given it a gray background for now so its easy to identify. Below the profile view, the ‘content view’ is just a screenshot from the app since we’re not going to be implementing that part.
Next, we place the header image underneath the profile view. The trick here is to make the image its own independent view in the scroller – a sibling of the profile view, not a child. This will let us adjust it with much more freedom since it won’t be constrained by the bounds of the profile view.
Also note that I’ve used an image that is taller than the profile view so that we can test the zooming action works correctly later.
Now we will start animating the zoom. First, we need to re-position the image as the user scrolls to make sure that the photo is always vertically centered between the bottom of the toolbar and the top of the content view. Remember that the profile view doesn’t grow in height – it just moves down the page with the rest of the content. So the center of the image actually moves away from the center of the profile view as the user scrolls. Since I’m a fan of autolayout (we do exist), I added an
NSLayoutConstraint that does exactly that. For every two pixels of scrolling, one pixel of vertical adjustment is required to maintain a vertical center alignment.
Now lets get the zoom working. We can add another constraint here to set the size of the image view to match the native proportions of the image itself. Just like before, this gives us a constant to tweak as the user scrolls.
Its a start, but the image is stretching. This is easily rectified using one more layout constraint to maintain the original aspect ratio.
Next we need to blur and darken the image, and only reveal it incrementally. If we place a blurred and darkened copy of the image into the view heirarchy we can just update its transparency. I found a nice implementation of the necessary image adjustments by Brian Clark and slotted my new image into place above the real photo.
The blurry image is constrained to maintain the same position and size of the original photo. Now we can simply fade out both the profile info and the blurry image to reveal the photo.
We’re almost there. But we’re still missing that nice detail from the original – the hidden top and bottom slivers of the image should be revealed before any zooming takes place. This is accomplished by comparing the height of the image with that of the profile view and storing an ‘overlap’ value that represents how much of the photo is hidden. Only once the user has scrolled beyond that value does the scaling begin.
In the Wild
I added this same detail into my baseball app No-Hitter Alerts, though I didn’t need the blurring behavior since the image is a known quantity and doesn’t obscure the text.
The source code for this implementation can be found on GitHub. Thanks for reading!
A long overdue update. More than an update, in fact; a full re-write.
Food Files was my first ever app and it was a lot of fun to revisit the design choices and the implementation I settled on the first time around.
Available for free on the App Store.
A new version of Scored is now available on the App Store. A few features added in the last couple of releases:
- Added a ‘Today’ widget – view live scores and today’s results directly from your Today screen.
- Support for iOS 8 and iPhones 6/6+
- New alert sound
- Redsk*ns mode
- Teams can now be swipe-deleted directly from the settings screen
The app also has a new icon designed by Shahriar Emil.
A couple of apps I’ve developed recently:
- Modal Buddy – a fun, effective way to learn and practice modal guitar scales and take your soloing to the next level.
- Slender Seven – healthy and delicious recipes using seven ingredients or less.
These were both very different clients from a requirements perspective. The guys at Ninebuzz had a very specific concept in mind and comprehensive design documents ready to go. Nikki at Slender Seven gave me free reign to build the app I thought best met her requirements and we worked together on defining the right user experience. I enjoy both approaches and both apps turned out great.
Screenshots of each can also be found on my portfolio page.
From time to time I am asked for travel tips by friends visiting London. Instead of recycling the same email I have started pointing people to this page.
Yesterday I launched Scored, a new sports app for the iPhone. I am frustrated by the amount of cruft that comes with sports apps – lots of visual noise, obnoxious ads, etc. I want a fast, clean sports app. When I want to check a score, I shouldn’t have to wait while a Godzilla ad downloads and then scroll through some jerky spaceship-inspired design.
Exactly one year ago I created IsPizzaHalfPrice.com. I consider this to be my most enduring and noble contribution to the progress of humanity.
To celebrate, I went digging into the pizza database. Here’s what I found.
Overall, there is some pretty consistent half-price pizza to be had throughout the year. As you might expect, the probability of half-price pizza increases when all participating teams are in season. We are pretty much at Peak Pizza right now, with six qualifying days already in the first week and a half of April.
Breaking down the stats by franchise tells a surprisingly accurate story of each team’s season. The Nationals start slowly, surge at the end of the season, but drop off the chart before October arrives. The Wizards wobble but finally make it stick over .500 with a hot March. The Caps seem to have been weirdly consistent, but consistently average.
All kidding aside, I’m pretty giddy about the response the website and the accompanying free iOS app have received over the last year. Thanks to everyone who visited and passed on your kind comments. Here’s to a pizza-filled Nats season and Wizards post-season.
Nerdy chart note: Don’t try to add up the numbers for each team and expect them to total the overall stats – on some days, multiple teams qualified for half-price pizza. What a waste!
I really enjoyed recreating the Dark Sky scrolling animation and have plans for a few more interactions I would like to attempt. I am going to start collecting these at a new website, CocoaCovers.com.
I consider these ‘covers’ in the sense that they are tributes to the originals. I’m hoping that they will be received in that same spirit and not considered rip-offs. I’m picking small, delightful moments to recreate from some of my favorite apps and sometimes adding my own twist. I have no access to the original source code and I’m not trying to peek under any covers for clues – these are my own interpretations of modern classics.
It really does remind me of the way I would learn to play a song on guitar before the internet was prevalent. Pressing an ear against the speaker, slack-jawed and confused.