• Rotating a Paged UIScrollView

    Adding rotation to your app is one of those things that users often expect, but which can really bump up the complexity of development. In most cases autosizing your content works nicely, but it’s not unusual to run into things that just don’t behave quite as you might expect.

    I came across one of these recently with a paged UIScrollView. The content was a number of full-screen photos, which allows the user to swipe left and right to change photos. This is a fairly common use of a UIScrollView, and has a nice bang-for-your-buck feeling about it – it’s pretty easy to setup, and results in one of the most natural way to view your photos.

    That is, until you enable rotation. The first problem that you’ll likely encounter is that all your pages suddenly overlap each other (or have big spaces between them). This is easily fixed by updating the origin of each page to reflect the new dimensions of the UIScrollView.

    The next problem you’ll come across is that the UIScrollView content offset is still in the same position as it was prior to rotation. This usually results in your UIScrollView being stuck between two photos, with only a portion of each photo being visible. If you’re really unlucky, your content offset will be past the new width of the UIScrollView, so that you can’t see anything at all. Either way, as soon as you try to move the content, it will jerk back into the right position for one of the pages (though, not necessarily the right page).

    The easiest solution to this is to simply update the content offset once rotation has completed. As long as you know which photo you were viewing before, and what the width of the UIScrollView is, this is easy to do with some simple maths. However, this approach is not as polished as it could be. You still get all the problems mentioned previously, and the only difference is that the UIScrollView jerks into position automatically, rather than when you try and interact with it.

    We can do better. The next approach would be to animate changing the content offset. UIViewController exposes a very handy method that lets you know when the rotation is about to occur:

    willAnimateRotationToInterfaceOrientation:duration:

    So, we simply make a call to change the content offset inside this method:

    [self.scrollView setContentOffset:CGPointMake(X,Y) animated:YES];

    However, this doesn’t quite work as expected either. As the rotation animation occurs, you can see the edges of the other photos move in and out. You start and end in the right position, but you shouldn’t see any parts of the other photos as the rotation is going on. I’m not entirely sure why this is – possibly because the content offset animation is handled using the animated: parameter, rather than in a block based animation with the same duration as the rotation. A handy trick when using the iOS Simulator is to tap the Shift key three times. This turns on slow motion animations, and make it’s much easier to see what’s really going on.

    The next approach might be to manually update the content offset one pixel at a time, in sync with the rotation animation. This sounds like a nasty hack though, and there’s a better way – it just requires a little thinking outside the box.

    The trick is to actually remove the current page from the UIScrollView before rotation, add it to the parent UIView, and hide the UIScrollView. Then, once the rotation has completed, update the UIScrollView content offset (not animated), put the page back into the UIScrollView, update all the frames of the UIScrollView to reflect their new positions, and then unhide the UIScrollView. Something like this:

    - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    self.scrollView.hidden = YES;
    
    //remove the current PageViewController and place it in the parent view
    PageViewController* pageViewController = [self.pageViewControllers objectAtIndex:self.currentPageIndex];
    CGRect frame = pageViewController.view.frame;
    frame.origin = CGPointZero;
    frame.size = kScrollViewLandscapeSize;
    if(UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) {
    frame.size = kScrollViewPortraitSize;
    }
    pageViewController.view.frame = frame;
    [self.view addSubview:pageViewController.view];
    
    //animate the frame change for the pageViewController
    [UIView animateWithDuration:duration animations:^{
    CGRect frame = pageViewController.view.frame;
    if(UIInterfaceOrientationIsLandscape(toInterfaceOrientation) {
    frame.size = kScrollViewLandscapeSize;
    }
    else {
    frame.size = kScrollViewPortraitSize;
    }
    pageViewController.view.frame = frame;
    }
    
    completion:^(BOOL finished) {
    
    CGSize scrollViewSize = [self scrollViewSizeForCurrentOrientation];
    [self.scrollView setContentSize:CGSizeMake(scrollViewSize.width*self.tips.count, scrollViewSize.height)];
    [self.scrollView setContentOffset:CGPointMake(scrollViewSize.width*self.currentPageIndex, 0)];
    
    //put the pageViewController back in the UIScrollView, and update the frames for all the PageViewControllers inside the UIScrollView
    [self.scrollView addSubview:pageViewController.view];
    
    [self.pageViewControllers enumerateObjectsUsingBlock:^(PageViewController* pageViewController, NSUInteger idx, BOOL *stop) {
    CGRect frame = pageViewController.view.frame;
    frame.origin.x = scrollViewSize.width * idx;
    pageViewController.view.frame = frame;
    }];
    self.scrollView.hidden = NO;
    }];
    }
    }
    
    

    A neat little trick that has the desired effect – smoothly animated rotations that behave just as you’d expect!

  • MyMoodTracker for iPad – Now Available

    I’m pleased to announce that after many months of hard work, the iPad edition of MyMoodTracker is now available. The app feels great on the larger screen, and should allow for some interesting and useful insights into one’s mental health.

    For those who are new to MyMoodTracker, it’s focus is on keeping track of how you are feeling throughout the day, and determining what factors have an influence on your mental state. Did I get enough sleep last night? Did I drink too many soft drinks yesterday? Am I on track with my medication? The things that affect your mood are many and varied, and MyMoodTracker helps you identify those patterns and get on the path to feeling good.

    If you’re a regular user of the iPhone edition of MyMoodTracker, you’ll be pleased to know that data can easily be synced between the two. Make your entries on your iPhone when you’re out and about, and analyse them later on the big screen of the iPad. There’s also some interesting additions to the iPad version, such as the handy statistics summary.

    For more information, please take a look at the MyMoodTracker website, or simply search for MyMoodTracker on the App Store. Alternatively, if you have any feedback or suggestions, I’d love to hear them!

    MyMoodTracker for iPad Screenshot 1MyMoodTracker for iPad Screenshot 1MyMoodTracker for iPad Screenshot 3MyMoodTracker for iPad Screenshot 4MyMoodTracker for iPad Screenshot 5

     

  • Developing with a Magic Trackpad

    As I don’t own a laptop, I’ve always felt that by using a mouse that I was missing out on some of the nice touch features of OS X. The Magic Trackpad is the obvious solution, but I was never able to get over  that sceptical feeling of how well it would work as a mouse replacement. Since I have several Magic Mouses (Magic Mice?) lying around already, when I bought a new iMac recently I decided to get the trackpad instead. I commited to trail it for two weeks, and if I was still finding it difficult at the end of that period, I’d go back to the trusty mouse. Those two weeks are now up, so here’s my verdict.

    The first thing I noticed is that the transition was really quite natural. I have used Apple laptops before so it’s not like I’ve never used a trackpad, but I think all those years of performing gestures on iOS devices just made me feel instantly at home with the trackpad. I was fully expecting to spend at least a few days feeling hindered, but the truth is I was up and running at normal speed almost instantly. All I needed was a little messing around in System Preferences to get the cursor speed correct. The gesture demonstrations in the System Prefs are excellent, and really make it clear what gestures are available.

    If you’re not too picky, then that’s really it. Developing in Xcode is great, navigating the OS is fun, and everything just works. Even doing the occasional touchups or cuts in Photoshop works just fine, though I’m not sure how well it would be for someone who spends all day designing pretty things.

    If you are picky like me, then there’s two things which may bug you. The first is creating IBOutlet connections in Xcode. I love the ability to drag connections that was first introduced in Xcode 4, and would always do this by right-click and dragging. However, I can’t find a way to make this work with the Magic Trackpad. The only way I’ve found to make connections so far is to hold down the Control key, and perform a 3 finger drag. A little cumbersome, but not that big of a deal.

    The other issue I came across is not so much of an issue as it is more of a side effect. When dragging things around with a trackpad, you will inevitably sometimes reach the edge of the trackpad before you’ve fully dragged the item to it’s destination. Imagine dragging a file from one side of the screen to another – chances are you reach the edge of the trackpad first unless you’re quick. To combat this, the trackpad gives you a little bit of time to readjust. For example, you can drag a file halfway across the screen, lift your fingers off the trackpad and move them back to the other side, and continue dragging. As long as you’re quick enough, this works great. The problem comes in when testing apps in the iOS Simulator. Often you’ll need to swipe or scroll, and because of the way the drag detection works, once you finish your swipe/scroll the screen sticks there for say a tenth of a second – just long enough for the OS to decide that you’re not trying to continue the gesture. Imagine scrolling a UITableView, and see it stick for just a little bit of a time after you finish scrolling. This really isn’t a big deal, as long as you test on a device and confirm that this stickiness is not just bad code!

    So, all in all, I love the Magic Trackpad, and I’m sticking with it. If you decide to give one a try, spend a few minutes familiarising yourself with all the gestures that aren’t possible with the Magic Mouse. There’s quite a few. You also might want to consider installing BetterTouchTool. It gives you a high level of customisation over how your trackpad behaves. Currently I’m only using it to alter Safari’s behaviour when tapping on links – basically if I perform a 3 finger tap, it opens links in the background.


    Enjoyed this post? Subscribe via RSS or follow me on Twitter.

  • Quit Checking Your Email!

    Being a freelancer, I’m always on the lookout for things which can help to improve my workflow. One source of productivity tips is the Back To Work podcast with Merlin Mann. For those who don’t know him, Merlin is what I’d call a productivity celebrity. He created 43 Folders, and invented Inbox Zero. You’d think that after 73 episodes of Back To Work, there wouldn’t be much more to talk about. Unfortunately, I’d probably agree! Each episode generally runs for about 90 minutes, and these days only seems to contain 5-10 minutes of what I’d call useful material. So, instead of recommending you go and subscribe, I’m going to detail a few of the things I learned from B2W that have made a big impact on my productivity – and they’re all to do with email.

    The first tip is to realise that it’s very unlikely that you work in a job where you need to respond to an email as soon as it arrives. How often are you in a situation where your livelihood depends on you replying to that email instantly? Not very often I’d say. So how long can you leave it? 5 minutes? 30 minutes? 2 hours? Chances are if the sender does require an urgent response, they’re just going to call you up anyway.

    Once you’ve realised that checking your email is not all that important, the next thing to do is turn off that badge. You know, that little red badge in your dock which says how many unread emails you have. If you’re anything like me, once you see a red badge you need to care take of it straight away. It’s sits there whispering to you – “hey, unread email over here…hello, pay attention to me”. Not good! Head on in to preferences and get rid of it, and I promise a few days later you won’t miss it at all. Same goes for your iPhone as well – don’t let your email be pushed to your device, set it to manual fetch only.

    The next step is to actually quit your email client altogether. I’ve gotten into a bad habit recently of checking my email whenever I’m waiting for something (eg. a build to complete). Naturally there’s one or two emails there waiting to be read, so I read them, and my attention gets pulled away to something else. Instead, try quitting your email client. Then, once you’ve finished a chunk of work, open it back up and take care of your emails all at once. Or if you have enough self control, just stare out the window while you’re waiting for that build to complete. Hands off the mouse!

    Depending on how much email you get, you might like to think about Inbox Zero. There’s a few different ways to approach Inbox Zero, but what I like to do is treat my inbox as a bit like a to-do list for the day. If there’s any emails in my inbox, they’re either unread, or they’re emails that I need to care of that day. Once I’ve processed an email (replied to it, stored the attachment, whatever needs to be done), the email gets archived. So at the end of each day, my inbox is empty. The easiest way to get started with Inbox Zero is just to move all your emails to an archive folder – hey presto, Inbox Zero! You can go back and sort those thousands of emails later if you want, but don’t let that task prevent you from starting.

    Inbox Zero also works really well with flags. Flags are great for when you want to clear out your inbox, but there’s certain emails you just don’t have time to take care of right now. For example, any personal emails that I might want to look over on the weekend, I flag with a green flag. Any work related emails that I need to follow up on within the next few days I flag with a yellow flag. Any support requests I flag as blue. These emails can then be archived, and I’ll take care of them in a day or two – this is easy to do using a flags smart folder in your email client. So, when I’m dedicating some time to support, I can quickly call up all the emails with a blue flag and get through them.

    The last tip I have is to use a different email client for your personal mail. All too often I want to read my email on the weekend, but I get bombarded with work emails – not good for work/personal life balance. This happens in reverse too – when there’s no boss looking over your shoulder it’s all too easy to open up that funny video your friend just sent you. So, find another email client, and use one for personal email, and one for work.

    I hope some of these tips were helpful. Email can be a big drain on your productivity, but only if you let it. If you have any others tips, I’d love to hear them!


    Enjoyed this post? Think we’re clever? Subscribe via RSS or follow us on Twitter.

  • An Interview with Beginning iOS Dev

    I was recently asked by Beginning iOS Development if I would like to take part in an interview, which sounded like fun to me. We discussed a range of things, including marketing, designs, my background, and development life in general. I’m really happy with how it all turned out, so if this sounds like it might be of interest, wander on over and have a read: http://www.beginningiosdev.com/interviews/interview-with-ben-williams-of-aspyre-apps


    Enjoyed this post? Think we’re clever? Subscribe via RSS or follow us on Twitter.

<< previous page or next page>>