CS185c
Chris Pollett
Apr 18, 2012
#import <UIKit/UIKit.h> #import "Constants.h" @interface QuartzTestView : UIView { CGPoint firstTouch; CGPoint lastTouch; UIColor *currentColor; ShapeType shapeType; UIImage *drawImage; BOOL useRandomColor; } @property CGPoint firstTouch; @property CGPoint lastTouch; @property (nonatomic, retain) UIColor *currentColor; @property ShapeType shapeType; @property (nonatomic, retain) UIImage *drawImage; @property BOOL useRandomColor; @end
Here is the implementation of QuartzTestView. The drawRect method is where we override the base UIView ways of drawing things. We also have code to handle touch events (when they start, end and how they change).
#import "QuartzTestView.h" #import "UIColor-Random.h" @implementation QuartzTestView @synthesize firstTouch; @synthesize lastTouch; @synthesize currentColor; @synthesize shapeType; @synthesize drawImage; @synthesize useRandomColor; //we're using initWithCoder as we are loading the view from a nib // and so init and initWithFrame will never be called -(id)initWithCoder:(NSCoder *)coder { if((self = [super initWithCoder:coder])) { self.currentColor = [UIColor redColor]; self.useRandomColor = NO; if(drawImage == nil) self.drawImage = [UIImage imageNamed:@"myphoto.png"]; } return self; } // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during animation. - (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetLineWidth(context, 2.0); CGContextSetStrokeColorWithColor(context, currentColor.CGColor); CGContextSetFillColorWithColor(context, currentColor.CGColor); CGRect currentRect = CGRectMake( (firstTouch.x > lastTouch.x) ? lastTouch.x : firstTouch.x, (firstTouch.y > lastTouch.y) ? lastTouch.y : lastTouch.y, fabsf(firstTouch.x - lastTouch.x), fabsf(firstTouch.y -lastTouch.y)); switch (shapeType) { case kLineShape: CGContextMoveToPoint(context, firstTouch.x, firstTouch.y); CGContextAddLineToPoint(context, lastTouch.x, lastTouch.y); CGContextStrokePath(context); break; case kRectShape: CGContextAddRect(context, currentRect); CGContextDrawPath(context, kCGPathFillStroke); break; case kEllipseShape: CGContextAddEllipseInRect(context, currentRect); CGContextDrawPath(context, kCGPathFillStroke); break; case kImageShape: { CGFloat horizontalOffset = drawImage.size.width/2; CGFloat verticalOffset = drawImage.size.width/2; CGPoint drawPoint = CGPointMake(lastTouch.x - horizontalOffset, lastTouch.y - verticalOffset); [drawImage drawAtPoint:drawPoint]; break; } default: break; } } // Basic idea for touch handlers is to store in fields the touch info and redraw - (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { if(useRandomColor) self.currentColor = [UIColor randomColor]; UITouch *touch = [touches anyObject]; //touches recieves all //finger presses we just need one firstTouch = [touch locationInView:self]; //store touch start info lastTouch = [touch locationInView:self]; [self setNeedsDisplay]; // redraw view } - (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"hi there, this method does nothing"); } -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; lastTouch = [touch locationInView:self]; [self setNeedsDisplay]; // redraw view } -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; lastTouch = [touch locationInView:self]; [self setNeedsDisplay]; // redraw view } - (void)dealloc { [currentColor release]; [drawImage release]; [super dealloc]; } @end
#import <UIKit/UIKit.h> @interface QuartzTestViewController : UIViewController { IBOutlet UISegmentedControl *colorControl; } @property (nonatomic, retain) UISegmentedControl *colorControl; -(IBAction)changeColor:(id)sender; -(IBAction)changeShape:(id)sender; @end
#import "QuartzTestViewController.h" #import "QuartzTestView.h" #import "UIColor-Random.h" #import "Constants.h"; @implementation QuartzTestViewController @synthesize colorControl; - (IBAction)changeColor:(id)sender { UISegmentedControl *control = sender; NSInteger index = [control selectedSegmentIndex]; QuartzTestView *quartzView = (QuartzTestView *)self.view; switch (index) { case kRedColorTab: quartzView.currentColor = [UIColor redColor]; quartzView.useRandomColor = NO; break; case kBlueColorTab: quartzView.currentColor = [UIColor blueColor]; quartzView.useRandomColor = NO; break; case kYellowColorTab: quartzView.currentColor = [UIColor yellowColor]; quartzView.useRandomColor = NO; break; case kGreenColorTab: quartzView.currentColor = [UIColor greenColor]; quartzView.useRandomColor = NO; break; case kRandomColorTab: quartzView.useRandomColor = YES; break; default: break; } } - (IBAction)changeShape:(id)sender { UISegmentedControl *control = sender; [(QuartzTestView *)self.view setShapeType:[control selectedSegmentIndex]]; if([control selectedSegmentIndex] == kImageShape) colorControl.hidden = YES; else colorControl.hidden = NO; } - (void)viewDidLoad { [super viewDidLoad]; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return (interfaceOrientation == UIInterfaceOrientationPortrait); } - (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; } - (void)dealloc { [colorControl release]; [super dealloc]; } @end
- (void) locationManager: (CLLocationManager *)manager didUpdateToLocation: (CLLocation *)newLocation fromLocation: (CLLocation *)oldLocation ; - (void) locationManager:(CLLocationManager *)manager didUpdateHeading: (CLHeading *)newHeading ; - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error ;to handle location and heading info, we implement the interface CLLocationManagerDelegate; to promise that our controller has methods
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration;to handle accelerometer info, we implement the interface UIAccelerometerDelegate.
#import <UIKit/UIKit.h> #import <CoreLocation/CoreLocation.h> @interface SensorTestsViewController : UIViewController <CLLocationManagerDelegate, UIAccelerometerDelegate> { IBOutlet UILabel *locationLabel; IBOutlet UILabel *headingLabel; IBOutlet UILabel *accelerometerLabel; CLLocationManager *locationManager; } @property (retain, nonatomic) UILabel *locationLabel; @property (retain, nonatomic) UILabel *headingLabel; @property (retain, nonatomic) UILabel *accelerometerLabel; @end
#import "SensorTestsViewController.h" @implementation SensorTestsViewController @synthesize locationLabel; @synthesize headingLabel; @synthesize accelerometerLabel; - (void) viewDidLoad { locationManager = [[CLLocationManager alloc] init]; locationManager.delegate = self; locationManager.desiredAccuracy = kCLLocationAccuracyBest; [locationManager startUpdatingLocation]; [locationManager startUpdatingHeading]; UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer]; accelerometer.delegate = self; accelerometer.updateInterval = 1.0f/60.0f; /* how many times a second to send acclerometer updates. Shouldn't do too fast or battery dies. */ } - (void) locationManager: (CLLocationManager *)manager didUpdateToLocation: (CLLocation *)newLocation fromLocation: (CLLocation *)oldLocation { NSString *loc = [[NSString alloc] initWithFormat:@"My Coordinates: %@", newLocation.description]; locationLabel.text = loc; [loc release]; } - (void) locationManager:(CLLocationManager *)manager didUpdateHeading: (CLHeading *)newHeading { NSString *head = [[NSString alloc] initWithFormat:@"My Heading: %@", newHeading.description]; headingLabel.text = head; [head release]; } - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { if ([error code] == kCLErrorDenied) { [locationManager stopUpdatingLocation]; } } - (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration { NSString *head = [[NSString alloc] initWithFormat:@"Acceleration: X:%f Y:%f Z:%f", acceleration.x, acceleration.y, acceleration.z]; accelerometerLabel.text = head; [head release]; } - (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; } - (void)viewDidUnload { } - (void)dealloc { [locationLabel release]; [locationManager release]; [super dealloc]; } @end
string.xml <?xml version="1.0" encoding="utf-8"?> <resources> <string name="xyz">Acceleration: X: Y: Z:</string> <string name="app_name">AccelerometerDemo</string> </resources>
main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/xyz" android:id="@+id/acceleration" /> </LinearLayout>
public void onSensorChanged(SensorEvent event); public void onAccuracyChanged(Sensor arg0, int arg1);We really only care about code in the former method.
package org.pollett; import java.util.List; import android.app.Activity; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Bundle; import android.widget.TextView; public class AccelerometerDemo extends Activity implements SensorEventListener { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); accelerationLabel = (TextView )this.findViewById(R.id.acceleration); manager = (SensorManager)getSystemService(SENSOR_SERVICE); List<Sensor> list = manager.getSensorList(Sensor.TYPE_ACCELEROMETER); if(list != null && list.size() > 0) { accelerometer = list.get(0); } } @Override public void onResume() { super.onResume(); if(accelerometer != null) { manager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL); } } @Override public void onPause() { super.onPause(); if(accelerometer != null) { manager.unregisterListener(this); } } public void onAccuracyChanged(Sensor arg0, int arg1) { } public void onSensorChanged(SensorEvent event) { accelerationLabel.setText("Acceleration X:" + event.values[0] + " Y:" + event.values[1] + " Z:" + event.values[2]); } protected TextView accelerationLabel; protected SensorManager manager; protected Sensor accelerometer; }
[glView startAnimation];in appDidFinishLaunching
renderer = [[ES2Renderer alloc] init];
- (void) startAnimation { if (!animating) { if (displayLinkSupported) { // CADisplayLink is API new to iPhone SDK 3.1. Compiling against earlier versions will result in a warning, but can be dismissed // if the system version runtime check for CADisplayLink exists in -initWithCoder:. The runtime check ensures this code will // not be called in system versions earlier than 3.1. displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(drawView:)]; [displayLink setFrameInterval:animationFrameInterval]; [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; } else animationTimer = [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)((1.0 / 60.0) * animationFrameInterval) target:self selector:@selector(drawView:) userInfo:nil repeats:TRUE]; animating = TRUE; } }
- (void) drawView:(id)sender { [renderer render]; }
// Create an ES 2.0 context - (id) init { if (self = [super init]) { context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; if (!context || ![EAGLContext setCurrentContext:context] || ![self loadShaders]) { [self release]; return nil; } // Create default framebuffer object. The backing will be allocated for the current layer in -resizeFromLayer glGenFramebuffers(1, &defaultFramebuffer); glGenRenderbuffers(1, &colorRenderbuffer); glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer); glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer); } return self; }
The code to render things should look reasonably familiar if you followed the Android examples...
- (void) render { // Replace the implementation of this method to do your own custom drawing static const GLfloat squareVertices[] = { -0.5f, -0.33f, 0.5f, -0.33f, -0.5f, 0.33f, 0.5f, 0.33f, }; static const GLubyte squareColors[] = { 255, 255, 0, 255, 0, 255, 255, 255, 0, 0, 0, 0, 255, 0, 255, 255, }; static float transY = 0.0f; // This application only creates a single context which is already set current at this point. // This call is redundant, but needed if dealing with multiple contexts. [EAGLContext setCurrentContext:context]; // This application only creates a single default framebuffer which is already bound at this point. // This call is redundant, but needed if dealing with multiple framebuffers. glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer); glViewport(0, 0, backingWidth, backingHeight); glClearColor(0.5f, 0.5f, 0.5f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); // Use shader program glUseProgram(program); // Update uniform value glUniform1f(uniforms[UNIFORM_TRANSLATE], (GLfloat)transY); transY += 0.075f; // Update attribute values glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices); glEnableVertexAttribArray(ATTRIB_VERTEX); glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, 1, 0, squareColors); glEnableVertexAttribArray(ATTRIB_COLOR); // Validate program before drawing. This is a good check, but only really necessary in a debug build. // DEBUG macro must be defined in your debug configurations if that's not already the case. #if defined(DEBUG) if (![self validateProgram:program]) { NSLog(@"Failed to validate program: %d", program); return; } #endif // Draw glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // This application only creates a single color renderbuffer which is already bound at this point. // This call is redundant, but needed if dealing with multiple renderbuffers. glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER]; }
glViewport(0, 0, backingWidth, backingHeight);corresponds to the code we had in our Android application
gl.glViewport(0, 0, view.getWidth(), view.getHeight());