私はiPhoneプログラミングのもう一人の新人であり、答えられない非常に基本的な質問があります。
まず、xcode 3.1.4でコードを書いているので、古いプラットフォームで基本を学ぶことができ、近い将来、ポータブルアプリを作成できるようになることを願っています(iPhone 3をサポートするという意味で)同じように)。残念ながら、私が理解していることから、Appleはxcode 3.1.4に付属のドキュメントを削除しました。現在のドキュメントと例では、iOS 5により関連性の高いルートが提案されているため、これにより、正しい方法を推測することが困難になります(例:ストーリーボード)。したがって、MVCの概念全体を理解しているように見えても、最初の実際のおもちゃアプリは実際に機能していますが、関連するdeallocメソッドが使用されていないため、メモリを正しく管理していません(私が持っている関連するNSLogは表示されていません) )。
詳細に今。シングルウィンドウアプリ(ポリゴン、割り当て3)を作成したいと思います。インターフェイスビルダーについては忘れましょう。すべてのビュー/サブビューはプログラムで作成されます。そのために、カスタムUIViewControllerを作成します。また、提示したい単一のビューのカスタムサブビューを記述するために、カスタムUIViewを作成します。(私たちはすでにここでの私のアプローチに同意しない可能性が非常に高いですが、今のところ私がここで試みていることは概念的に正しいと真剣に信じています)。アプリデリゲート(.h)で、mainControllerを宣言します。これが、実際に宣言するすべてです。ファイルは次のとおりです。
#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
実行時に、mainControllerに、ユーザーに表示されるメインビューに必要なメモリを割り当て、カスタムUIViewのサブビューを追加する必要があります(これはポリゴンを描画するためのキャンバスになります)。したがって、基本的には、ウィンドウをコンテナとして扱い、mainControllerのビューを使用して、メインビューを実際にユーザーに表示します。そのビューには、カスタムキャンバスのインスタンス(カスタムビュー)を含むサブビューも添付します。以下はAppDelegate.mファイルです。
#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
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
次に、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
それでは、Canvas.hファイルを見てみましょう。
#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
Canvas.mファイル:
#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
PrepDefs.hファイル:
#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
最後に、Polygonクラスのファイル。Polygon.hファイル:
#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
Polygon.mファイル:
#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
上記のすべての後、私は質問が次のとおりであると信じています:
- なぜdealloc関数が使用されないのですか?実際にどこに漏れがありますか?
- mainControllerを初期化するときに、mainControllerのカスタムクラスを作成したときに作成されたnibファイルの名前をパラメーターとして渡す必要がありますか?
- mainControllerとそのビューを別の方法で初期化する必要がありますか?
- loadViewまたはviewDidLoadの代わりにawakeFromNibを使用する必要がありますか?
- アプローチ全体に関して他にコメントはありますか?1画面アプリケーションのプログラミング手法の良し悪しなど。本当に小さなプログラムなので、これは自由形式の質問ではないと思います。ただし、これが自由形式の質問だと思われる場合は、お気軽に回答しないでください。これはおそらく、新人からベテランへの素朴な質問です。
これにご尽力いただき、誠にありがとうございます。フィードバックをお待ちしております。