Why am I not getting a grey background in this program - objective-c

I followed the tutorial on this page:
http://iphone-3d-programming.labs.oreilly.com/ch01.html
I got down to the part where it says, "Compile and build and you should now see a solid gray screen. Hurray!" However, when I ran the program, I just get a black screen.
These are what the files look like:
HelloArrowAppDelegate.h
#import <UIKit/UIKit.h>
#import "GLView.h"
#interface HelloArrowAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *m_window;
GLView* m_view;
}
#end
HelloArrowAppDelegate.mm
#import "HelloArrowAppDelegate.h"
#implementation HelloArrowAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
CGRect screenBounds = [[UIScreen mainScreen] bounds];
m_window = [[UIWindow alloc] initWithFrame: screenBounds];
m_view = [[GLView alloc] initWithFrame:screenBounds];
[m_window addSubview: m_view];
[m_window makeKeyAndVisible];
return YES;
}
- (void)dealloc {
[m_view release];
[m_window release];
[super dealloc];
}
#end
GLView.h
#import <UIKit/UIKit.h>
#import <OpenGLES/EAGL.h>
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>
#interface GLView : UIView {
EAGLContext* m_context;
}
-(void) drawView;
#end
GLView.mm
#import "GLView.h"
#implementation GLView
- (void) drawView
{
glClearColor(0.5f, 0.5f, 0.5f, 1);
glClear(GL_COLOR_BUFFER_BIT);
[m_context presentRenderbuffer:GL_RENDERBUFFER_OES];
}
+ (Class) layerClass
{
return [CAEAGLLayer class];
}
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
CAEAGLLayer* eaglLayer = (CAEAGLLayer*) super.layer;
eaglLayer.opaque = YES;
m_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
if (!m_context || ![EAGLContext setCurrentContext:m_context]) {
[self release];
return nil;
}
// OpenGL Initialization
GLuint framebuffer, renderbuffer;
glGenFramebuffersOES(1, &framebuffer);
glGenFramebuffersOES(1, &renderbuffer);
[m_context
renderbufferStorage:GL_RENDERBUFFER_OES
fromDrawable: eaglLayer];
glFramebufferRenderbufferOES(
GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES,
GL_RENDERBUFFER_OES, renderbuffer);
glViewport(0, 0, CGRectGetWidth(frame), CGRectGetHeight(frame));
[self drawView];
}
return self;
}
- (void)dealloc {
if ([EAGLContext currentContext] == m_context) {
[EAGLContext setCurrentContext:nil];
}
[m_context release];
[super dealloc];
}
#end

The initWithFrame is incorrect. You want to generate a framebuffer and a renderbuffer and link the two. Instead you generate two framebuffers and completely ignore one. You should also keep the references to them (the variables 'renderbuffer' and 'framebuffer') in your class, as you'll need to delete them later unless you want to leak memory.
Without fixing the second issue, I recommend:
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
CAEAGLLayer* eaglLayer = (CAEAGLLayer*) super.layer;
       eaglLayer.opaque = YES;
m_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
       if (!m_context || ![EAGLContext setCurrentContext:m_context]) {
           [self release];
               return nil;
       }
// these should be in the class so that we can release them later,
// this will leak resources
GLuint framebuffer, renderbuffer;
// generate and bind a framebuffer
glGenFramebuffersOES(1, &framebuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, framebuffer);
// generate a colour renderbuffer; this example doesn't seem to want
// e.g. a depth buffer, but if it did then you'd generate and add one
// of those here also
// generate and bind
glGenRenderbuffersOES(1, &renderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, renderbuffer);
// get storage from the layer
[m_context
renderbufferStorage:GL_RENDERBUFFER_OES
fromDrawable: eaglLayer];
// link to the framebuffer
glFramebufferRenderbufferOES(
                            GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES,
                            GL_RENDERBUFFER_OES, renderbuffer);
glViewport(0, 0, CGRectGetWidth(frame), CGRectGetHeight(frame));
[self drawView];
}
return self;
}
Put framebuffer and renderbuffer somewhere you can get to them again at the relevant moment and you should also:
- (void)dealloc {
if(renderbuffer) glDeleteRenderbuffersOES(1, &renderbuffer);
if(framebuffer) glDeleteFramebuffersOES(1, &framebuffer);
if ([EAGLContext currentContext] == m_context) {
   [EAGLContext setCurrentContext:nil];
}
[m_context release];
[super dealloc];
}
I've tested this against the code you provide. I get the grey screen. Changing the call to glClearColor changes the colour of the screen, so clearly the GL context is working.

