I’ve talked a lot on this blog about the circular scroll-view/table-view thing I created for my metronome app, and while I’m very pleased with how the whole thing turned out, I was still annoyed that I hadn’t figured out how to do one thing: give it inertia like UIScrollView. A commenter on one of the posts even asked me about inertia, and I basically just flat out said I’d like to, but it would be too hard! But after that, it just kept bugging me; there had to be a way. Inertial scrolling on iOS – and now on OS X too – just feels so nice. It’s one of those things that many people probably don’t even think about or notice until it’s gone. After feeling the smooth scrolling and realistic deceleration of UIScrollViews, my little rotary control just feels like garbage. I mean, come on, you give it a little flick and it stops rotating immediately when you lift your finger!? Lame! Frankly, my control just doesn’t feel quite completely at home on iOS yet, and I want to change that.
Maybe UIScrollView Can Help!
I got really exciting after watching the WWDC 2012 video for Session 223: Enhancing User Experience with Scroll Views. In the presentation, they give an excellent demo of how to leverage the physics of UIScrollView even when your app uses OpenGL. The UIScrollView is not used for any drawing, but by passing touches through it, listening for the scrollViewDidScroll, and reading the contentOffset, you can enjoy all the benefits of UIScrollView like inertia and edge bounce without actually drawing your content in the scroll view itself. Maybe, I thought, I can use that same trick on my circular scroller. Using the same techniques from the video, I was able to get up and running with the same circular action as well as a bit of UIScrollView provided inertia, but it just did not feel right at all. The geometry of linear scrolling and deceleration just don’t line up with what I need in terms of angular rotation.
The video technique didn’t work out quite like I had hoped, but I still took away one key idea: implement the inertia in the touch handler, not in the circular view itself. Seeing as how no one has seen the details of my implementation, that might not make sense to anyone but me right now, but I’ll try to explain. At its most basic, my control has two parts: the transparent view which interprets the touches as circular movements (a gesture recognizer, if you will, although it’s not an actual UIGestureRecognizer subclass) and the display view that receives notifications of those movements and rotates, swapping elements in and out sort of like UITableView cells. I had already implemented a bit of animation in the view component that allowed it to center on a certain cell, and I kept trying to think how I could make this also work with a longer rotation, and I was running into a lot of problems with the cell swapping during the rotation animation. But, if I implement the inertia on the control side and use the same delegate call for rotation that comes from an actual touch OR from the inertia, the view side doesn’t even know the difference. And it actually worked pretty well without much modification at all!
Check out the Repo
So, here’s the real/final point of this post: I’d love to get this whole rotation control thing (or most of it, at least) out into the wild as an open source component, but it’s been hard finding the time. It’s a fairly complex API, and I don’t feel like I can just release it without some more clarity and focus, as well as a nice sample project. So, I’m going to try to get it out there piece by piece if possible. I’ve put up a little bare bones project on GitHub – my first! – to test the waters. Let me know what you think, and of course, feel free to fork it and make it better. Right now it’s just a simple version of the rotation control view (RDDRotationControlSurface) which is hooked up to a UIViewController delegate that spins a UIImageView based on the input. Wow, that sounds way more complicated than it is – it’s just spinning a little picture of a very nice-looking German (or Austrian; can’t remember where this picture was taken) man in a cool getup. Don’t ask me why I chose this picture; it was there, and I went with it!
PS: You may be wondering why I didn’t go with a UIGestureRecognizer subclass for this. As far as I can tell, the gesture recognizer API would not work very well with something like this which needs to continue sending updates even after all touches have stopped. So, in the end, I’d still end up with a UIView subclass of some kind. Doesn’t mean GR couldn’t help here, but I just didn’t go that route.