Why isn't my variable preserved in drawRect? - objective-c

Im trying to have an nsview that takes a custom object as an argument, then draws out some text fields based on that object.
-(instancetype)initWithFrame:(NSRect)frameRect parent:(id)parent item:(TempObj*)item{
self = [super initWithFrame:frameRect];
if (self){
textDelegate = parent;
myObject = item;
}
return self;
}
But by the time it reaches the drawRect, the myObject variable is nil.
- (void)drawRect:(NSRect)dirtyRect {
if (myObject != nil){
NSTextField* title = [[NSTextField alloc]initWithFrame:CGRectMake(textRoot.x, textRoot.y-20, 200, 20)];
title.stringValue = [myObject getName];
title.backgroundColor = [NSColor yellowColor];
[self addSubview:title];
NSLog(#"here");
}else{
NSLog(#"broken");
}
}
The view is initialized in another class with:
_inventoryItemView = [[InventoryItemView alloc]initWithFrame:_inventoryItemView.frame parent:self item:testObj];
Is there some way for me to maintain the variable, or is there something with the scope I am missing?

Related

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];
}

Map view annotations with different pin colors

I have an array with over 200 objects and I am trying to perform a loop through each of them.
Each object will have a yes/no field and I want to display a different coloured marker dependent on that yes / no value.
From what I can see is happening my loop is going through each object first and then all the annotation is added at the end for each object .
Since I perform a check within my loop through the array on the yes no value when all the annotation is added to my map, it will use the yes/no value from the last object in the array when it goes to plot for all.
How can I have it so that the marker will be different dependent on the yes/no value for each individual element?
My code is
for (i = 0; i < [appDelegate.itemArray count]; i++) {
item_details *tempObj = [appDelegate.itemArray objectAtIndex:i];
location.latitude = [tempObj.lat floatValue];
location.longitude = [tempObj.lon floatValue];
current_yesno = tempObj.yesno;
MapViewAnnotation *newAnnotation = [[MapViewAnnotation alloc]initWithTitle:tempObj.name andCoordinate:location];
[self.mapView addAnnotation:newAnnotation];
[newAnnotation release];
}
with my annotation code as follows
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation{
MKPinAnnotationView *annView=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"currentloc"];
if(current_yesno == YES){
annView.pinColor = MKPinAnnotationColorGreen;
}
else
{
annView.pinColor = MKPinAnnotationColorRed;
}
annView.animatesDrop=NO;
annView.canShowCallout = YES;
annView.calloutOffset = CGPointMake(-5, 5);
return annView;
}
and current_yesno is declared in my .h file.
The viewForAnnotation delegate method isn't necessarily called immediately after you do addAnnotation and it can also be called at other times by the map view when it needs to get the view for an annotation (while your code is doing something completely different).
So you can't depend on the value of an ivar being in sync with some code outside that delegate method.
Instead, add the yesno property to your custom MapViewAnnotation class, set it when creating the annotation and then access its value in viewForAnnotation through the annotation parameter (ie. the map view is giving you a reference to the exact annotation object it wants the view for).
Example:
MapViewAnnotation *newAnnotation = [[MapViewAnnotation alloc] init...
newAnnotation.yesno = tempObj.yesno; // <-- set property in annotation
[self.mapView addAnnotation:newAnnotation];
Then in viewForAnnotation:
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation
{
if (![annotation isKindOfClass:[MapViewAnnotation class]])
{
// Return nil (default view) if annotation is
// anything but your custom class.
return nil;
}
static NSString *reuseId = #"currentloc";
MKPinAnnotationView *annView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
if (annView == nil)
{
annView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId];
annView.animatesDrop = NO;
annView.canShowCallout = YES;
annView.calloutOffset = CGPointMake(-5, 5);
}
else
{
annView.annotation = annotation;
}
MapViewAnnotation *mvAnn = (MapViewAnnotation *)annotation;
if (mvAnn.yesno)
{
annView.pinColor = MKPinAnnotationColorGreen;
}
else
{
annView.pinColor = MKPinAnnotationColorRed;
}
return annView;
}
MKPinAnnotationView *pin = (MKPinAnnotationView *) [self.mapView dequeueReusableAnnotationViewWithIdentifier: #"id"];
if (pin == nil)
{
pin = [[MKPinAnnotationView alloc] initWithAnnotation: annotation reuseIdentifier: #"id"] ;
}
else
{
pin.annotation = annotation;
}
pin.pinTintColor=[UIColor blueColor];
pin.canShowCallout = true;

AQGridViewCell customization

I am looking to customize an AQGridViewCell by adding a title, date, and image for each cell.
What I have tried so far is:
//View Controller
- (AQGridViewCell *) gridView: (AQGridView *) gridView cellForItemAtIndex: (NSUInteger) index
{
static NSString * CellIdentifier = #"CellIdentifier";
IssueCell * cell = (IssueCell *)[self.gridView dequeueReusableCellWithIdentifier: CellIdentifier];
if ( cell == nil )
{
cell = [[IssueCell alloc] initWithFrame: CGRectMake(0.0, 0.0, 72.0, 72.0) reuseIdentifier: CellIdentifier];
}
//This model object contains the title, picture, and date information
IssueModel *m = (IssueModel *)[self.issues objectAtIndex:index];
[cell setIssueModel:m];
return cell;
}
//Cell class
#import "IssueCell.h"
#import <QuartzCore/QuartzCore.h>
#implementation IssueCell
#synthesize issueModel;
- (id) initWithFrame: (CGRect) frame reuseIdentifier:(NSString *) reuseIdentifier
{
self = [super initWithFrame: frame reuseIdentifier: reuseIdentifier];
if ( self == nil )
return ( nil );
self.contentView.backgroundColor = [UIColor redColor];
self.backgroundColor = [UIColor blueColor];
self.contentView.opaque = NO;
self.opaque = NO;
self.selectionStyle = AQGridViewCellSelectionStyleNone;
return self;
}
#end
My questions is, since init is called before I have access to the model object, where can I setup the title, picture, and date for my cell?
You have to initialize your UI components in the initWithFrame. Example:
In the interface of your IssueCell add UI variables you would like to have:
#interface IssueCell : AQGridViewCell {
UIImageView *im;
UILabel *dateLabel;
}
- (id) initWithFrame: (CGRect) frame reuseIdentifier:(NSString *) reuseIdentifier
{
self = [super initWithFrame: frame reuseIdentifier: reuseIdentifier];
if ( self == nil )
return ( nil );
self.contentView.backgroundColor = [UIColor redColor];
self.backgroundColor = [UIColor blueColor];
self.contentView.opaque = NO;
self.opaque = NO;
self.selectionStyle = AQGridViewCellSelectionStyleNone;
im = [[UIImageView alloc] initWithFrame:yourImageViewFrameHere];
dateLabel = [[UILabel alloc] initWithFrame:yourLabelFrameHere];
[self addSubview:im];
[self addSubview:dateLabel];
return self;
}
#end
Later, you assign desired values in the cellForItemAtIndex method. Example:
- (AQGridViewCell *) gridView: (AQGridView *) gridView cellForItemAtIndex: (NSUInteger) index
{
static NSString * CellIdentifier = #"CellIdentifier";
IssueCell * cell = (IssueCell *)[self.gridView dequeueReusableCellWithIdentifier: CellIdentifier];
if ( cell == nil )
{
cell = [[IssueCell alloc] initWithFrame: CGRectMake(0.0, 0.0, 72.0, 72.0) reuseIdentifier: CellIdentifier];
}
//This model object contains the title, picture, and date information
//
IssueModel *m = (IssueModel *)[self.issues objectAtIndex:index];
[cell.im setImage: m.picture];
[cell.dateLabel setText:[date localizedDescription]];
return cell;
}
Do not store your model data in the UI components. That's a no no. Keep your model separated from the UI. This is only a pseudocode, not tested since I do not have my mac here.
Let me know if it helps.

Obj-C, Instance variable used while 'self' is not set to the result of '[(super or self) init...]'

I know I asked a similar question to this not long ago, but I'm still a little unsure about it. The same sort of thing happens in several places.
Instance variable used while 'self' is not set to the result of '[(super or self) init...]'
A
- (id)initWithCoder:(NSCoder *)decoder {
if (![super init]) return nil;
red = [decoder decodeFloatForKey:kRedKey]; //occurs here
green = [decoder decodeFloatForKey:kGreenKey];
blue = [decoder decodeFloatForKey:kBlueKey];
return self;
}
B
- (id)initWithFrame:(CGRect)frame title:(NSString*)str sideUp:(BOOL)up{
if(![super initWithFrame:frame]) return nil;
int y;
UIImage *img;
if(up){
img = [UIImage imageNamedTK:#"TapkuLibrary.bundle/Images/graph/popup"];
y = 5;
}else{
img = [UIImage imageNamedTK:#"TapkuLibrary.bundle/Images/graph/popdown"];
y = 14;
}
background = [[UIImageView alloc] initWithImage:img]; // occurs here
C
- (id) initWithFrame:(CGRect)frame {
if(![super initWithFrame:frame]) return nil;
UILabel *titleBackground = [[[UILabel alloc] initWithFrame:
CGRectMake(0, 0, 480, 40)] autorelease];
titleBackground.backgroundColor = [UIColor whiteColor];
[self addSubview:titleBackground];
titleLabel = [[UILabel alloc] initWithFrame:CGRectZero]; // occurs here
For block A, is this correct
self = [self init];
if( self != nil )
{
And B & C
- (id) initWithFrame:(CGRect)frame {
super = [super initWithFrame:frame]
if(super != nil)
{
Generally, you're supposed to write:
self = [super init...]; // Potentially change "self"
if (self) {
something = x;
another = y;
}
return self;
This is because init may not return the original self value in some cases.

Objective-C: Assigning the value of a static variable to an instance variable

I essentially want to give each instance of a class a unique id.
So, I created a static integer. I increment it each time a new object is created and then assign the value of the static variable to an ivar. But clearly I don't understand something because, let's say I create three objects, "thisPageNumber" (which is the instance variable) is always 3 no matter which object I reference.
More information:
This class creates a number of "Page" objects. I'd like each page to know it's page number so that it can display the correct page art as well as perform a number of other various actions.
.h partial code:
#interface Page : UIViewController
{
NSNumber *thisPageNumber;
UIImageView *thisPageView;
UIImageView *nextPageView;
UIImageView *prevPageView;
UIImageView *pageArt;
}
.m partial code:
#implementation Page
static int pageCount = 0;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
pageCount++;
thisPageNumber = pageCount;
}
return self;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
CGRect defaultFrame = CGRectMake(0.0, 0.0, 1024.0, 768.0);
if (thisPageView == nil) {
thisPageView = [[UIImageView alloc]
initWithImage:[UIImage
imageNamed:[NSString stringWithFormat:#"Page%i.png", [thisPageNumber intValue]]]];
thisPageView.frame = defaultFrame;
[self.view addSubview:thisPageView];
}
if (nextPageView == nil && [thisPageNumber intValue] < BOOK_PAGE_COUNT) {
nextPageView = [[UIImageView alloc]
initWithImage:[UIImage
imageNamed:[NSString stringWithFormat:#"Page%i.png", [thisPageNumber intValue]+1]]];
nextPageView.frame = defaultFrame;
[self.view addSubview:nextPageView];
}
if (prevPageView == nil && [thisPageNumber intValue] > 1) {
prevPageView = [[UIImageView alloc]
initWithImage:[UIImage
imageNamed:[NSString stringWithFormat:#"Page%i.png", [thisPageNumber intValue]-1]]];
prevPageView.frame = defaultFrame;
[self.view addSubview:prevPageView];
}
}
I'm not sure why the compiler didn't complain, but part of your problem is here:
thisPageNumber = pageCount;
NSNumber is an object. To set it to the current pageCount value, use
thisPageNumber = [[NSNumber alloc] initWithInt:pageCount];
Why don't you just use self as the unique ID? It's unique.