I ran into this issue and fixed it in my case by making sure the layer class was implemented right after the #implementation line.
#implementation GLView
+ (Class) layerClass
{
return [CAEAGLLayer class];
}

I got the gray background by getting rid of the line
m_window = [[UIWindow alloc] initWithFrame: screenBounds];
in application:didFinishLaunchingWithOptions: method in the file HelloArrowAppDelegate.h

The most likely cause is that you missed something when following the tutorial. Or they got it wrong. Whichever is most likely :-)
So the next stage is to debug and figure out what went wrong. Mostly likely you have missed a line of code which adds stuff to the display. I'd look in that section of your code first and compare it to the tutorial.
If that doesn't work then I'd take a copy of you code as a back up, then start stripping stuff out of it until you have the absolute minimal amount of code. Then post that here. Without some code we cannot tell you what is wrong.

whithout any code it might be difficult to tell you lol
your gray window comes from
(void) drawView
{
glClearColor(0.5f, 0.5f, 0.5f, 1);
glClear(GL_COLOR_BUFFER_BIT);
[m_context presentRenderbuffer:GL_RENDERBUFFER_OES];
}
check that this is called correclty

I'd try add the following to your above code:
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
CAEAGLLayer* eaglLayer = (CAEAGLLayer*) super.layer;
eaglLayer.opaque = YES;
eaglLayer.drawableProperties =
[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking,
kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
...
}
In code I use I define a drawableProperties but you seem to be missing it.

Related

Objective C - How to call UI-process from non-UI class

I'm trying to get the user position in iOS.
I guess the problem could be related to the dispatch_async.
I don't get errors during compilation but only the init function is called and the locationManager never gives an update.
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
 
#interface LocationC: UIViewController <CLLocationManagerDelegate> {
    CLLocationManager *locationManager;
}
 
#end
 
#implementation LocationC
 
