In this post, I will introduce the Model-View-Controller design paradigm and use NSTimer-based animation to make the iPhone app shown in the video below.
The basic idea here is to simulate the motion of two circular balls enclosed inside a rectangular box in 2D space. The balls can collide with each other and with the walls of the rectangular box. To keep things simple, all collisions are perfectly elastic. The user can start and stop the animation using the two buttons in the UI.
Model View Controller (MVC)
MVC is an elegant way to organize the classes within your project based on their function. Here is an attempt at a quick summary using the present app as an example:
- MODEL – this is where we store abstract information about our model, which in this case is the radius of the two balls, their position (x and y coordinates) and their velocity (x and y components). In addition, this can also contain the logic needed to detect collisions with the walls and between each other and to advance the position of the balls in time.
- VIEW – The View class (inherits from UIView) is mainly concerned with drawing the two balls on the sub-view or drawing canvas or graphics context or whatever you may wish to call it. We provide the controller with an instance of this class by creating an IBOutlet property.
- CONTROLLER – this class reads in information from the model, properly interprets this information and sends it to the view for drawing. It also deals with user input (button clicks in this case) from the view.
The whole MVC idea is to promote better object-oriented coding habits and help make the code easier to understand, maintain and expand in the future.
For example, this code started with just one ball. To add the second ball, I simply went into the Model class and added the necessary information about the second ball. Then I had to go to the ViewController class and add the model properties for drawing the second ball. Here is a snapshot of the Model class interface:
#import <Foundation/Foundation.h> @interface Model : NSObject @property float x1; @property float x2; @property float y1; @property float y2; @property float R; @property float speedX1; @property float speedY1; @property float speedX2; @property float speedY2; @property float WIDTH; @property float HEIGHT; - (void) updateBallPositions; @end
Coordinates and velocity components for the two balls are identified using “1” and “2” respectively. The WIDTH and HEIGHT property refers to the size of the box inside which the balls are moving around. Setting the values for WIDTH and HEIGHT is an example where the controller serves as a liaison between the view and the model. The code snippet below is from ViewController.m and illustrates how the initial condition is set up (before the animation begins).
// ball is drawn at a random location on screen (based on the model) - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. ball1 = [UIImage imageNamed:@"yellowBall.png"]; ball2 = [UIImage imageNamed:@"whiteBall.png"]; // instantiate model variables model = [[Model alloc] init]; model.WIDTH = self.blackView.frame.size.width; model.HEIGHT = self.blackView.frame.size.height; [model setInitialBallPositions]; // display initial condition before animation starts [self showBalls]; }
We can see that the Controller obtains the size of the “blackView” object (of class UI View) and passes this information along to the “model” object (of class Model).
That the controller can talk with both the model and the view is immediately clear if you look at the ViewController interface, which includes the model and view header files.
#import <UIKit/UIKit.h> // Controller talks with the Model and the View #import "Model.h" #import "View.h" @interface ViewController : UIViewController @property (nonatomic, strong) Model* model; @property (nonatomic, strong) UIImage* ball1; @property (nonatomic, strong) UIImage* ball2; @property (nonatomic, strong) IBOutlet View *blackView; @property (nonatomic, strong) UIImageView* ballView1; @property (nonatomic, strong) UIImageView* ballView2; @property (nonatomic, strong) NSTimer* timer; - (IBAction)startTimer:(id)sender; - (IBAction)stopTimer:(id)sender; - (void) update; @end
For those new to object oriented programming, notice that we instantiate a Model class called model and make it a property. All of the machinery for the class we designed will be available to this instance variable.
Animation using NSTimer
This app introduces one of the commonly used way to do animations in iOS. This technique is quite similar to the setInterval-based animation technique used in HTML5/JavaScript animations, where the code calls a function or method continuously at preset time intervals. Here is a code snapshot of the startTimer method, which is triggered when the user presses the START button on the UI:
// pressing the START button begins the animation - (IBAction)startTimer:(id)sender { // remove previously active instances (if any) [timer invalidate]; timer = nil; // create a new timer based loop timer = [NSTimer timerWithTimeInterval:1.0/60.0 target:self selector:@selector(update) userInfo:nil repeats:YES]; [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; }
In this case, the update method is called repeatedly at a frequency of 60 frames per second. To stop the animation, simply destroy the timer as shown below.
// pressing the STOP button stops the animation - (IBAction)stopTimer:(id)sender { [timer invalidate]; timer = nil; }
This approach appears to work quite well for the animation needs of this app.
Future Work
Be aware that the ball-ball collision model used here is rather simplistic and based on a “momentum exchange upon impact” approach. This is applicable only for collisions where the velocities before impact are collinear. In the future, I plan to use a more sophisticated model that accounts for oblique impacts.
—
I hope this post clears up some of the mystery surrounding the MVC design paradigm. You can clone the entire project from my GitHub link:
https://github.com/jabhiji/ios-bouncing-ball.git
Happy Xcoding!
I just replaced the UIImage-based balls with custom-drawn UIView balls. These look much sharper than the ones I made earlier using GIMP.
Simply create two new subclasses of UIView (called Ball1 and Ball2) and create filled circles inside the drawRect method. You can then instantiate Ball1 and Ball2 objects inside your ViewController, set the background color to clearColor and add these to the superclass view for displaying on screen.
The GitHub repo now contains the updated code.