Back to Top

Jon Hocking

2012-01-15
Creating Reactive Interfaces Part 2

Part one here

Previously in this animation series, I introduced the basics of UIView animation using the block API. We covered everything (hopefully) you need to get started with the API, and so in this article we’re going to cover an interesting and very useful little extra feature.

Often in iOS, I find myself adding animations which start as a result of some user interaction. A lot of the time, this animation is reversed by simply reversing the interaction.

I like to call this a reactive animated interface.

As an example, imagine scrolling to the final page in a scrollview, and causing a button to fade in. If the user scrolls away from the final page then the button fades out.

This is really simple to pull off with the block API. And you could use a check like this to do it:

if (finalPage){
    // block to fade in
}
else{
    // block to fade out
}

But what happens when the user moves rapidly between pages? Fast enough so that the animation doesn’t have time to finish. Well with the default behaviour, the block snaps to the final state of its animation and calls complete. The second, interrupting block then begins from the final state of the first animation.

The result of this is a jarring effect that can leave your user wondering where the rest of the animation went.

So how can we avoid this problem?

The secret to this is in the often overlooked beginFromCurrentState option.

Begin from current state ensures that the animation is prevented from snapping to the final position / alpha etc. Instead the current state is used as the initial state of the next animation. This means a smooth animation will display instead of the visual jerk - much nicer for your users!

To use this option just pass in UIViewAnimationOptionBeginFromCurrentState in exactly the same way as we’ve been doing with previous options

[UIView animateWithDuration: 0.5 delay: 0
options:UIViewAnimationOptionBeginFromCurrentState |
UIViewAnimationOptionCurveEaseInOut animations:^{

                    } completion:^(BOOL finished) {

                    }];

One side effect of this is that the final completion block will not get called on the first animation. This makes sense since the animation never fully completes, however it’s good to bear in mind so make sure you don’t have any critical releases in there.

So as usual with animations, the best way to understand them is to see them in action. Below is a simple example showing an animation on the frame of a view.

The view animates up from the bottom as the user moves between the first and second pages with begin from current state set in the options. If the user switches to the previous page as the animation is still running then the animation will play in reverse with a graceful transition instead of a jerky one.

Once you’ve mastered the basic concept, you can take it further and animate multiple views with the beginFromCurrentState option.

Below is a second example found in the same app where the animation is actually working on about five different images. Positioning and fading them to give a nice effect.

Unfortunately I’m not allowed to release the code used in this app as is it was client work but I hope the demos help to give you some good inspiration and a view of what’s possible.

If you find this useful I’d love to know so please get in touch on Twitter: @jon_hocking or send me a demo of your animations

2012-01-7

Writing code is inherently creative. The more creativity we can get into our apps, the better we can get at building great experiences.

Take risks.

Make mistakes.

Go and finish something.

2012-01-4
kimlaama:

(via LukeW | Touch Gesture Reference Cards)
2011-12-18
iOS Localisation with Thai

So recently, I’ve been working on an app that’s going to launch in a lot of emerging markets. It’s been good fun because the app not only has to localise the strings but also has different sets of data depending on which region you use the app in.

Localising most latin based alphabets is really simple in iOS, you just set up your strings file then with a little more work you’re good to go. The problem with this app was that most of these countries are in or around Asia, so we had languages like Thai to cater for.

Thai has a really incredible alphabet. Each character is very intricate and has lots of curves. The words merge together so there’s hardly any spaces, except to mark the end of sentences, but what’s most interesting is that they have separate characters / accents for different tones. Like a lot of languages, these accents sit above the characters and without them you can change the entire meaning of a word.

So why was this an issue?

Well the app includes a lot of dynamically sized text - that is text that can be various lengths / heights based on which language is used or a certain description in an individual table cell item, for example. To cope with this, I generally use the

[self.aLabel sizeToFit];

UIView instance method. This is great and had me covered for pretty much every situation.

The problem with Thai is that calling sizeToFit actually cuts off the little accents on top of the words. So what you think says something useful actually turns out to be completely meaningless to the users.

How to solve it?

Generally at The App Business, when we have a label which uses a custom typeface, we create a UILabel subclass specifically for that font. We then just override the initWithFrame: and initWithCoder: methods to set the custom font, making sure to use the already set point size. This way you can easily place labels in Interface Builder, set the right point size and then just change the class.

To get around the issue with the clipping of Thai accents, I also added an override of the sizeToFit method inside the subclass. This method called [super sizeToFit]; then checked for the Thai localisation. If it finds that the current language is Thai, it increases the height of the labels frame by point size/2 - just enough to get rid of the clipping but not enough to be dramatically noticeable. That way the designs still look consistent across languages, the clipping is avoided at various point sizes and most importantly the users can make sense of the app.

The fix is really simple but felt a little too hacky to me. The reason it works well is that the app forces the language based on region (a client request and perhaps necessary to make sure we don’t accommodate every language found in Asia!). Having searching around for a solution, this seemed to be the only one I could find.