- (instancetype) init {
    self = [super init];
    if (self) {
        locationManager = [[CLLocationManager alloc] init];
        locationManager.delegate = self;
        locationManager.distanceFilter = kCLDistanceFilterNone;
        locationManager.desiredAccuracy = kCLLocationAccuracyBest;
          
        if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
            [locationManager requestWhenInUseAuthorization];
        [locationManager startMonitoringSignificantLocationChanges];
        [locationManager startUpdatingLocation];
    }
    return self;
}
 
 //never called
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
    NSLog(#"test")
}
#end
 
 
class LocationGet : public claid::Module
{
    public:   
        void initialize()
        {
            dispatch_async(dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
                //Background Thread   
                dispatch_async(dispatch_get_main_queue(), ^(void){
                    //Run UI Updates
                    LocationC *example = [[LocationC alloc] init];
                });
            });   
        }
};
There might be a scenario in which the authorization status is not "Granted" when you call startMonitoring...
If the user sees the page for the first time and and location permission hasn't been authorized yet, requestWhenInUseAuthorization will display the dialogue, but you need to implement locationManager(_:didChangeAuthorization:) in order to know when the status has changed.
See this article:
https://developer.apple.com/documentation/corelocation/cllocationmanager/1620562-requestwheninuseauthorization
It says:
After the user makes a selection and determines the status, the location manager delivers the results to the delegate's locationManager(_:didChangeAuthorization:) method.
-- Update --
After another look, you should store a reference to the example instance, or else it would dealloc from memory before any location update is received. A UIViewController instance should be presented or pushed from another UIViewController instance, or alternatively it can be set as the root view controller of the application. Just instantiating it is not enough.
You have to wait for user permission if it's not explicitly given before starting listening to the location updates, so you enable your listener only when this permission choice is delivered:
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface LocationC: UIViewController <CLLocationManagerDelegate>
#property (nonatomic, readonly) CLLocationManager *locationManager;
#end
#implementation LocationC
- (instancetype) init {
if (self = [super init]) {
_locationManager = [CLLocationManager new];
_locationManager.delegate = self;
_locationManager.distanceFilter = kCLDistanceFilterNone;
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[self locationManagerDidChangeAuthorization:_locationManager];
}
return self;
}
#pragma mark CLLocationManagerDelegate
- (void)locationManagerDidChangeAuthorization:(CLLocationManager *)manager {
switch (manager.authorizationStatus) {
case kCLAuthorizationStatusAuthorizedWhenInUse:
case kCLAuthorizationStatusAuthorizedAlways:
[self startLocationListener];
break;
case kCLAuthorizationStatusNotDetermined:
[manager requestWhenInUseAuthorization];
break;
default:
[self stopLocationListener];
}
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
NSLog(#"New locations: %#", locations);
}
#pragma mark Private
- (void)startLocationListener {
[_locationManager startMonitoringSignificantLocationChanges];
[_locationManager startUpdatingLocation];
}
- (void)stopLocationListener {
[_locationManager stopMonitoringSignificantLocationChanges];
[_locationManager stopUpdatingLocation];
}
#end
Also, be advised that you will need to provide the user with some message explaining why exactly you request location permissions by adding NSLocationWhenInUseUsageDescription key in your app's Info.plist
EDIT:
Somehow I completely missed the part that LocationC is a view controller itself. With the extra details you provided in the comments, it seems to me that this instance is not retained by Cocoa Touch framework classes, so your example gets deallocated straight away.
Here are a couple of Cocoa Touch tweaks important to understand: UIKit expects an instance of any UIViewController (or its subclasses) to represent some view on the screen, thus if you expect LocationC to be purely responsible for non-UI logic, you should not subclass it from a UIViewController class and instead you may want to create as another controller property:
// Some view controller interface section
#interface ViewController: UIViewController
...
#property (strong, nonatomic, readonly) LocationC *locationListener;
...
#end
// Some view controller implementation section
#implementation ViewController
...
- (void)viewDidLoad {
[super viewDidLoad];
_locationListener = [LocationC new];
}
...
#end
If you instead expect this object to really represent a separate view, and conventionally a separate screen, then you are supposed to somehow pass the presenting view controller to the point in the code where LocationC is supposed to show up:
void initialize()
{
// m_PresentingController is the membmer of the class which keeps a reference to the
// presenting controller
__weak typeof(m_PresentingController) weakController = m_PresentingController;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
...
dispatch_async(dispatch_get_main_queue(), ^{
// If the presenting controller was destroyed before response handler comes
// int play, ignore the response
if (!weakController) {
return;
}
__strong typeof(weakController) strongController = weakController;
[weakController presentViewController:[LocationC new] animated:YES completion:nil]
});
});
}
Alternatively, you can make LocationC a new root controller of your app:
void initialize()
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
...
dispatch_async(dispatch_get_main_queue(), ^{
UIApplication.sharedApplication.delegate.window.rootViewController = [LocationC new];
});
});
}

SKView does not display?

