1. Welcome to the new MacRumors forums. See our announcement and read our FAQ

Strange problem with views, subviews, and orientation

Discussion in 'iPhone/iPad Programming' started by MaxFreud, Jul 23, 2011.

  1. macrumors newbie

    #1
    Hi all,

    I am making a simple iPad game that includes some OpenGL code; I am using Jeff Lamarche's Xcode template, which includes a UIView subclass for the OpenGL stuff. It is called GLView, and it comes with its view controller, called GLViewController. Here are those classes for reference:

    Code:
    #import <QuartzCore/QuartzCore.h>
    #import <OpenGLES/EAGLDrawable.h>
    
    #import "GLView.h"
    #import "ConstantsAndMacros.h"
    
    
    @interface GLView ()
    @property (nonatomic, retain) EAGLContext *context;
    @property (nonatomic, assign) NSTimer *animationTimer;
    - (BOOL) createFramebuffer;
    - (void) destroyFramebuffer;
    @end
    
    #pragma mark -
    
    @implementation GLView
    
    @synthesize context;
    @synthesize animationTimer;
    @synthesize animationInterval;
    @synthesize delegate;
    
    + (Class)layerClass 
    {
        return [CAEAGLLayer class];
    }
    - (id)initWithCoder:(NSCoder*)coder {
        
        if ((self = [super initWithCoder:coder])) {
            // Get the layer
            CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
            
            eaglLayer.opaque = YES;
            eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                            [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
            
    #if kAttemptToUseOpenGLES2
            context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
            if (context == NULL)
            {
    #endif
                context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
                
                if (!context || ![EAGLContext setCurrentContext:context]) {
                    [self release];
                    return nil;
                }
    #if kAttemptToUseOpenGLES2
            }
    #endif
            
            animationInterval = 1.0 / kRenderingFrequency;
        }
        return self;
    }
    - (void)drawView 
    {
        glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
        [delegate drawView:self];
        glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
        [context presentRenderbuffer:GL_RENDERBUFFER_OES];
    }
    - (void)layoutSubviews 
    {
        [EAGLContext setCurrentContext:context];
        [self destroyFramebuffer];
        [self createFramebuffer];
        [self drawView];
    }
    - (BOOL)createFramebuffer 
    {
        glGenFramebuffersOES(1, &viewFramebuffer);
        glGenRenderbuffersOES(1, &viewRenderbuffer);
        
        glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
        glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
        [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
        glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
        
        glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
        glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
        
        if (USE_DEPTH_BUFFER) 
        {
            glGenRenderbuffersOES(1, &depthRenderbuffer);
            glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
            glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
            glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
        }
        
        if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) 
        {
            NSLog(@"failed to make complete framebuffer object %4x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
            return NO;
        }
        
        [delegate setupView:self];
        return YES;
    }
    - (void)destroyFramebuffer 
    {
        glDeleteFramebuffersOES(1, &viewFramebuffer);
        viewFramebuffer = 0;
        glDeleteRenderbuffersOES(1, &viewRenderbuffer);
        viewRenderbuffer = 0;
        
        if(depthRenderbuffer) 
        {
            glDeleteRenderbuffersOES(1, &depthRenderbuffer);
            depthRenderbuffer = 0;
        }
    }
    - (void)startAnimation 
    {
        self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES];
    }
    - (void)stopAnimation 
    {
        self.animationTimer = nil;
    }
    - (void)setAnimationTimer:(NSTimer *)newTimer 
    {
        [animationTimer invalidate];
        animationTimer = newTimer;
    }
    
    - (void)setAnimationInterval:(NSTimeInterval)interval 
    {
        animationInterval = interval;
        if (animationTimer) 
        {
            [self stopAnimation];
            [self startAnimation];
        }
    }
    - (void)dealloc 
    {
        [self stopAnimation];
        
        if ([EAGLContext currentContext] == context) 
            [EAGLContext setCurrentContext:nil];
        
        [context release];  
        [super dealloc];
    }
    
    @end
    
    Code:
    #import "GLViewController.h"
    #import "ConstantsAndMacros.h"
    #import "OpenGLCommon.h"
    #import "OpenGLTexture3D.h"
    #import "gazeShip.h"
    
    @implementation GLViewController
    
    @synthesize texture;
    
    - (void)drawView:(UIView *)theView
    {
        static GLfloat rot = 0.0;
        
        glColor4f(0.0, 0.0, 0.0, 0.0);
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
        // Drawing code here
        glEnableClientState(GL_VERTEX_ARRAY);
    //    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        glEnable(GL_TEXTURE_2D);
        glEnable(GL_BLEND);
    
        [texture bind];
        glColor4f(1.0, 0.0, 0.0, 0.0);
        glLoadIdentity();
        glTranslatef(0.0, 0.2, -1.7);
        glRotatef(90.0, 0.0, 0.0, 1.0);
        glRotatef(170.0, 0.0, 1.0, 0.0);
        glRotatef(rot, 0.0, 0.0, 1.0);
        glVertexPointer(3, GL_FLOAT, 0, shipVerts);
        glTexCoordPointer(2, GL_FLOAT, 0, shipTexCoords);
        glDrawArrays(GL_TRIANGLES, 0, shipNumVerts);
        
        glDisableClientState(GL_VERTEX_ARRAY);
        glDisableClientState(GL_NORMAL_ARRAY);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        glDisable(GL_TEXTURE_2D);
        glDisable(GL_BLEND);
        
        static NSTimeInterval lastDrawTime;
        if (lastDrawTime)
        {
            NSTimeInterval timeSinceLastDraw = [NSDate timeIntervalSinceReferenceDate] - lastDrawTime;
            rot+=35 * timeSinceLastDraw;  
        }
        lastDrawTime = [NSDate timeIntervalSinceReferenceDate];
    }
    
    -(void)setupView:(GLView*)view
    {
        const GLfloat zNear = 0.01, zFar = 1000.0, fieldOfView = 45.0; 
    	GLfloat size;
        
        ((GLView*)(self.view)).delegate = self;
        
        // Enabling GL_DEPTH_TEST is used for depth buffering
        // this allows openGL to properly obscure one object
        // when another one is in front of it.
    	glEnable(GL_DEPTH_TEST);
    	glMatrixMode(GL_PROJECTION); 
    	size = zNear * tanf(DEGREES_TO_RADIANS(fieldOfView) / 2.0); 
    	CGRect rect = view.bounds; 
    	glFrustumf(-size, size, -size / (rect.size.width / rect.size.height), size / 
    			   (rect.size.width / rect.size.height), zNear, zFar); 
    	glViewport(0, 0, rect.size.width, rect.size.height);  
    	glMatrixMode(GL_MODELVIEW);
    	
    
        // Enable lighting
        glEnable(GL_LIGHTING);
        
        // Turn the first light on
        glEnable(GL_LIGHT0);
        
        // Define the ambient component of the first light
        static const Color3D light0Ambient[] = {{0.4, 0.4, 0.4, 1.0}};
    	glLightfv(GL_LIGHT0, GL_AMBIENT, (const GLfloat *)light0Ambient);
        
        // Define the diffuse component of the first light
        static const Color3D light0Diffuse[] = {{0.7, 0.7, 0.7, 1.0}};
    	glLightfv(GL_LIGHT0, GL_DIFFUSE, (const GLfloat *)light0Diffuse);
        
        // Define the specular component and shininess of the first light
        static const Color3D light0Specular[] = {{0.7, 0.7, 0.7, 1.0}};
        glLightfv(GL_LIGHT0, GL_SPECULAR, (const GLfloat *)light0Specular);
        
        // Define the position of the first light
        // const GLfloat light0Position[] = {10.0, 10.0, 10.0}; 
        static const Vertex3D light0Position[] = {{5.0, 5.0, 5.0}};
    	glLightfv(GL_LIGHT0, GL_POSITION, (const GLfloat *)light0Position); 
    	
        // Calculate light vector so it points at the object
        static const Vertex3D objectPoint[] = {{0.0, 0.0, -5.0}};
        const Vertex3D lightVector = Vector3DMakeWithStartAndEndPoints(light0Position[0], objectPoint[0]);
        glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, (GLfloat *)&lightVector);
        
        NSString *path = [[NSBundle mainBundle] pathForResource:@"BrushedAluminum" ofType:@"jpg"];
        OpenGLTexture3D *newTexture = [[OpenGLTexture3D alloc] initWithFilename:path width:512 height:512];
        self.texture = newTexture;
        [newTexture release];
    
    	glLoadIdentity(); 
    }
    - (void)dealloc 
    {
        [super dealloc];
    }
    
    -(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
    {
        return (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft
                || toInterfaceOrientation == UIInterfaceOrientationLandscapeRight);
    }
    
    -(void) viewWillAppear:(BOOL)animated
    {
        NSLog(@"View will appear");
        
        [((GLView*)(self.view)) startAnimation];
    }
    
    - (void) viewWillDisappear:(BOOL)animated
    {
        NSLog(@"View will disappear");
        
        [((GLView*)(self.view)) stopAnimation];
    }
    @end
    
    I have created another view with its view controller:

    Code:
    #import "GazeDemoAppDelegate.h"
    #import "GazeIntroViewController.h"
    #import "SettingsPopover.h"
    
    @implementation GazeIntroViewController
    
    @synthesize screenWidth, screenHeight;
    @synthesize calibrationMode;
    @synthesize testMode;
    @synthesize gazeTitle, instructions, logo, calibrate, settings, squareView;
    @synthesize start;
    @synthesize glView;
    
    
    #pragma Init and Memory
    
    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    {
        self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
        if (self) {
            // Custom initialization
            connected = NO;
            [self setupViewWithCalibrationState:NO];
        }
        return self;
    }
    
    - (void) setupViewWithCalibrationState:(BOOL)done
    {
        calibrationDone = done;
        
        gazeTitle.hidden = NO;
        instructions.hidden = NO;
        logo.hidden = NO;
        calibrate.hidden = NO;
        settings.hidden = NO;
        squareView.hidden = YES;
        //TODO: Replace YES with !done
        start.hidden = YES;
    }
    
    - (void)didReceiveMemoryWarning
    {
        // Releases the view if it doesn't have a superview.
        [super didReceiveMemoryWarning];
        
        // Release any cached data, images, etc that aren't in use.
    }
    
    #pragma mark - View lifecycle
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // Do any additional setup after loading the view from its nib.
        
        NSLog(@"Am I first responder? %@", [self.view isFirstResponder]?@"Yes":@"No");
        
        CGRect screenRect = [[UIScreen mainScreen] bounds];
        
        self.screenWidth = screenRect.size.width;
        self.screenHeight = screenRect.size.height;
        
        if (!connected) {
            calibrate.enabled = NO;
        }
        
        if([self.view becomeFirstResponder])
            NSLog(@"Assumed 1st responder");
    }
    
    - (void)viewDidUnload
    {
        [super viewDidUnload];
        // Release any retained subviews of the main view.
        // e.g. self.myOutlet = nil;
    }
    
    - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
    {
        // Return YES for supported orientations
    	return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft
                || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
    }
    
    #pragma mark UI Action Methods
    
    - (IBAction)calibratePressed:(id)sender
    {
        //gazeTitle, instructions, logo, calibrate, settings, square
        gazeTitle.hidden = YES;
        instructions.hidden = YES;
        logo.hidden = YES;
        calibrate.hidden = YES;
        settings.hidden = YES;
        squareView.hidden = NO;
        start.hidden = YES;
        
        [GazeDemoAppDelegate singleton].calibrationMode = YES;
        [[NetworkManager singleton] beginCalibration];
    }
    
    - (IBAction)settingsPressed:(id)sender
    {
        if (settingsPopover.isPopoverVisible) {
            [settingsPopover dismissPopoverAnimated:YES];
        }
        else
        {
            SettingsPopover* content = [[SettingsPopover alloc] init];
            
            // setup the popover for use
            settingsPopover = [[UIPopoverController alloc] initWithContentViewController:content];
            settingsPopover.popoverContentSize = CGSizeMake(360., 200.);
            settingsPopover.delegate = self;
            
            [settingsPopover presentPopoverFromRect:((UIButton*)sender).frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
            
            [content release];
        }
    }
    
    - (IBAction)startPressed:(id)sender
    {
        CGRect frame = [self.view frame];
        CGRect newFrame = CGRectMake(frame.origin.x, frame.origin.y + 1024, frame.size.width, frame.size.height);;
        [UIView animateWithDuration:1
                         animations:^{
                             self.view.frame = newFrame;
                         }
                         completion:^(BOOL finished){
                             NSLog(@"Finished Animations: %@", finished?@"YES":@"NO");
                             [[GazeDemoAppDelegate singleton] displayGameView];
                         }];
    }
    
    @end
    
    GazeIntroViewController has its own xib file. As for the instance of GLView, inside of MainWindow.xib, I have this:
    - Window
    - App Delegate
    - GLViewController
    - View (set up as a GLView instance)

    Inside of the xib files (both MainWindow and GazeIntroViewController) I have set up the views to have a width of 1024 and height of 768 and the orientation to landscape. (This app will ONLY run in landscape)

    My applicationDidFinishLaunching:
    Code:
    - (void)applicationDidFinishLaunching:(UIApplication *)application {
        viewController = [[GazeIntroViewController alloc] initWithNibName:@"GazeIntroViewController" bundle:[NSBundle mainBundle]];
        
        [window addSubview:glView];
        [glView setFrame:[glView.superview frame]];
    //    [window addSubview:viewController.view];
    //    [window bringSubviewToFront:viewController.view];
        
        g_AppDelegate = self;
        calibrationMode = NO;
        
        CGRect screenRect = [[UIScreen mainScreen] bounds];
        self.screenWidth = screenRect.size.height;
        self.screenHeight = screenRect.size.width;
        
        [window setFrame:CGRectMake(0, 0, 768, 1024)];
        [viewController.view setFrame:[viewController.view.superview frame]];
        
        [window makeKeyAndVisible];
        
        NSLog(@"Window subviews are: %@", [[window subviews] description]);
        NSLog(@"Window status is: %@", [window description]);
    }
    
    These are my two problems (and the reason for all those setFrame calls):
    1. If I uncomment those two lines in applicationDidFinishLaunching, my GazeIntroViewController view will show up rotated 90 deg. As in: the device is in landscape, but the view appears fixed to the 'portrait' position and will not move. The only way I can get that view to display correctly is if, inside MainWindow.xib, I set the instance of GLView to be inside of the window. Why does it work this way? I really want to understand this behaviour because it limits what I can do with views in code.
    2. Regardless of the orientation, the GLView will display nothing but a purple screen. There are no errors; I don't know how to proceed for debugging. Note that this project started from the OpenGL template linked to above. Before I started adding new views to it, it was perfectly OK rendering my models.

    Any help?
     

Share This Page