I can't figure out how I'm supposed to initialize this custom class, the code isn't mine and I'm fairly new to Obj-C, so I'm slow on the uptake.
PFTintedButton.h
#import <UIKit/UIKit.h>
#class CALayer;
typedef enum
{
PFTintedButtonRenderTypeStandard,
PFTintedButtonRenderTypeCandy,
PFTintedButtonRenderTypeOpal,
} PFTintedButtonRenderType;
#interface PFTintedButton : UIButton
{
#private
UIColor * tint;
CGFloat cornerRadius;
PFTintedButtonRenderType renderType;
UIImage * stretchImage;
CALayer * glowLayer;
UILabel * subLabel;
BOOL customShadow;
BOOL customTitleColor;
}
#property( nonatomic, retain ) UIColor * tint;
#property( nonatomic, assign ) CGFloat cornerRadius;
#property( nonatomic, assign ) PFTintedButtonRenderType renderType;
#property( nonatomic, readonly ) UILabel * subLabel;
#end
PFTintedButton.m excerpt
#import "PFTintedButton.h"
#import "PFDrawTools.h"
#import "UIColor+PFExtensions.h"
#import <QuartzCore/QuartzCore.h>
#define glowRadius 30
#interface PFTintedButton()
-(void) createBackgroundImage;
-(void) createGlowLayer;
#end
#implementation PFTintedButton
-(void) dealloc
{
SafeRelease( tint );
SafeRelease( stretchImage );
SafeRelease( subLabel );
SafeRelease( glowLayer );
[NSObject cancelPreviousPerformRequestsWithTarget: self selector: #selector(createGlowLayer) object: nil];
[NSObject cancelPreviousPerformRequestsWithTarget: self selector: #selector(renderGlowLayerOnMainThread) object: nil];
[super dealloc];
}
-(void) initCommon
{
cornerRadius = 6;
}
-(id) initWithFrame: (CGRect) frame
{
if( self = [super initWithFrame: frame] )
{
[self initCommon];
}
return self;
}
-(id) initWithCoder: (NSCoder *) coder
{
if( self = [super initWithCoder: coder] )
{
// this is such a hack but there's no other way to determine if the title label's settings
// have been modified in IB.
NSDictionary * dic = [coder decodeObjectForKey: #"UIButtonStatefulContent"];
NSObject * st = [dic objectForKey: [NSNumber numberWithInt: UIControlStateNormal]];
NSString * bc = [st description];
customTitleColor = [bc rangeOfString: #"TitleColor = UIDeviceRGBColorSpace 0.196078 0.309804 0.521569 1"].location == NSNotFound;
customShadow = [bc rangeOfString: #"ShadowColor = UIDeviceRGBColorSpace 0.5 0.5 0.5 1"].location == NSNotFound;
[self initCommon];
//[super setBackgroundColor: [[UIColor redColor] colorWithAlphaComponent: .5]];
}
return self;
}
I just included a small bit of the implementation, the full source can be found here. As a part of this repository by Paul-Alexander.
I initialize it like this:
PFTintedButton* buttony = [PFTintedButton buttonWithType:UIButtonTypeCustom];
[buttony setTint:[UIColor redColor]];
[buttony setBackgroundColor:[UIColor redColor]];
[buttony setRenderType:PFTintedButtonRenderTypeCandy];
[buttony setTitle:#"Title for Button" forState:UIControlStateNormal];
And it hiccups in PFDrawTools, it seems the tint variable is nil. So is it because I am initializing it incorrectly? Has anyone had success with this?
It seems to explicitly handle initWithFrame, I would try that.
PFTintedButton *buttony = [[[PFTintedButton alloc] initWithFrame:<some_frame>] autorelease];
Try this instead:
PFTintedButton* buttony = [[PFTintedButton alloc] initWithFrame:CGRectMake(x, y, width, height)];
Have you defined buttonWithType: initialize method in PFTintedButton class?
Related
I have a collections view and the view works fine when I load the data initially, but crashes when I try to reload it. take a look at the reload thats happening on the method - scrollViewDidEndDecelerating
and the error is
-[FeedCollectionViewCell release]: message sent to deallocated instance 0x800d6f70
here is the code.
This the Controller:
#interface MyViewController : UIViewController <UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout, UIScrollViewDelegate>
{
}
#property (retain, nonatomic) IBOutlet UICollectionView *collectionView;
#end
This is the implementation :
#implementation MyViewController
#interface FeedCollectionViewController ()
#property (nonatomic, strong) NSMutableArray *dataArray;
-(void)getData;
#end
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
if (self.dataArray == nil) {
self.dataArray = [[NSMutableArray alloc] init];
}
}
return self;
}
- (void)viewDidLoad
{
[self.collectionView registerClass:[FeedCollectionViewCell class] forCellWithReuseIdentifier:[FeedCollectionViewCell reuseIdentifier]];
// Configure layout
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setItemSize:CGSizeMake(153, 128)];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
[self.collectionView setCollectionViewLayout:flowLayout];
[self getData];
}
-(void)getData
{
// here I make a call to the server to get the data and I set the data array
self.dataArray = mydatafromserver
[self.collectionView reloadData];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath;
{
FeedCollectionViewCell *cell = (FeedCollectionViewCell *)[cv dequeueReusableCellWithReuseIdentifier:[FeedCollectionViewCell reuseIdentifier] forIndexPath:indexPath];
[cell.label setText:[[self.dataArray objectAtIndex:indexPath.row] name];
return cell;
}
- (void)scrollViewDidEndDecelerating:(UICollectionView *) collectionView
{
NSLog(#"FeedCollectionViewController::scrollViewDidEndDecelerating");
CGPoint offset = collectionView.contentOffset;
CGRect bounds = collectionView.bounds;
CGSize size = collectionView.contentSize;
UIEdgeInsets inset = collectionView.contentInset;
float y = offset.y + bounds.size.height - inset.bottom;
float h = size.height;
// NSLog(#"offset: %f", offset.y);
// NSLog(#"content.height: %f", size.height);
// NSLog(#"bounds.height: %f", bounds.size.height);
// NSLog(#"inset.top: %f", inset.top);
// NSLog(#"inset.bottom: %f", inset.bottom);
// NSLog(#"pos: %f of %f", y, h);
float reload_distance = 300;
if(y > h - reload_distance) {
NSLog(#"load more rows...");
***//// FAIL HERE***
[self.collectionView reloadData];
}
}
Here is the cell
#import <UIKit/UIKit.h>
#define FB_COLL_CELL_IDENTIFIER #"CollectionCellIdentifier"
#interface FeedCollectionViewCell : UICollectionViewCell
#property (retain, nonatomic) IBOutlet UIImageView *image;
#property (retain, nonatomic) IBOutlet UILabel *label;
#property (retain, nonatomic) IBOutlet UIActivityIndicatorView *spinner;
+ (NSString *)reuseIdentifier;
#end
#import "FeedCollectionViewCell.h"
#import "CustomCellBackground.h"
#implementation FeedCollectionViewCell
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
NSLog(#"FeedCollectionViewCell initWithFrame");
// Initialization code
NSArray *arrayOfViews = [[NSBundle mainBundle] loadNibNamed:#"FeedCollectionViewCell" owner:self options:nil];
if ([arrayOfViews count] < 1) {
return nil;
}
if (![[arrayOfViews objectAtIndex:0] isKindOfClass:[UICollectionViewCell class]]) {
return nil;
}
self = [arrayOfViews objectAtIndex:0];
CustomCellBackground *backgroundView = [[CustomCellBackground alloc] initWithFrame:CGRectZero];
self.selectedBackgroundView = backgroundView;
}
return self;
}
+ (NSString *)reuseIdentifier
{
NSLog(#"FBDisplayCell ... static reuseIdentifier called ");
return (NSString *)FB_COLL_CELL_IDENTIFIER;
}
#end
and this is another one. adding this because its being used. I dont think the problem is here, but u never know!
#import <UIKit/UIKit.h>
#interface CustomCellBackground : UIView
#end
#import "CustomCellBackground.h"
#implementation CustomCellBackground
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void)drawRect:(CGRect)rect
{
// draw a rounded rect bezier path filled with blue
CGContextRef aRef = UIGraphicsGetCurrentContext();
CGContextSaveGState(aRef);
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:5.0f];
[bezierPath setLineWidth:5.0f];
[[UIColor blackColor] setStroke];
UIColor *fillColor = [UIColor colorWithRed:0.529 green:0.808 blue:0.922 alpha:1]; // color equivalent is #87ceeb
[fillColor setFill];
[bezierPath stroke];
[bezierPath fill];
CGContextRestoreGState(aRef);
}
#end
The issue was with the UICollectioViewCell -initWithFrame
i changed to not load the nib and i started creating my own
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor colorWithWhite:0.85f alpha:1.0f];
self.layer.borderColor = [UIColor whiteColor].CGColor;
self.layer.borderWidth = 3.0f;
self.layer.shadowColor = [UIColor blackColor].CGColor;
self.layer.shadowRadius = 3.0f;
self.layer.shadowOffset = CGSizeMake(0.0f, 2.0f);
self.layer.shadowOpacity = 0.5f;
self.imageView = [[UIImageView alloc] initWithFrame:self.bounds];
self.imageView.contentMode = UIViewContentModeScaleAspectFill;
self.imageView.clipsToBounds = YES;
[self.contentView addSubview:self.imageView];
}
return self;
}
I am another rookie in iPhone programming and I have a very basic question which I can not answer.
First of all, I am writing code in xcode 3.1.4, so that I can learn the basics on an old platform and hopefully in the near future this will allow me to create portable apps (in the sense that I will be supporting iPhone 3 as well). Unfortunately though, from what I understand, apple has stripped the documentation that came with xcode 3.1.4 and this makes it hard to guess what is the right way of doing things, since the current documentation and examples suggest routes more relevant to iOS 5 (e.g. storyboard). So, even though I appear to understand the whole concept of MVC, and although my first real toy app is actually functioning it is certainly not managing the memory correctly as the relevant dealloc methods are not used (the relevant NSLogs that I have are not shown). For the learning process I am following the course material that is online from CS193P in Winter of 2010.
To the specifics now.
I want to create a single-window app (Polygon, Assignment 3). Let's forget about the interface builder; all views/subviews will be created programmatically. For that I create a custom UIViewController. I also create a custom UIView in order to describe a custom subview of my single view that I want to present. (It is very likely that we disagree with my approach here already but for now I seriously believe that what I am trying here is conceptually right). In the app delegate (.h) I declare my mainController and this is all I really declare. The file is the following:
#import <UIKit/UIKit.h>
#import "MainViewController.h"
#interface MySimpleHelloPolyAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
MainViewController * mainController;
}
#property (nonatomic, retain) IBOutlet UIWindow * window;
#property (nonatomic, assign) IBOutlet MainViewController * mainController;
#end
On runtime, I want mainController to allocate the necessary memory for the main view presented to the user and also add a subview of my custom UIView (this will be the canvas for drawing polygons). So, essentially I want to treat window as a container, the view of mainController to actually present the main view to the user, and to that view I will also have attached a subview with an instance of our custom canvas (custom view). Below is the AppDelegate.m file:
#import "MySimpleHelloPolyAppDelegate.h"
#implementation MySimpleHelloPolyAppDelegate
#synthesize window, mainController;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[window setBackgroundColor:[UIColor blueColor]]; // I do not want to see 'blue' on runtime
// On the creation of the MainViewController subclass I indicated that I want a nib for my new controller
// simply because this is the recommended way by apple. Apparently this should be an overkill for a simple
// one-window app, but nevertheless it should be ok.
mainController = [[MainViewController alloc] initWithNibName:nil bundle:nil];
[window addSubview:mainController.view];
[window makeKeyAndVisible];
}
- (void)dealloc {
NSLog(#"(AppDelegate): Dealloc is called");
[mainController.view removeFromSuperview];
[mainController release];
[window release];
[super dealloc];
}
#end
Let's see the MainViewController.h:
#import <UIKit/UIKit.h>
#import "Polygon.h"
#import "Canvas.h"
#interface MainViewController : UIViewController {
IBOutlet UILabel * numSidesTextLabel;
IBOutlet UILabel * numSidesValueLabel;
IBOutlet UILabel * advancedOptionsLabel;
IBOutlet UILabel * polygonNameLabel;
IBOutlet UISwitch * advancedOptionsSwitch; // Not supported yet
IBOutlet UIButton * increaseButton;
IBOutlet UIButton * decreaseButton;
IBOutlet Canvas * myCanvas;
Polygon * myPolygon;
}
#property (nonatomic, retain) IBOutlet UILabel * numSidesTextLabel;
#property (nonatomic, retain) IBOutlet UILabel * numSidesValueLabel;
#property (nonatomic, retain) IBOutlet UIButton * increaseButton;
#property (nonatomic, retain) IBOutlet UIButton * decreaseButton;
#property (nonatomic, retain) IBOutlet UIView * myCanvas;
#property (nonatomic, retain) IBOutlet Polygon * myPolygon;
- (IBAction) increase;
- (IBAction) decrease;
- (void) updateInterface;
#end
Now let's see the MainViewController.m:
#import "MainViewController.h"
#implementation MainViewController
#synthesize numSidesTextLabel, numSidesValueLabel, decreaseButton, increaseButton, myCanvas, myPolygon;
- (IBAction) decrease {
[myPolygon setNumberOfSides:([myPolygon numberOfSides]-1)];
[self updateInterface];
}
- (IBAction) increase {
[myPolygon setNumberOfSides:([myPolygon numberOfSides]+1)];
[self updateInterface];
}
- (void) updateInterface {
int sides = [myPolygon numberOfSides];
UIColor * myOceanColor = [UIColor colorWithRed:0.00 green:0.333 blue:0.557 alpha:1.00];
[increaseButton setTitleColor:myOceanColor forState:UIControlStateNormal];
[increaseButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateDisabled];
[decreaseButton setTitleColor:myOceanColor forState:UIControlStateNormal];
[decreaseButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateDisabled];
numSidesValueLabel.text = [NSString stringWithFormat:#"%d", sides];
decreaseButton.enabled = YES; increaseButton.enabled = YES;
if (sides == MAX_NUM_SIDES_CONSTANT) increaseButton.enabled = NO;
else if (sides == MIN_NUM_SIDES_CONSTANT) decreaseButton.enabled = NO;
[myCanvas updateState:sides withName:[myPolygon name]];
[myCanvas setNeedsDisplay];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Custom initialization
}
return self;
}
- (void)loadView {
self.view = [[UIView alloc] initWithFrame:CGRectMake(0.0, 20.0, 320.0, 460.0)];
self.view.backgroundColor = [UIColor whiteColor];
}
- (void)viewDidLoad {
CGRect tempFrame;
UIColor * oceanColor = [UIColor colorWithRed:0.000 green:0.333 blue:0.557 alpha:1.000];
// Draw the labels
tempFrame = CGRectMake(20.0, 20.0, 150.0, 20.0);
numSidesTextLabel = [[UILabel alloc] initWithFrame:tempFrame];
numSidesTextLabel.text = #"Number of sides:";
numSidesTextLabel.textAlignment = UITextAlignmentLeft;
[self.view addSubview:numSidesTextLabel];
[numSidesTextLabel release];
tempFrame = CGRectMake(160.0, 20.0, 40.0, 20.0);
numSidesValueLabel = [[UILabel alloc] initWithFrame:tempFrame];
numSidesValueLabel.text = #"-----";
numSidesValueLabel.textAlignment = UITextAlignmentLeft;
[self.view addSubview:numSidesValueLabel];
[numSidesValueLabel release];
tempFrame = CGRectMake(20.0, 125.0, 160.0, 20.0);
advancedOptionsLabel = [[UILabel alloc] initWithFrame:tempFrame];
advancedOptionsLabel.text = #"Advanced options";
advancedOptionsLabel.textAlignment = UITextAlignmentLeft;
[self.view addSubview:advancedOptionsLabel];
[advancedOptionsLabel release];
// Draw the advanced switch
tempFrame = CGRectMake(205.0, 120.0, 120.0, 60.0);
advancedOptionsSwitch = [[UISwitch alloc] initWithFrame:tempFrame];
[advancedOptionsSwitch setOn:NO animated:YES];
[self.view addSubview:advancedOptionsSwitch];
[advancedOptionsSwitch release];
// Decrease Button
decreaseButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; // autoreleased
[decreaseButton setTitle:#"Decrease" forState:UIControlStateNormal];
[decreaseButton setTitleColor:oceanColor forState:UIControlStateNormal];
[decreaseButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateDisabled];
decreaseButton.frame = CGRectMake(20.0, 60.0, 110.0, 40.0);
[decreaseButton addTarget:self action:#selector(decrease) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:decreaseButton];
// Increase Button
increaseButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; // autoreleased
[increaseButton setTitle:#"Increase" forState:UIControlStateNormal];
[increaseButton setTitleColor:oceanColor forState:UIControlStateNormal];
[increaseButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateDisabled];
increaseButton.frame = CGRectMake(190.0, 60.0, 110.0, 40.0);
[increaseButton addTarget:self action:#selector(increase) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:increaseButton];
// Initialize the polygon
myPolygon = [[Polygon alloc] init];
numSidesValueLabel.text = [NSString stringWithFormat:#"%d", [myPolygon numberOfSides]];
// Prepare the canvas
CGRect myCanvasFrame = CGRectMake(20.0, 160.0, 280.0, 280.0);
myCanvas = [[Canvas alloc] initWithFrame:myCanvasFrame withNumSides:[myPolygon numberOfSides] withPolygonName:[myPolygon name]];
[self.view addSubview:myCanvas];
[myCanvas release];
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
numSidesTextLabel = nil;
numSidesValueLabel = nil;
advancedOptionsLabel = nil;
myPolygon = nil;
myCanvas = nil;
[self.view release]; self.view = nil;
}
- (void)dealloc {
NSLog(#"(MainViewController): Dealloc is called");
[numSidesTextLabel removeFromSuperview];
[numSidesValueLabel removeFromSuperview];
[advancedOptionsLabel removeFromSuperview];
[myPolygon release];
[myCanvas removeFromSuperview]; // This should generate additional output
[super dealloc];
}
#end
Now let's have a look on the Canvas.h file:
#import <UIKit/UIKit.h>
#import "PrepDefs.h"
#interface Canvas : UIView {
IBOutlet UILabel * polygonNameLabel;
int currentStateNumSides;
CGPoint center;
}
#property (nonatomic, retain) UILabel * polygonNameLabel;
#property (nonatomic, assign) int currentStateNumSides;
#property (nonatomic, assign) CGPoint center;
+ (NSArray *) pointsForPolygonInRect:(CGRect) rect numberOfSides:(int) numberOfSides;
- (id) initWithFrame:(CGRect)frame withNumSides:(int)sides withPolygonName:(NSString *) name;
- (void) updateState:(int)sides withName:(NSString *)name;
#end
The Canvas.m file:
#import "Canvas.h"
#implementation Canvas
#synthesize polygonNameLabel, currentStateNumSides, center;
+ (NSArray *) pointsForPolygonInRect:(CGRect)rect numberOfSides:(int)numberOfSides {
CGPoint center = CGPointMake(rect.size.width / 2.0, rect.size.height / 2.0);
float radius = 0.9 * center.x;
NSMutableArray *result = [NSMutableArray array];
float angle = (2.0 * M_PI) / numberOfSides;
float exteriorAngle = M_PI - angle;
float rotationDelta = angle - (0.5 * exteriorAngle);
for (int currentAngle = 0; currentAngle < numberOfSides; currentAngle++) {
float newAngle = (angle * currentAngle) - rotationDelta;
float curX = cos(newAngle) * radius;
float curY = sin(newAngle) * radius;
[result addObject:[NSValue valueWithCGPoint:CGPointMake(center.x + curX, center.y + curY)]];
}
return result;
}
- (id) initWithFrame:(CGRect)frame withNumSides:(int)sides withPolygonName:(NSString *)name {
if (self = [super initWithFrame:frame]) {
// Initialization code
[self setBackgroundColor:[UIColor brownColor]];
currentStateNumSides = sides;
center = CGPointMake ([self bounds].size.width / 2.0, [self bounds].size.height / 2.0);
polygonNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0, center.y - 10.0, [self bounds].size.width, 20.0)];
polygonNameLabel.text = name;
polygonNameLabel.textAlignment = UITextAlignmentCenter;
polygonNameLabel.backgroundColor = [UIColor clearColor];
[self addSubview:polygonNameLabel];
[polygonNameLabel release];
}
return self;
}
- (id)initWithFrame:(CGRect)frame {
NSLog(#"(Canvas, -initWithFrame): You should always use initWithFrame:withNumSides:withPolygonName:");
return [self initWithFrame:frame withNumSides:DEFAULT_NUM_SIDES withPolygonName:DEFAULT_POLYGON_NAME];
}
- (void)drawRect:(CGRect)rect {
// Drawing code
NSArray * points = [Canvas pointsForPolygonInRect:[self bounds] numberOfSides:[self currentStateNumSides]];
CGContextRef currentContext = UIGraphicsGetCurrentContext();
[[UIColor blackColor] set];
UIRectFrame ([self bounds]);
CGContextBeginPath (currentContext);
for (int i = 0; i < [points count]; i++) {
CGPoint currentPoint;
NSValue * currentValue;
currentValue = [points objectAtIndex:i];
currentPoint = [currentValue CGPointValue];
//NSLog(#"(%.1f, %.1f)", currentPoint.x, currentPoint.y);
if (i == 0)
CGContextMoveToPoint(currentContext, currentPoint.x, currentPoint.y);
else
CGContextAddLineToPoint(currentContext, currentPoint.x, currentPoint.y);
}
CGContextClosePath(currentContext);
[[UIColor yellowColor] setFill];
[[UIColor blackColor] setStroke];
CGContextDrawPath(currentContext, kCGPathFillStroke);
}
- (void) updateState:(int)sides withName:(NSString *)name {
currentStateNumSides = sides;
polygonNameLabel.text = name;
}
- (void)dealloc {
NSLog(#"(Canvas): Calling dealloc");
[polygonNameLabel removeFromSuperview]; polygonNameLabel = nil;
[super dealloc];
}
#end
The PrepDefs.h file:
#ifndef __PREP_DEFS_H__
#define __PREP_DEFS_H__
#define MIN_NUM_SIDES_CONSTANT 3
#define DEFAULT_NUM_SIDES 5
#define MAX_NUM_SIDES_CONSTANT 12
#define DEFAULT_POLYGON_NAME #"Pentagon"
#endif
Finally the files for the Polygon class. The Polygon.h file:
#import <Foundation/Foundation.h>
#import "PrepDefs.h"
#interface Polygon : NSObject {
int numberOfSides;
int minimumNumberOfSides;
int maximumNumberOfSides;
}
#property int numberOfSides;
#property int minimumNumberOfSides;
#property int maximumNumberOfSides;
#property (readonly) float angleInDegrees;
#property (readonly) float angleInRadians;
#property (readonly) NSString * name;
- (void) setNumberOfSides:(int)numSides;
- (void) setMinimumNumberOfSides:(int)minNumSides;
- (void) setMaximumNumberOfSides:(int)maxNumSides;
- (id) initWithNumberOfSides:(int)sides minimumNumberOfSides:(int)min maximumNumberOfSides:(int)max;
#end
The Polygon.m file:
#import "Polygon.h"
#implementation Polygon
#synthesize numberOfSides, minimumNumberOfSides, maximumNumberOfSides, angleInDegrees, angleInRadians, name;
- (void) setNumberOfSides:(int)numSides {
if ((numSides >= [self minimumNumberOfSides]) && (numSides <= [self maximumNumberOfSides])) numberOfSides = numSides;
else NSLog(#"PolygonShape [setNumberOfSides]: Assignment out of bounds");
}
- (void) setMinimumNumberOfSides:(int)minNumSides {
if (minNumSides >= MIN_NUM_SIDES_CONSTANT) minimumNumberOfSides = minNumSides;
else NSLog(#"PolygonShape [setMinimumNumberOfSides]: Assignment out of bounds");
}
- (void) setMaximumNumberOfSides:(int)maxNumSides {
if (maxNumSides <= MAX_NUM_SIDES_CONSTANT) maximumNumberOfSides = maxNumSides;
else NSLog(#"PolygonShape [setMaximumNumberOfSides]: Assignment out of bounds");
}
- (float) angleInDegrees {
int sides = [self numberOfSides];
return ((float) (180.0 * ((double) (sides - 2)) / ((double) sides)));
}
- (float) angleInRadians {
int sides = [self numberOfSides];
return ((double) M_PI) * ((double) (sides - 2.0)) / ((double) sides);
}
- (NSString *) name {
NSString * s;
switch ([self numberOfSides]) {
case 3: s = [NSString stringWithString:#"Triangle"]; break;
case 4: s = [NSString stringWithString:#"Square"]; break;
case 5: s = [NSString stringWithString:#"Pentagon"]; break;
case 6: s = [NSString stringWithString:#"Hexagon"]; break;
case 7: s = [NSString stringWithString:#"Heptagon"]; break;
case 8: s = [NSString stringWithString:#"Octagon"]; break;
case 9: s = [NSString stringWithString:#"Enneagon"]; break;
case 10: s = [NSString stringWithString:#"Decagon"]; break;
case 11: s = [NSString stringWithString:#"Hendecagon"]; break;
case 12: s = [NSString stringWithString:#"Dodecagon"]; break;
default: NSLog(#"PolygonShape [name]: I should never enter here."); s = nil; break;
}
return s;
}
- (id) init {
return [self initWithNumberOfSides:DEFAULT_NUM_SIDES minimumNumberOfSides:MIN_NUM_SIDES_CONSTANT maximumNumberOfSides:MAX_NUM_SIDES_CONSTANT];
}
- (id) initWithNumberOfSides:(int)sides minimumNumberOfSides:(int)min maximumNumberOfSides:(int)max {
if (self = [super init]) {
// Cautiously initialize everything to zero
numberOfSides = 0;
minimumNumberOfSides = 0;
maximumNumberOfSides = 0;
// Attempt the actual assignment
[self setMaximumNumberOfSides:max];
[self setMinimumNumberOfSides:min];
[self setNumberOfSides:sides];
}
return self;
}
- (NSString *) description {
return [NSString stringWithFormat:#"Hello I am a %d-sided polygon (aka a %#) with angles of %.3f degrees (%.6f radians)", [self numberOfSides], [self name], [self angleInDegrees], [self angleInRadians]];
}
- (void) dealloc {
NSLog(#"(Polygon): Dealloc is called");
[super dealloc];
}
#end
After all the above, I believe the questions are:
Why dealloc functions are not used? Where do I actually have leaks?
When I initialize my mainController, should I pass as parameter the name of the nib file that was created when I created the custom class for the mainController?
Should I initialize the mainController and its view differently?
Should I use awakeFromNib instead of loadView or viewDidLoad?
Other comments that you have regarding the entire approach? Such as good or bad programming practices for one-screen applications? It really is a small program, so, I believe this is not an open-ended question. If however you think that this is an open-ended question, feel free not to answer. This is probably a naive question by a rookie to some veterans.
I really appreciate all the time that you spend on this and I am looking forward to your feedback.
Can you help me please to fix the memory leak ?
#import <Foundation/Foundation.h>
#interface NavBar : NSObject
{
NSString* nav;
}
#property (nonatomic, retain) NSString* nav;
+ (NavBar *) sharedInstance;
#end
#import "NavBar.h"
#implementation NavBar
#synthesize nav;
-(void)dealloc
{
[nav release];
}
+(NavBar *)sharedInstance
{
static NavBar *myInstance = nil;
if (nil == myInstance) {
myInstance = [[[self class] alloc] init];
}
return myInstance;
}
#end
i have a leak here :
- (void) viewWillAppear:(BOOL)animated
{
[NavBar sharedInstance].nav = #"navBar.png";
self.navigationController.navigationBar.barStyle = UIBarStyleDefault;
self.navigationController.navigationBar.tintColor = [UIColor clearColor];
self.navigationController.navigationBar.barStyle = UIBarStyleDefault;
self.navigationController.navigationBar.tintColor = [UIColor colorWithRed:19/255.0 green:140/255.0 blue:130/255.0 alpha:1.0];
[super viewWillAppear:animated];
}
in the delegate of my app:
#interface UINavigationBar (CustomBackground)
- (void)drawRectCustomBackground:(CGRect)rect;
#end
#implementation UINavigationBar (CustomBackground)
- (void)drawRectCustomBackground:(CGRect)rect
{
if (self.barStyle == UIBarStyleDefault)
{
UIImage *image = [UIImage imageNamed:[NavBarStyles sharedInstance].navStyle];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
return;
}
[self drawRectCustomBackground:rect];
}
At some point, you need to call
[[NavBar sharedInstance] release];
or an equivalent. Probably right before you exit.
I am using an NSMutableArray defined in a CCLayer subclass as follows:
//
// GameOverScene.h
// Cocos2DSimpleGame
//
// Created by Ray Wenderlich on 2/10/10.
// Copyright 2010 Ray Wenderlich. All rights reserved.
//
#import "cocos2d.h"
#interface scoLayer : CCColorLayer {
CCLabelTTF *_label;
CCLabelTTF *_howtoplay;
NSMutableArray *highscores;
}
#property (nonatomic, retain) CCLabelTTF *label;
#property (nonatomic, retain) NSMutableArray *highscores;
#property (nonatomic, retain) CCLabelTTF *back;
+ (id)initWithScore:(int)lastScore;
+(void)print_label:(int)lb;
+(void)menu;
#end
#interface sco : CCScene {
scoLayer *_layer;
}
#property (nonatomic, retain) scoLayer *layer;
#end
Here is the .m file for the class:
#implementation sco
#synthesize layer = _layer;
- (id)init {
if ((self = [super init])) {
self.layer = [scoLayer node];
[self addChild:_layer];
}
return self;
}
- (void)dealloc {
[_layer release];
_layer = nil;
[super dealloc];
}
#end
#implementation scoLayer
#synthesize label = _label;
#synthesize highscores ;
//#synthesize how_to_play ;
#synthesize back;
-(id) init
{
if( (self=[super initWithColor:ccc4(255,255,255,255)] )) {
CGSize winSize = [[CCDirector sharedDirector] winSize];
self.label = [CCLabelTTF labelWithString:#"" fontName:#"Arial" fontSize:16];
//self.how_to_play = [CCLabelTTF labelWithString:#"" fontName:#"Arial" fontSize:32];
self.highscores = [[NSMutableArray alloc] initWithObjects:nil ];
// [highscores addObject:#"asdf"];
// NSLog(#"hig %#", [highscores objectAtIndex:0]);
_label.color = ccc3(0,0,0);
_label.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:_label z:100];
[self runAction:[CCSequence actions:
[CCDelayTime actionWithDuration:3],
[CCCallFunc actionWithTarget:self selector:#selector(gameOverDone)],
nil]];
CCSprite *bk1 =[CCSprite spriteWithFile:#"bg3.png" ];//rect: CGRectMake(0, 0, 40, 480)];
[bk1 setPosition:ccp(160, 239)];
[self addChild:bk1 z:0];
CCMenuItem *back = [CCMenuItemImage
itemFromNormalImage:#"gameBackButton.png" selectedImage:#"gameBackButton.png"
target:self selector:#selector(back_game:)];
CCMenu *menu1 = [CCMenu menuWithItems:back,nil];
menu1.position = ccp(70, 100);
[menu1 alignItemsVerticallyWithPadding: 40.0f];
// [self addChild:menu z: 2];
[self addChild:menu1 z: 0];
}
return self;
}
In this function:
+(id)initWithScore:(int)lastScore
{
NSLog(#"score %d", lastScore);
//NSMutableArray *highscores = [[NSMutableArray alloc] initWithObjects:nil ];
//[highscores addObject:#"asdf"];
if([highscores count] == 0)
}
I want to use the highscores array and insert the data (lastScore), but when I do this the application exits with an EXC_BAD_ACCESS signal. How can I fix this error?
The + before the method declaration indicates, that this is a class method. So you have no access to instance variables.
I think this is more what you want:
-(id)initWithScore:(int)lastScore
{
NSLog(#"score %d", lastScore);
if(!self = [super init])
return nil;
highscores = [[NSMutableArray alloc] init];
[highscores addObject:lastScore];
return self;
}
You're on the right track, but unless you have many different highscore tables you don't need to make it a separate class, as you've tried to do now. (I.e. 'create many highscore table instances with a class'.) You can of course use a class and create an object of class highscoretable at [[alloc] init] (as yan kun shows), but if all you need is a mutable array to add highscores to in your game, just allocate one in the app delegate, or use other methods for variables shared between viewcontrollers, I think.
Ey there, so as the title says, I am having a tough time adding an MKPolygon as an overlay to an MKMapView. Here is the relevant code:
ParkingMapViewContoller.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface ParkingMapViewController : UIViewController <MKMapViewDelegate> {
MKMapView *mapView;
}
#property (nonatomic, retain) IBOutlet MKMapView *mapView;
-(void)loadAnnotations;
-(void)showCurrentLocationButtonTapped:(id)sender;
#end
ParkingMapViewController.m
//...
#import "ParkingRegionOverlay.h"
//...
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"%#",self.title);
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:100 target:self action:#selector(showCurrentLocationButtonTapped:)];
/*MKMapPoint points[3] = {{38.53607,-121.765793}, {38.537606,-121.768379}, {38.53487,-121.770578}};
MKPolygon *polygon = [MKPolygon polygonWithPoints:points count:3];*/
ParkingRegionOverlay *polygon = [[ParkingRegionOverlay alloc] initialize];
[mapView addOverlay:polygon];
[self loadAnnotations];
CLLocationCoordinate2D centerCoord = { UCD_LATITUDE, UCD_LONGITUDE };
[mapView setCenterCoordinate:centerCoord zoomLevel:13 animated:NO]; //from "MKMapView+ZoomLevel.h"
}
//...
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
NSLog(#"in viewForOverlay!");
if ([overlay isKindOfClass:[MKPolygon class]])
{
MKPolygonView* aView = [[[MKPolygonView alloc] initWithPolygon:(MKPolygon*)overlay] autorelease];
aView.fillColor = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
aView.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.7];
aView.lineWidth = 3;
return aView;
}
return nil;
}
//...
ParkingRegionOverlay.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface ParkingRegionOverlay : NSObject <MKOverlay> {
//CLLocationCoordinate2D origin;
MKPolygon *polygon;
//MKMapRect rect;
}
//#property (nonatomic) CLLocationCoordinate2D origin;
#property (nonatomic, retain) MKPolygon *polygon;
//#property (nonatomic) MKMapRect rect;
-(ParkingRegionOverlay*)initialize;
-(MKMapRect)boundingMapRect;
-(CLLocationCoordinate2D)coordinate;
#end
ParkingRegionOverlay.m
#import "ParkingRegionOverlay.h"
#implementation ParkingRegionOverlay
//#synthesize origin;
#synthesize polygon;
//#synthesize rect;
-(ParkingRegionOverlay*) initialize {
MKMapPoint points[3] = {{38.53607,-121.765793}, {38.537606,-121.768379}, {38.53487,-121.770578}};
polygon = [MKPolygon polygonWithPoints:points count:3];
polygon.title = #"Some Polygon";
return self;
}
- (MKMapRect)boundingMapRect{
MKMapRect bounds = MKMapRectMake(-121.770578,38.537606,-121.770578-(-121.765793),38.537606-38.53487);
return bounds;
}
- (CLLocationCoordinate2D)coordinate{
return CLLocationCoordinate2DMake((38.537606-38.53487)/2, (-121.770578-(-121.765793))/2);
}
#end
You see that NSLog I stuck in the viewForOverlay: method? Well that never shows up in the console, so that function is never called. Any idea of what's wrong? Many thanks!
The main issue is that the code is giving the map view latitude/longitude coordinates where it expects MKMapPoints. For an explanation of the difference, see "Understanding Map Geometry" in the Location Awareness Programming Guide. Use the MKMapPointForCoordinate function to convert from lat/long coordinates to an MKMapPoint.
The second issue is that in viewForOverlay, it is checking if overlay is of type MKPolygon. Your overlay class ParkingRegionOverlay contains an MKPolygon object inside it but is not itself of type MKPolygon.
To fix the main issue, you need to change the initialize and boundingMapRect methods:
-(id)init {
if (self = [super init]) {
MKMapPoint points[3];
CLLocationCoordinate2D c1 = {38.53607,-121.765793};
points[0] = MKMapPointForCoordinate(c1);
CLLocationCoordinate2D c2 = {38.537606,-121.768379};
points[1] = MKMapPointForCoordinate(c2);
CLLocationCoordinate2D c3 = {38.53487,-121.770578};
points[2] = MKMapPointForCoordinate(c3);
polygon = [MKPolygon polygonWithPoints:points count:3];
polygon.title = #"Some Polygon";
}
return self;
}
- (MKMapRect)boundingMapRect{
CLLocationCoordinate2D corner1 =
CLLocationCoordinate2DMake(38.537606, -121.770578);
MKMapPoint mp1 = MKMapPointForCoordinate(corner1);
CLLocationCoordinate2D corner2 =
CLLocationCoordinate2DMake(38.53487, -121.765793);
MKMapPoint mp2 = MKMapPointForCoordinate(corner2);
MKMapRect bounds =
MKMapRectMake(mp1.x, mp1.y, (mp2.x-mp1.x), (mp2.y-mp1.y));
return bounds;
}
Please notice by the way that I changed the method "initialize" to "init". Though it wasn't preventing the polygon from showing, the way you are overriding the initialization of ParkingRegionOverlay using a method called "initialize" and not calling [super init] does not follow convention. (Also remove "initialize" from the .h file.)
To fix the second issue, the viewForOverlay method should look like this:
- (MKOverlayView *)mapView:(MKMapView *)mapView
viewForOverlay:(id <MKOverlay>)overlay
{
NSLog(#"in viewForOverlay!");
if ([overlay isKindOfClass:[ParkingRegionOverlay class]])
//^^^^^^^^^^^^^^^^^^^^
{
//get the MKPolygon inside the ParkingRegionOverlay...
MKPolygon *proPolygon = ((ParkingRegionOverlay*)overlay).polygon;
MKPolygonView *aView = [[[MKPolygonView alloc]
initWithPolygon:proPolygon] autorelease];
//^^^^^^^^^^
aView.fillColor = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
aView.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.7];
aView.lineWidth = 3;
return aView;
}
return nil;
}
Finally, change the code in viewDidLoad:
ParkingRegionOverlay *polygon = [[ParkingRegionOverlay alloc] init];
[mapView addOverlay:polygon];
[polygon release]; //don't forget this
it seems to me that you forget to set your delegate
somthing like that
_mapView.delegate = self;
Have you set this?:
yourMap.delegate=self;
And what's your target? 3.x or 4.x?? I think MKPolygon is available from 4.0.
You can try to add the import clause to your prefix file like:
#ifdef __OBJC__
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#endif