The compiler does not give any errors but the the appeared screen is somewhat empty, even the following line of code does not affect it:
self.backgroundColor = [SKColor colorWithRed:0.0 green:0.0 blue:0.5 alpha:0.0];
I have tried to compare my code to other template codes of the view controller and scene files and did not succeed finding whats wrong. I am using Xcode 6. Below is the code:1)scene file
#import "GameScene.h"
#import "Ball.h"
#import "WorldGenerator.h"
// 1
#interface GameScene ()
#property BOOL isStarted;
#end
#implementation GameScene
{
Ball *ball;
SKNode *world;
WorldGenerator *generator;
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
//setup your scene here
self.anchorPoint = CGPointMake(0.5, 0.5);
self.backgroundColor = [SKColor colorWithRed:0.0 green:0.0 blue:0.5 alpha:0.0];
//World
world = [SKNode node];
[self addChild:world];
generator = [WorldGenerator generatorWithWorld:world];
[self addChild:generator];
[generator populate];
// Ball allocation
ball = [Ball ball];
[self addChild:ball];
}
return self;
}
2) View controller file
#import "GameViewController.h"
#import "GameScene.h"
#implementation GameViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
SKScene * scene = [SKScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}
- (BOOL)shouldAutorotate{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return UIInterfaceOrientationMaskAllButUpsideDown;
} else {
return UIInterfaceOrientationMaskAll;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (BOOL)prefersStatusBarHidden {
return YES;
}
#end
I think this line could be the issue.
SKScene * scene = [SKScene sceneWithSize:skView.bounds.size];
and should be
GameScene * scene = [GameScene sceneWithSize:skView.bounds.size];
You weren't creating an instance of GameScene just an empty SKScene.
I hope that helps.

Animating custom property

I'm trying to animate a custom property, and as I've seen on several sources it seems this would be the way to go, but I'm missing something. Here's the CALayer subclass:
#implementation HyNavigationLineLayer
#dynamic offset;
- (instancetype)initWithLayer:(id)layer
{
self = [super initWithLayer:layer];
if (self) {
HyNavigationLineLayer * other = (HyNavigationLineLayer*)layer;
self.offset = other.offset;
}
return self;
}
-(CABasicAnimation *)makeAnimationForKey:(NSString *)key
{
// TODO
return nil;
}
- (id<CAAction>)actionForKey:(NSString *)event
{
if ([event isEqualToString:#"offset"]) {
return [self makeAnimationForKey:event];
}
return [super actionForKey:event];
}
+ (BOOL)needsDisplayForKey:(NSString *)key
{
if ([key isEqualToString:#"offset"]) {
return YES;
}
return [super needsDisplayForKey:key];
}
- (void)drawInContext:(CGContextRef)ctx
{
NSLog(#"Never gets called");
}
#end
I believe this is the only relevant method on my view:
#implementation HyNavigationLineView
+ (Class)layerClass
{
return [HyNavigationLineLayer class];
}
#end
And, finally, in my view controller:
- (void)viewDidLoad
{
[super viewDidLoad];
// Instantiate the navigation line view
CGRect navLineFrame = CGRectMake(0.0f, 120.0f, self.view.frame.size.width, 15.0f);
self.navigationLineView = [[HyNavigationLineView alloc] initWithFrame:navLineFrame];
// Make it's background transparent
self.navigationLineView.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.0f];
self.navigationLineView.opaque = NO;
[[self.navigationLineView layer] addSublayer:[[HyNavigationLineLayer alloc] init]];
[self.view addSubview:self.navigationLineView];
}
The thing is that the drawInContext method is not called at all, although layerClass is. Am I missing something to make the layer draw?
Solved it. Need to call setNeedsDisplay
- (void)viewDidLoad
{
[super viewDidLoad];
// Instantiate the navigation line view
CGRect navLineFrame = CGRectMake(0.0f, 120.0f, self.view.frame.size.width, 15.0f);
self.navigationLineView = [[HyNavigationLineView alloc] initWithFrame:navLineFrame];
// Make it's background transparent
self.navigationLineView.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.0f];
self.navigationLineView.opaque = NO;
[[self.navigationLineView layer] addSublayer:[[HyNavigationLineLayer alloc] init]];
[self.view addSubview:self.navigationLineView];
[self.navigationLineView.layer setNeedsDisplay];
}

UIWebView Delegate : webViewDidFinishLoad not being called

I try to load an array of UIWebView with delegate associated.
for (GDataXMLElement *post in array) {
NSString *msg = [[[post elementsForName:#"message"] objectAtIndex:0] stringValue];
UIWebView *web_view = [[UIWebView alloc] initWithFrame:CGRectZero];
web_view.delegate = self;
[web_view loadHTMLString:msg baseURL:nil];
NSLog(#"Msg: %#", msg);
}
where msg is some HTML codes reading from XML. XML is loaded properly (verified by the NSLog line). Then in my webViewDidFinishLoad::
- (void)webViewDidFinishLoad:(UIWebView *)webView {
CGRect frame = webView.frame;
frame.size.height = 1;
webView.frame = frame;
CGSize fittingSize = [webView sizeThatFits:CGSizeZero];
frame.size = fittingSize;
webView.frame = frame;
NSLog(#"WebView Height: %.1f", webView.frame.size.height);
[webviews addObject:webView];
}
I auto resize the web views and add them to a NSMutableArray called webviews. However, webViewDidFinishLoad is not called.
In the header .h file, the interface is defined as:
#interface ViewController : UIViewController<UIWebViewDelegate>
What did I miss? Is the web_view in the loop get disposed ?
p.s. It looks like a duplicate of this question, but it isn't.
Alternate Approach 1
Declared at .h:
#property (nonatomic, weak) NSMutableArray *webviews;
Then for implementation:
for (GDataXMLElement *post in array) {
NSString *msg = [[[post elementsForName:#"message"] objectAtIndex:0] stringValue];
UIWebView *web_view = [[UIWebView alloc] initWithFrame:CGRectZero];
web_view.delegate = self;
[web_view loadHTMLString:msg baseURL:nil];
NSLog(#"Msg: %#", msg);
[self.webviews addObject:web_view];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
    CGRect frame = webView.frame;
    frame.size.height = 1;
    webView.frame = frame;
    CGSize fittingSize = [webView sizeThatFits:CGSizeZero];
    frame.size = fittingSize;
    webView.frame = frame;
    NSLog(#"WebView Height: %.1f", webView.frame.size.height);
}
Alternate Approach 2
Instead of instantiating UIWebView in for-loop, I put it in header file.
#interface ViewController : UIViewController<UIWebViewDelegate> {
UIWebView *web_view;
}
Then change the for-loop:
for (GDataXMLElement *post in array) {
NSString *msg = [[[post elementsForName:#"message"] objectAtIndex:0] stringValue];
web_view = [[UIWebView alloc] initWithFrame:CGRectZero];
web_view.delegate = self;
[web_view loadHTMLString:msg baseURL:nil];
NSLog(#"Msg: %#", msg);
[self.webviews addObject:web_view];
}
In this approach, only the delegate of last message gets called.
Summary & Highlights:
My objectives:
Load all UIWebView with variable-size contents
The web views should auto fit the size of contents ( without scrolling ); that's why webViewDidFinishLoad is required.
Arrange the web views properly on current view ( or probably a UIScrollView ) in order to make it not overlapped.
You should do like this,
#interface UIViewController <UIWebViewDelegate>
{
}
#property(nonatomic, strong)NSMutableArray *webViews;
#end
////////////////
#implementation UIViewController
-(void)viewDidLoad
{
[super viewDidLoad];
webViews = [[NSMutableArray alloc] init];
for (GDataXMLElement *post in array)
{
NSString *msg = [[[post elementsForName:#"message"] objectAtIndex:0] stringValue];
UIWebView *web_view = [[UIWebView alloc] initWithFrame:CGRectZero];
web_view.delegate = self;
[web_view loadHTMLString:msg baseURL:nil];
NSLog(#"Msg: %#", msg);
[self.webViews addObject:web_view];
//i don't know why would you not add these web-views on the view controller,
// but still, here is the addition of these.
[self.view addSubView:web_view];
}
}
#end
This should make the web-views call the delegate properly on load.
If you're using ARC, the web_view is deallocated at the end of the for loop.
Here is my final solution ( hope it is useful for others ):
In Storyboard, I added a UIScrollView in the view controller, and link it with the IBOutlet.
Header file:
#import <UIKit/UIKit.h>
#import "GDataXMLNode.h"
#interface ViewController : UIViewController<UIWebViewDelegate> {
IBOutlet UIScrollView *scroll_view;
}
#property (nonatomic, strong) UIWebView *web_view;
#end
Implementation file:
float currentY;
NSArray *array;
int count;
GDataXMLDocument *doc;
- (void)viewDidLoad
{
[super viewDidLoad];
currentY = 0;
count = 0;
[self loadXML];
}
- (void)loadXML {
// Some codes to load the XML contents into array variable
[self loadWebView];
}
- (void)loadWebView {
if(count < array.count) {
GDataXMLElement *post = [array objectAtIndex:count];
NSString *msg = [[[post elementsForName:#"message"] objectAtIndex:0] stringValue];
count++;
self.web_view = [[UIWebView alloc] initWithFrame:CGRectMake(10, currentY, 300, 1.0f)];
self.web_view.delegate = self;
[self.web_view setHidden:YES];
[self.web_view loadHTMLString:msg baseURL:nil];
[scroll_view addSubview:self.web_view];
} else {
// end the process
[scroll_view setContentSize:CGSizeMake(320, currentY + 30)];
return;
}
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
CGRect frame = webView.frame;
frame.size.height = 1;
webView.frame = frame;
CGSize fittingSize = [webView sizeThatFits:CGSizeZero];
frame.size = fittingSize;
webView.frame = frame;
[webView setHidden:NO];
currentY += webView.frame.size.height + 20.0f; // 20.0f is spaces between posts
NSLog(#"WebView Height: %.1f", webView.frame.size.height);
[self loadWebView];
}

How to use drawRect to draw in a existing view?

I'm doing a GPS tracking app. Every time it receives a Latitude/Longitude it converts it to (x,y) coordinates and calls drawRect to draw a line between two (x,y) pairs.
However, the drawRect method just clear all the old contents before it draw new thing. How I can make the drawRect to draw new thing on the existing canvas? Thanks in advance
here is my viewController.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#class routetrack;
#interface simpleDrawViewController : UIViewController <CLLocationManagerDelegate> {
CLLocationManager *locationManager;
IBOutlet routetrack *routeroute;
float latOld;
float longOld;
float latNew;
float longNew;
}
- (IBAction) gpsValueChanged;
#property (nonatomic,retain) CLLocationManager *locationManager;
#property (retain,nonatomic) routetrack *routeroute;
ViewController.m
#import "simpleDrawViewController.h"
#import "routetrack.h"
#implementation simpleDrawViewController
#synthesize locationManager,btn,routeroute;
- (IBAction) gpsValueChanged{
[routeroute setLocationX:longNew setLocationY:latNew
oldLocation:longOld oldLocationY:latOld ];
[routeroute setNeedsDisplay];
}
- (void)viewDidLoad {
[super viewDidLoad];
if (nil == locationManager)
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}
- (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.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void) locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
if (oldLocation) {
latOld=oldLocation.coordinate.latitude;
longOld=oldLocation.coordinate.longitude;
latNew=newLocation.coordinate.latitude;
longNew=newLocation.coordinate.longitude;
}
else {
latOld=53.3834;
longOld=-6.5876;
latNew=53.3835;
longNew=-6.5877;
}
[self gpsValueChanged];
}
- (void) locationManager:(CLLocationManager *)manager didFailwithError:
(NSError *)error
{
if([error code] == kCLErrorDenied)
[locationManager stopUpdatingLocation];
NSLog(#"location manger failed");
}
- (void)dealloc {
[locationManager release];
[super dealloc];
}
#end
my drawing class, a subclass of UIView
#import "routetrack.h"
#implementation routetrack
- (void) setLocationX:(float) loX setLocationY:(float) loY
oldLocation:(float) oldX oldLocationY:(float) oldY {
self->locationX = loX;
self->locationY = loY;
self->oldLoX = oldX;
self->oldLoY = oldY;
scaleX= 36365.484375;
scaleY= 99988.593750;
maxLat= 53.3834;
maxLog= -6.5976;
drawX = locationX;
drawY = locationY;
tempX=(maxLog - drawX) * scaleX;
tempY=(maxLat - drawY) * scaleY;
lastX = (int) tempX;
lastY = (int) tempY;
drawX = oldLoX;
drawY = oldLoY;
tempX=(maxLog - drawX) * scaleX;
tempY=(maxLat - drawY) * scaleY;
startX = (int) tempX;
startY = (int) tempY;
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
- (void)drawRect:(CGRect)rect {
int firstPointX = self->startX;
int firstPointY = self->startY;
int lastPointX = self->lastX;
int lastPointY = self->lastY;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextSetLineWidth(context, 2.0);
CGContextMoveToPoint(context, firstPointX, firstPointY);
CGContextAddLineToPoint(context, lastPointX , lastPointY);
CGContextStrokePath(context);
}
UIView does not have a canvas model. If you want to keep a canvas, you should create a CGLayer or a CGBitmapContext and draw onto that. Then draw that in your view.
I would create an ivar for a CGLayerRef, and then drawRect: would look something like this (untested):
- (void)drawRect:(CGRect)rect {
if (self.cgLayer == NULL) {
CGLayerRef layer = CGLayerCreateWithContext(UIGraphicsContextGetCurrent(), self.bounds, NULL);
self.cgLayer = layer;
CGLayerRelease(layer);
}
... various Core Graphics calls with self.cgLayer as the context ...
CGContextDrawLayerInRect(UIGraphicsContextGetCurrent(), self.bounds, self.cgLayer);
}
Whenever you wanted to clear your canvas, just self.cgLayer = NULL.
setCgLayer: would be something like this:
- (void)setCgLayer:(CGLayerRef)aLayer {
CGLayerRetain(aLayer);
CGLayerRelease(cgLayer_);
cgLayer_ = aLayer;
}
What exactly to you mean by "old contents"? If you want to draw a line from your GPS data, you have to draw all points every time in the implementation of drawRect.