If anyone out there has a better fix I’d love to hear about it, either on Twitter or by email. If not then I hope this post helps if you find yourself in a similar situation with localisation.

The next post will be back to the reactive animation series that I started here.

2011-12-5
Creating Animated Interfaces with iOS

I believe creating an enjoyable user experience in apps is of paramount importance and it’s part of what sets apart the good from the great apps. One of the ways we can achieve this is to bring our interfaces to life with Core Animation.

This article is going to be the first part in a series on adding animation to your interfaces. Starting with the very basic UIView block animations, then taking the block API a little further, and finally moving on to cover Core Animation.

The current standard method for straightforward animation in iOS is to use the block API. This allows you to specify durations, properties to change, and finally what to do on completion.

Some of the common properties you might want to animate are:

  • alpha
  • frame
  • center
  • backgroundColor

You can even take this a further and animate the properties on the views layer such as corner radius and transform.

Naturally there are some properties that can’t be animated, like BOOL values (since a boolean can only be true or false, nothing in between) and a layer’s anchor point, but these are minor constraints.

So lets check out the API. Below is the basic UIVIew class method for a block animation.

[UIView animateWithDuration: 0.5 delay: 0.1 
    options: UIViewAnimationOptionCurveEaseInOut 
    animations:^{

        self.view.alpha = 0.5;
        self.view.frame = CGRectMake(0, 0, 320, 400);

    } completion:^(BOOL finished) {
        NSLog(@"Animation complete");
}];

The method call is fairly self explanatory. You pass in some variables and options and then specify the changes to be made in the animation block. You can then add another block to execute on completion.

In the above example a UIViewControllers view is changing it’s frame and alpha value. The most intersting thing about this example is the option passed in.

The option parameter allows you to pass in some predefined enums which can alter the way you animation looks and reacts. The one we’ve chosen here is UIViewAnimationOptionCurveEaseInOut.

This basically means that your animation will start slowly (ease in), build up momentum, and then slow down towards the end of its duration (ease out). There’s variations of this animation curve option so you can customise it for the situation. The other choices are

  • UIViewAnimationOptionCurveEaseIn
  • UIViewAnimationOptionCurveEaseOut
  • UIViewAnimationOptionCurveLinear

It’s important to know about these if you want to chain some animations together. To chain an animation to another one, simple call the block animation method again in the complete: of the first.

So how can we use this to create something cool? How about a nice drop down view that animates in and bounces into position.

To achieve this we can chain together two block animations. First lets split the animation into parts.

We have the first animation down where the view comes in from the top of the screen, and then the second animation back up to give the bounce effect.

The best thing to do with animations is to actually see them in action. I’ve recorded a quick screen cast so you can see how the bounce effect works and hopefully you’ll notice the subtle EaseInOut effect.

The Code

I’ve set up a basic UIViewController with a member variable called dropDownView. I’ve also defined an IBAction linked to a nav bar button so we can control the animation.

I’ve put the Xcode project for this up on Github so you can take a look but first lets check out the main animation method.

- (IBAction)animateButtonPressed:(id)sender{
    self.dropDownView.frame = CGRectMake(0, 0, 
    self.dropDownView.frame.size.width, 
    self.dropDownView.frame.size.height);

    // first block animation
    [UIView animateWithDuration: 0.4 delay: 0.2                         
        options:UIViewAnimationOptionAllowUserInteraction 
        | UIViewAnimationOptionCurveEaseInOut 
        animations:^{

        self.dropDownView.frame = CGRectMake(0, 44,
        self.dropDownView.frame.size.width, 
        self.dropDownView.frame.size.height);

         } completion:^(BOOL finished) {

                                    // second animation
                           [UIView animateWithDuration: 0.2 delay: 0
                            options:UIViewAnimationOptionAllowUserInteraction
                |UIViewAnimationOptionCurveEaseInOut 
               animations:^{

            self.dropDownView.frame = CGRectMake(0, 40,
            self.dropDownView.frame.size.width, 
            self.dropDownView.frame.size.height);

            } completion:^(BOOL finished) {
                    // no need to put anything here
        }];                            
     }];
}

This animateButtonPressed: method simply sets the frame of the dropDownView so that it’s out of sight, then it animates down by setting a new frame.

On completion, a second block is called which animates the view back up by 4 pixels.

Notice how both animations use EaseInOut to give the effect of the view building up momentum as it drops and then again as it springs back up.

You may have spotted an extra option: argument in there. This new one is

UIViewAnimationOptionAllowUserInteraction

which allows touch events to be registered on the view during animation. By default this is not enabled so if you’re creating an interface where you want some user interaction during the animation you’ll have to enable it.

I hope you enjoyed this quick intro to the block api. In the next article I’ll be talking about how you can take this further to create reactive interfaces.

In the mean time have a look at the project on Github here

https://github.com/jonhocking/JHDropDownAlertDemo

Update: Part 2 here

2011-11-14
New notebook :)

New notebook :)

2011-10-25
Simple can be harder than complex; you need to work hard to get your thinking clean to make it simple, but when you do you can move mountains.
— Steve Jobs