0

all. I'm trying to follow a tutorial on making a ball bounce around the screen of an iPhone. The tutorial constructs the application in a MVC scheme. I'm having trouble wrapping my head around this concept when it comes to the drawRect method in the View implementation.

This is my Model header file:

#import <Foundation/Foundation.h>
#import "TestView.h"

#define BALL_SIZE 20.0
#define VIEW_WIDTH 320.0
#define VIEW_HEIGHT 460.0

@interface TestModel : NSObject
{
    TestView* ball;
    CGPoint ballVelocity;
    CGFloat lastTime;
    CGFloat timeDelta;
}

- (void) updateModelWithTime:(CFTimeInterval) timestamp;
- (void) checkCollisionWithScreenEdges;

@property (readonly) TestView* ball;

@end

The tutorial instructs me the user to override the init method of NSObject. I've also included the methods for controlling the "animation" logic:

    - (id) init {
        self = [super init];

        if (self) {
            ball = [[TestView alloc] initWithFrame: CGRectMake(0.0, 0.0, BALL_SIZE, BALL_SIZE)];

            // Set the initial velocity for the ball
            ballVelocity = CGPointMake(200.0, -200.0);

            // Initialize the last time
            lastTime = 0.0;
        }

        return self;
    }

- (void) checkCollisionWithScreenEdges {
    // Left Edge
    if (ball.frame.origin.x <= 0) {
        ballVelocity.x = abs(ballVelocity.x);
    }

    // Right Edge
    if (ball.frame.origin.x >= VIEW_WIDTH - BALL_SIZE) {
        ballVelocity.x = -1 * abs(ballVelocity.x);
    }

    // Top Edge
    if (ball.frame.origin.y <= 0) {
        ballVelocity.y = abs(ballVelocity.y);
    }

    // Bottom Edge
    if (ball.frame.origin.y >= VIEW_HEIGHT - BALL_SIZE) {
        ballVelocity.y = -1 * abs(ballVelocity.y);
    }
}

- (void) updateModelWithTime:(CFTimeInterval) timestamp {
    if (lastTime == 0.0) {
        // initialize lastTime if first time through
        lastTime = timestamp;
    } else {
        // Calculate time elapsed since last call
        timeDelta = timestamp - lastTime;

        // Update the lastTime
        lastTime = timestamp;

        [self checkCollisionWithScreenEdges];

        // Calculate the new position of the ball
        CGFloat x = ball.frame.origin.x + ballVelocity.x * timeDelta;
        CGFloat y = ball.frame.origin.y + ballVelocity.y * timeDelta;

        ball.frame = CGRectMake(x, y, BALL_SIZE, BALL_SIZE);
    }
}

The View implementation file is the following:

#import "TestView.h"

@implementation TestView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}

// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect) rect {

}
@end

Finally, my View Controller:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    gameModel = [[TestModel alloc] init];

    [self.view addSubview:gameModel.ball];

    // Set up the CADisplayLink for the animation
    gameTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateDisplay:)];

    // Add the display link to the current run loop
    [gameTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}

- (void) updateDisplay:(CADisplayLink *) sender {
    [gameModel updateModelWithTime:sender.timestamp];
}

OK, so now that I've provided a look at the structure of the code (hopefully I've given enough) I can get to my question. So when I add anything to drawRect a new object is drawn and does not get "animated" by the model logic methods.

Right now I have a bouncing square. When I try to fill the square with an ellipse in drawRect, I get a new object, drawn how I want, that just sits at 0,0 while the bouncing square is still active.

I'm sure I'm missing something really big here, but I've been banging my head against the wall for hours and can't figure it out. Any help would be greatly appreciated!

Nathaniel Waisbrot
  • 23,261
  • 7
  • 71
  • 99

1 Answers1

0

A couple of things here:

1- Have you overriden the view class in SB to TestView?
2- Looking at your code, I do not see how your model is connected to your View through your controller. Recall from the MVC model, that the Controller is on top and talks down (pyramid style) to the model and the view and so, somewhere in your view controller:
a) you need to instantiate your model
b) get values from your model and pass them to your view variables - which would be called in drawrect

Last but not least, lookup setNeedsDisplay which is the way to call and refresh the view.

Hope this helps without spoiling the assignment.

Khaled Barazi
  • 8,681
  • 6
  • 42
  • 62
  • Hey, thanks for the reply. I updated my initial post with my View Controller implementation. What does SB stand for? =/ From what I can gather, I am instantiating the model properly and adding the TestView. – Bobby Knezevic Feb 11 '13 at 13:25
  • SB= Storyboard. I recommend you spend some time on CS193P MVC (I think lesson#1 or #2) itunes class. in your viewDIdLoad, you are instantiating the model correctly but you cannot add the model as a subview. It is not a UIView, it is a model. Again, your View Controller instantiates the model and then should send that information to the TestView class through properties. – Khaled Barazi Feb 11 '13 at 13:36
  • OK, I'll check it out. Thanks for the advice. Also, yea I've changed the view class to TestView in SB. – Bobby Knezevic Feb 11 '13 at 13:48
  • @Yugocode, if this answers your question, please mark it as the answer.Thanks – Khaled Barazi Feb 13 '13 at 18:14