如何使用单个窗口将mainController和自定义视图分配给应用程序?

时间:2012-04-25 19:39:28

标签: iphone xcode uiview uiviewcontroller polygon

我是iPhone编程的另一位新手,我有一个非常基本的问题,我无法回答。

首先,我在xcode 3.1.4中编写代码,以便我可以在旧平台上学习基础知识,希望在不久的将来,这将允许我创建便携式应用程序(从某种意义上说,我将成为支持iPhone 3)。不幸的是,根据我的理解,苹果已经删除了xcode 3.1.4附带的文档,这使得很难猜出什么是正确的做事方式,因为当前的文档和示例提示了与iOS 5更相关的路由(例如故事板)。所以,即使我似乎理解MVC的整个概念,虽然我的第一个真正的玩具应用程序实际上正在运行但它肯定没有正确管理内存,因为没有使用相关的dealloc方法(我没有显示相关的NSLogs) )。对于学习过程,我正在关注2010年冬季CS193P在线的课程资料。

现在具体细节。 我想创建一个单窗口应用程序(Polygon,Assignment 3)。让我们忘记界面构建器;将以编程方式创建所有视图/子视图。为此,我创建了一个自定义UIViewController。我还创建了一个自定义UIView,以描述我想要呈现的单个视图的自定义子视图。 (我们很可能已经不同意我的方法,但是现在我认真地相信我在这里尝试的是概念上正确的)。在app delegate(.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

完成上述所有操作后,我相信问题是:

  1. 为什么不使用dealloc功能?我在哪里真的有泄漏?
  2. 当我初始化mainController时,我应该将参数作为参数传递给我在为mainController创建自定义类时创建的nib文件的名称吗?
  3. 我应该以不同方式初始化mainController及其视图吗?
  4. 我应该使用awakeFromNib而不是loadView或viewDidLoad吗?
  5. 您对整个方法的其他评论?例如单屏应用程序的编程实践的好坏?这真的是一个小程序,所以,我认为这不是一个开放式的问题。如果您认为这是一个开放式问题,请随意不回答。对于一些老手来说,这可能是一个天真的问题。
  6. 我非常感谢你花在这上面,我很期待你的反馈。

0 个答案:

没有答案