YY Digital Blog

7
March
2012

Custom SplitApartView UI

By David Bankier

Tagged: Titanium Tutorial

In this blog we are going to create an animated "split apart view". The idea is much like when you tap on an application group on the iOS home screen and the screen appears to split apart revealing the apps in that group.

App Group

Summary

The implementation is quite simple. It should be clear that no view is actually going to split. The split effect can be achieved however as follows.

We will use two views, each with their background image set with the image of the window to be split apart - we shall call these the "doors" or leftView and rightView. We will also need to have the view that will be behind the doors when they open - insideView. The doors and insideView will be placed on top of the window that we want to be split apart - hostWin - when it is time for the effect. The impatient can skip straight to the example on github.

SplitApartView Module

We are going to build a CommonJS module naming the file SplitApartView.js. First we need to get the details of the device we are using: height, width and half screen dimensions (which we will use later). Also up front, we are going to define some common animation settings.

var screenHeight = Ti.Platform.displayCaps.platformHeight;
var screenWidth = Ti.Platform.displayCaps.platformWidth;
var half_screen = {
  top : 0,
  height : screenHeight,
  width : screenWidth / 2
};
var common_animation = {
  top : 0,
  duration : 500
};

When instantiating the module we need create the leftView and rightView and hold on to the hostWin and insideView. Note, in the code below we added an event listener to both doors so that when they are tapped, the doors slide back into place and then be removed, along with the insideView, from the hostWin.

exports.SplitApartView = function(obj) {
  this.hostWin = obj.hostWin;
  this.insideView = obj.insideView;
  this.animated = false;
  this.leftView = Ti.UI.createView(mixin({
    left : 0
  }, half_screen));
  this.rightView = Ti.UI.createView(mixin({
    right : 0
  }, half_screen));
      
  var me = this;
  function close() {
    me.rightView.animate(mixin({
      right : 0
    }, common_animation));
    me.leftView.animate(mixin({
      left : 0
    }, common_animation), function() {
      me.animated = false;
      //Cleanup
      obj.hostWin.remove(me.insideView);
      obj.hostWin.remove(me.rightView);
      obj.hostWin.remove(me.leftView);
    });
  }
  this.rightView.addEventListener('click', close);
  this.leftView.addEventListener('click', close);
};

Since we have already mentioned the door closing animation, we will included the animation for when the doors open. Note that the doors do not open fully or slide off the screen, so that we can tap them to close again.

exports.SplitApartView.prototype.open = function() {
  if(!this.animated) {
    var win = this.hostWin;
    this.animated = true;
    win.add(this.insideView);
    win.add(this.leftView);
    win.add(this.rightView);

    this.rightView.animate(mixin({
      right : -(screenWidth / 3)
    }, common_animation));
    this.leftView.animate(mixin({
      left : -(screenWidth / 3)
    }, common_animation));
  }
};

Up to this point we have the opening and closing animations. We also need the code that takes an image of the hostWin, splits it and uses each half as the background image of the doors. The code is as follows:

var cropping = {
  y : 0,
  height : screenHeight,
  width : screenWidth / 2
};

exports.SplitApartView.prototype.prepare = function() {
  var screenShot = this.hostWin.toImage();

  this.leftView.backgroundImage = screenShot.imageAsCropped(mixin({
    x : 0
  }, cropping));
  this.rightView.backgroundImage = screenShot.imageAsCropped(mixin({
    x : screenWidth / 2
  }, cropping));
};

The reason why this code has been placed in a separate function instead of incorporating it in the open function is that when combined, the user experience can be laggy. Depending on the application the prepare function can be called early after graphical changes to the hostWin or if no further UI changes are expected.

You might have noticed the use of the mixin function that I used to cut down on lines of code. I first saw it used way back in the Tweetanium app. The code is here if you need it:

var empty = {};
var mixin = function(target, source) {
  var name, s, i;
  for(name in source) {
    s = source[name];
    if(!( name in target) || (target[name] !== s && (!( name in empty) || empty[name] !== s))) {
      target[name] = s;
    }
  }
  return target;
}; 

Using the Module

Here is an example of a very basic app.js file that uses the module.

var SplitApartView = require('/SplitApartView').SplitApartView;

var win = Ti.UI.createWindow({
  backgroundColor: '#2192E3',
  fullscreen: true
});

var insideView = Ti.UI.createView({
  backgroundColor: "#666"
});

var textArea = Titanium.UI.createTextArea( {
  height: 100,
  width: 200,
  borderColor: "black",
  borderRadius: 10,
  font : {
	fontSize : 20
  }
});
win.add(textArea);


var split = new SplitApartView({
  hostWin: win,
  insideView: insideView 
});

win.addEventListener("swipe", function () {
  split.prepare();
  split.open();
});

win.open(); 

The textArea is placed in the middle of the host window. Add quite a bit of text, then swipe the window and it should appear that the window and text area are torn apart. Here are some screen shots showing the before and after.

Split Before Split After

Extensions

A few extra things to do to improve the experience and module:

  • Further UI work should be done to the insideView to make it appear behind the split window, e.g. shadows, textures etc.
  • The example above is more of a swipe then split approach. You might want to modify it so that you can drag the doors open.

That is my blog for today.

 
blog comments powered by Disqus