Don't understand why I'm receiving EXC_BAD_ACCESS in xCode - objective-c

I don't understand why I'm getting a EXC BAD ACCESS error when I call the mineHit method in my .m file. I understand that it's indicating that the button array has been released, but I don't understand why It would have been released at all.
#import "basicsViewController.h"
#implementation basicsViewController
#synthesize resetGame;
#synthesize scoreLabel;
#synthesize timeLabel;
#synthesize time;
#synthesize score;
-(void)newGame{
int index=0;
int yAxis=70;
for(int y=0;y<100;y=y+10){
int xAxis=20;
for( int x = 1; x < 11; x++) {
buttonArray[index] = [[UIButton alloc]init];
buttonArray[index] = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonArray[index] setTag:index];
[buttonArray[index] addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
buttonArray[index].frame = CGRectMake(xAxis, yAxis, 26.0, 26.0);
NSLog(#"tag:%d xAxis:%d yAxis:%d",buttonArray[index].tag,(int)buttonArray[index].frame.origin.x,(int)buttonArray[index].frame.origin.y);
[self.view addSubview:buttonArray[index]];
xAxis=xAxis+28;
index=x+y;
}
yAxis=yAxis+28;
}
//generate bombs
for (int bombs=0;bombs<10;bombs++){
bombArray[bombs]= (arc4random()%99);
//TODO compare against bombArray to make sure of no duplicates
NSLog(#"BOMB AT %d",bombArray[bombs]);
}
}
- (IBAction)resetPress:(id)sender {
[self newGame];
}
- (void)buttonClicked:(UIButton*)button
{
BOOL hit;
NSLog(#"SELECTED BUTTON:%d",button.tag);
for (int b=0;b<10;b++){
if (button.tag==bombArray[b]){
//BOMB HIT
hit=YES;
b=10;
}
else {
//no bomb
hit=NO;
}
}
if (hit==YES){
//if hit
NSLog(#"HIT AT %d",button.tag);
[self mineHit];
}
else {
//if not hit
NSLog(#"%d is clean",button.tag);
[self cleanHit:button];
}
}
-(void)mineHit{
for (int d=0;d<100;d++){
NSLog(#"%i",buttonArray[d].tag);
buttonArray[d].enabled=NO;
[buttonArray[d] setTitle:#"*" forState:UIControlStateDisabled];
}
}
-(void)cleanHit:(UIButton*)button{
button.enabled=NO;
[button setTitle:#"!" forState:UIControlStateDisabled];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self newGame];
}
- (void)viewDidUnload
{
[self setResetGame:nil];
[self setScoreLabel:nil];
[self setTimeLabel:nil];
[super viewDidUnload];
}
#end
Here is my .h file
#import <UIKit/UIKit.h>
NSInteger bombArray[];
UIButton *buttonArray[];
#interface basicsViewController : UIViewController
#property (weak, nonatomic) IBOutlet UIButton *resetGame;
#property (weak, nonatomic) IBOutlet UILabel *scoreLabel;
#property (weak, nonatomic) IBOutlet UILabel *timeLabel;
#property int time;
#property int score;
-(void)newGame;
-(void)buttonClicked:(UIButton*)button;
-(void)mineHit;
-(void)cleanHit:(UIButton*)button;
#end

When I compile your code I get four warnings. All four warnings are the same and say:
Tentative array definition assumed to have one element
The warnings apply to the definition of your bombArray and buttonArray arrays in the interface (.h) file.
If we give the two arrays a size your -mineHit method works fine.
Change the beginning of your .h file to:
#import <UIKit/UIKit.h>
NSInteger bombArray[10];
UIButton *buttonArray[100];
#interface basicsViewController : UIViewController
The compiler generates warnings for a reason and it is a good idea to try and get your code to compile cleanly with no warnings or errors.
Update: While we are here there is no reason why you couldn't move these arrays inside the interface and declare them as instance variables. Doing this would mean that the arrays are associated with an individual instance of the view controller. It is unlikely that you'd have multiple instances of this view controller but it is better to do it correctly now than get bitten later.
#import <UIKit/UIKit.h>
#interface basicsViewController : UIViewController {
NSInteger bombArray[10];
UIButton *buttonArray[100];
}
Interestingly, moving the declaration into the interface turns the warnings into errors.

Related

Why is my code only working the second time around? Delegate issue?

In Objective-C, I am trying to make a NSTextField, when clicked, open a sheet with a NSDatePicker that slides out under the text field. You select a date which closes the sheet and populates the NSTextField with the date chosen.
I have found this article on how to use a protocol to do this in Swift. http://www.knowstack.com/swift-nsdatepicker-sample-code/#comment-20440
But when I convert it to Objective-C I have a few issues.
The first time I click my button to trigger the sheet, the sheet appears in the middle of the screen, ignoring the event:
-(NSRect)window:(NSWindow *)window willPositionSheet:(NSWindow *)sheet usingRect:(NSRect)rect {
When I select a date, the textfield in the main xib is updated with the selection so the protocol part is working but the sheet remains unresponsive on screen.
If I click the button a second time, the unresponsive sheet closes and reappears under the NSTextField and dismisses itself when I choose a date. This is the expected behaviour.
My question is, why does this not work the first time I click the button but only works the second time?
Here is the code:
#import <Cocoa/Cocoa.h>
#protocol DatePickerProtocol
#required
-(void) selectedDate:(NSDate *)date;
#optional
#end
#interface datePickerWindowController : NSWindowController {
id delegate;
}
-(void)setDelegate:(id)newDelegate;
#end
#import "datePickerWindowController.h"
#interface datePickerWindowController ()
#property (weak) IBOutlet NSDatePicker *datePicker;
#end
#implementation datePickerWindowController
- (void)windowDidLoad {
[super windowDidLoad];
self.datePicker.dateValue = [NSDate date];
}
-(void)setDelegate:(id)newDelegate {
delegate = newDelegate;
NSLog(#"delegate has been set in datePickerWindowController");
}
- (IBAction)selectDate:(NSDatePicker *)sender {
[delegate selectedDate:self.datePicker.dateValue];
[self.window close];
}
#end
#import <Cocoa/Cocoa.h>
#import "datePickerWindowController.h"
#interface AppDelegate : NSObject <NSApplicationDelegate, DatePickerProtocol, NSWindowDelegate>
#end
#import "AppDelegate.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#property (weak) IBOutlet NSDatePicker *timePicker;
#property (weak) IBOutlet NSTextField *textDate;
#property (retain) datePickerWindowController * myDatePickerWindowController;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
self.window.delegate = self;
[self.window setDelegate:self];
self.textDate.stringValue = [NSString stringWithFormat:#"%#",[NSDate date]];
datePickerWindowController * windowController = [[datePickerWindowController alloc] initWithWindowNibName:#"datePickerWindowController"];
self.myDatePickerWindowController = windowController;
self.myDatePickerWindowController.delegate = self;
[self.myDatePickerWindowController setDelegate:self];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
}
-(void)selectedDate:(NSDate *)date {
self.textDate.stringValue = [NSString stringWithFormat:#"%#", date];
}
- (IBAction)pickDateButton:(NSButton *)sender {
[self.window beginSheet:self.myDatePickerWindowController.window completionHandler:nil];
}
// Position sheet under text field
-(NSRect)window:(NSWindow *)window willPositionSheet:(NSWindow *)sheet usingRect:(NSRect)rect {
if (sheet == self.myDatePickerWindowController.window) {
NSRect r = self.textDate.frame;
r.origin.y = r.origin.y + 5;
return r;
} else {
return rect;
}
}
#end
I am assuming I have the delegate messed up somehow. Maybe in the xib or the code. I can not see why it works a second time though. Is this due to retain or how I am keeping the DatePicker around.
Many thanks for any help.

DrawRect Square/Rectangle user values

I've got the books, I've checked the apple docs and something is not clicking.
Here's what I want to do: get user values and draw a square/rectangle with them.
A window with one custom view on the right.
On the left two text fields, two labels (width and height) and a button "Draw".
On the right DrawView.
There's how one could do it:
You have to do the connection for the textFields and the button in your appDelegate and also have an IBOutlet for your customView.
In your customView you need to have the two variables which will hold the values from the textField. Don't use property/synthesize because once you set the values you need to call setNeedsDisplay in your customView-DrawView.
AppDelegate.h
#import <Cocoa/Cocoa.h>
#import "DrawView.h"
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (assign) IBOutlet NSWindow *window;
#property (weak) IBOutlet DrawView *myDrawView;
#property (weak) IBOutlet NSTextField *widthTextField;
#property (weak) IBOutlet NSTextField *heightTextField;
- (IBAction)draw:(id)sender;
#end
AppDelegate.m
#import "AppDelegate.h"
#import "DrawView.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
}
- (IBAction)draw:(id)sender {
[_myDrawView setWidth:[_widthTextField floatValue]];
[_myDrawView setHeight:[_heightTextField floatValue]];
}
DrawView.h
#import <Cocoa/Cocoa.h>
#interface DrawView : NSView {
float width;
float height;
}
-(void) setWidth: (float) aWidth;
-(void) setHeight: (float) aHeight;
#end
DrawView.m
#import "DrawView.h"
#implementation DrawView
#pragma mark - Setters
-(void) setWidht:(float)aWidth {
width = aWidth;
[self setNeedsDisplay:YES];
}
-(void) setHeight:(float)aHeight {
height = aHeight;
[self setNeedsDisplay:YES];
}
#pragma mark - Drawing
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setWidth:10];
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect {
//BackGround Color
[[NSColor whiteColor] setFill];
NSRectFill(dirtyRect);
[super drawRect:dirtyRect];
NSBezierPath* square = [NSBezierPath bezierPath];
[square setLineWidth:2];
NSRect squareRect = NSMakeRect(10,10,width,height);
[square appendBezierPathWithRoundedRect:squareRect xRadius:5 yRadius:5];
[square stroke];
}
#end
That's it. I don't know if there's a better way to do it. It seems a bit bizarre.
Yes it´s nill in drawRect but just before it reaches drawRect it's not because I can calculate the area with the same values...Could you please try it. It takes only 5' to do it.

how do I push a view controller from a ViewCell in a UICollectionView

I'm trying to use a UICollectionView and it's viewCell. I have that going fine. But on the viewcell form I have a button that I intend to have go to a settings view. I'm trying to "push it onto the stack". Xcode complains of "no visible interface for the selector". Some code and the error message are below.
// TryColViewCell.h
#import <UIKit/UIKit.h>
#import "TryColViewContViewController.h"
#interface TryColViewCell : UICollectionViewCell
#property (weak, nonatomic) IBOutlet UILabel *outletNameLBL;
#property (weak, nonatomic) IBOutlet UILabel *outletCellLabel;
#property (readwrite) NSInteger intTest;
#property (readwrite) TryColViewContViewController *theHost;
- (IBAction)actionPlus1:(id)sender;
- (IBAction)actionMinus1:(id)sender;
- (IBAction)actionNameEdit:(id)sender;
// TryColViewCell.m
#import "TryColViewCell.h"
#import "SettingsViewController.h"
#implementation TryColViewCell {
#public int intCellNumber; //TODO: get this to track the row/cell #
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
- (IBAction)actionPlus1:(id)sender {
}
- (IBAction)actionMinus1:(id)sender {
}
- (IBAction)actionNameEdit:(id)sender {
NSLog(#"Name Edit");
SettingsViewController *viewControllerB =
[[SettingsViewController alloc] initWithNibName:#"SettingsViewController" bundle:nil];
viewControllerB.propName = _outletNameLBL.text;
[self pushViewController:viewControllerB animated:YES];
[self.theHost pushViewController:viewControllerB animated:YES];
}
#end
// Error message on PushVIewController line
TryColViewCell.m:83:11: No visible #interface for 'TryColViewCell'
declares the selector 'pushViewController:animated:'
Push the new view controller from the view controller that contains the collection view, not the cell. Only a subclass of UIViewController has the method pushViewController:animated. to override.
In your table view controller, do something like this, assuming you are using storyboard:
-(UITableViewCell*)tableView:(UITableView*)tableView
cellForRowAtIndexPath:(NSIndexPath*)indexPath {
static NSString* identifier = #"Cell";
CellSubClass *cell =
(CellSubClass*) [tableView dequeueReusableCellWithIdentifier:identifier];
[cell.upButton addTarget:self selector:#selector(actionPlus1:)
forControlEvent:UIControlEventTouchUpInside];
[cell.downButton addTarget:self selector:#selector(actionMinus1:)
forControlEvent:UIControlEventTouchUpInside];
...
}

#protocol with integers

I am trying to make a protocol for a detail view for a tableView. The detail view has a question and then an answer. If I get the answer correct, it will set an integer to increase by 1 in a protocol method.
I am new to protocols and I don't understand what I am doing wrong.
Code
DetailViewController.h
Where the protocol is made
#import "Question.h"
#protocol DetailQuestionViewControllerDelegate <NSObject>
-(void)questionsCorrectHasChangedTo:(int)questionNumberChanged;
#end
#interface DetailQuestionViewController : UIViewController
#property (nonatomic, strong) Question *selectedQuestion;
#property (strong, nonatomic) IBOutlet UILabel *questionLabel;
#property (strong, nonatomic) IBOutlet UITextField *answerField;
#property (strong, nonatomic) IBOutlet UILabel *correctLabel;
#property (nonatomic,strong) id <DetailQuestionViewControllerDelegate> delegate;
#property (assign, nonatomic) int questionsCorrect;
DetailViewController.m
#implementation DetailQuestionViewController
#synthesize questionLabel;
#synthesize answerField;
#synthesize correctLabel;
#synthesize selectedQuestion;
#synthesize questionsCorrect;
#synthesize delegate;
- (void)viewDidLoad
{
[super viewDidLoad];
// Sets the questionLabel to the question we put in the array
self.questionLabel.text = [selectedQuestion questionName];
// Sets the navigation title to the rowName we put in the array
self.navigationItem.title = [selectedQuestion questionRowName];
NSLog(#"The question's answer for the question you selected is %#", [selectedQuestion questionAnswer]);
}
- (IBAction)checkAnswer:(UITextField *)sender
{
if ([[selectedQuestion questionAnswer] caseInsensitiveCompare:answerField.text] == NSOrderedSame)
{
// Show the correct label
[correctLabel setHidden:NO];
correctLabel.text = #"Correct!";
correctLabel.textColor = [UIColor greenColor];
*questionsCorrect = 1;
NSLog(#"questionsCorrect int is %d", questionsCorrect);
[self.delegate questionsCorrectHasChangedTo:questionsCorrect];*
}
else
{
// Show the incorrect label
[correctLabel setHidden:NO];
correctLabel.text = #"Incorrect";
correctLabel.textColor = [UIColor redColor];
}
// Erase the text in the answerField
answerField.text = #"";
}
ScoreViewController.h
Now here is my ScoreView which will be acsessing the delegate
#import <UIKit/UIKit.h>
#import "DetailQuestionViewController.h"
#interface ScoreViewController : UIViewController *<DetailQuestionViewControllerDelegate>*
#property (strong, nonatomic) IBOutlet UILabel *scoreLabel;
- (IBAction)resetButtonClicked:(UIButton *)sender;
-(void)checkScore;
#end
ScoreViewController.m
#import "ScoreViewController.h"
#import "DetailQuestionViewController.h"
#interface ScoreViewController ()
#end
#implementation ScoreViewController
#synthesize scoreLabel;
- (void)viewDidLoad
{
[super viewDidLoad];
*DetailQuestionViewController *dqvc = [[DetailQuestionViewController alloc] init];
dqvc.delegate = self;*
}
-(void)viewWillAppear:(BOOL)animated
{
[self checkScore];
}
-(void)checkScore
{
}
- (IBAction)resetButtonClicked:(UIButton *)sender
{
}
#pragma mark - DetailQuestionViewControllerDelegate -
*-(void)questionsCorrectHasChangedTo:(int)questionNumberChanged*
{
//set the textlabel text value to the number of questions correct
NSLog(#"questionsNumberChanged is %i", questionNumberChanged);
scoreLabel.text = [NSString stringWithFormat:#"You answered %d questions correctly",questionNumberChanged];
}
#end
The label is never updating for some reason.
Sorry for making the question so long, tried to be very specific.
I'm guessing somewhat here as you talk about increasing a value in a protocol method, yet you don't have a single + or ++ anywhere... You also have quite a few *'s sprinkled in strange places in your sample code, it is unclear whether these are typos, intended as emphasis, or intended to be pointer indirection.
So you have a property questionsCorrect in your DetailQuestionViewController class so let's assume this is the class you expect to own the counter (we'll skip that this is a view and not a model class...). If this is the idea then lines:
*questionsCorrect = 1;
NSLog(#"questionsCorrect int is %d", questionsCorrect);
[self.delegate questionsCorrectHasChangedTo:questionsCorrect];*
should be:
self.questionsCorrect++; // increment the counter
NSLog(#"questionsCorrect int is %d", self.questionsCorrect);
[self.delegate questionsCorrectHasChangedTo:self.questionsCorrect];
(you can also declare the instance variable questionsCorrect yourself and drop the use of self. above - whichever you prefer)
Now just go through and remove the other cases of extra *'s if they are in your code as well as the sample above and you'll be a bit closer to your goal.
If alternatively you wish ScoreViewController to own the counter then you need to declare it there and provide a method to increment and display it.
HTH

iOS: How do I know if a property is KVO-compliant?

In the Key-Value Observing Programming Guide, the section Registering for Key-Value Observing says "Typically properties in Apple-supplied frameworks are only KVO-compliant if they are documented as such." But, I haven't found any properties in the documentation that are documented as KVO-compliant. Would you please point me to some?
Specifically, I would like to know if the #property(nonatomic,retain) UIViewController *rootViewController of UIWindow is KVO-compliant. The reason is that I'm adding the rootViewController property to UIWindow for iOS < 4 and want to know if I should make it KVO-compliant.
#interface UIWindow (Additions)
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_4_0
#property (nonatomic, retain) UIViewController *rootViewController;
#endif;
#end
#implementation UIWindow (Additions)
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_4_0
#dynamic rootViewController;
- (void)setRootViewController:(UIViewController *)newRootViewController {
if (newRootViewController != _rootViewController) {
// Remove old views before adding the new one.
for (UIView *subview in [self subviews]) {
[subview removeFromSuperview];
}
[_rootViewController release];
_rootViewController = newRootViewController;
[_rootViewController retain];
[self addSubview:_rootViewController.view];
}
}
#endif
#end
Short answer: No.
Long answer: Nothing in UIKit is guaranteed to be KVO-compliant. If you happen to find that KVO-ing a property works, be grateful, it's unintentional. Also: be wary. It could very well break in the future.
If you find that this is something you need, please file an enhancement request.
About your actual code, it's inherently flawed. Do NOT attempt to add a "rootViewController" setter to UIWindow this way. It will break when you compile your code on iOS 4 but someone runs it on an iOS 5 device. Because you compiled using the 4.x SDK, the #if statements will evaluate to true, meaning your category method smasher will be included in the binary. However, when you run it on an iOS 5 device, you're now going to get a method conflict because two methods on UIWindow will have the same method signature, and there's no guarantee as to which one will be used.
Don't screw with the frameworks like this. If you have to have this, use a subclass. THIS IS WHY SUBCLASSING EXISTS.
Your subclass would look something like this:
#interface CustomWindow : UIWindow
#property (nonatomic, retain) UIViewController *rootViewController;
#end
#implementation CustomWindow : UIWindow
static BOOL UIWindowHasRootViewController = NO;
#dynamic rootViewController;
- (void)_findRootViewControllerMethod {
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
IMP uiwindowMethod = [UIWindow instanceMethodForSelector:#selector(setRootViewController:)];
IMP customWindowMethod = [CustomWindow instanceMethodForSelector:#selector(setRootViewController:)];
UIWindowHasRootViewController = (uiwindowMethod != NULL && uiwindowMethod != customWindowMethod);
});
}
- (UIViewController *)rootViewController {
[self _findRootViewControllerMethod];
if (UIWindowHasRootViewController) {
// this will be a compile error unless you forward declare the property
// i'll leave as an exercise to the reader ;)
return [super rootViewController];
}
// return the one here on your subclass
}
- (void)setRootViewController:(UIViewController *)rootViewController {
[self _findRootViewControllerMethod];
if (UIWindowHasRootViewController) {
// this will be a compile error unless you forward declare the property
// i'll leave as an exercise to the reader ;)
[super setRootViewController:rootViewController];
} else {
// set the one here on your subclass
}
}
Caveat Implementor: I typed this in a browser window
Based on #David DeLong's solution, this is what I came up with, and it works beautifully.
Basically, I made a category on UIWindow. And in +load, I (run-time) check whether [UIWindow instancesRespondToSelector:#selector(rootViewController)]. If not, I use class_addMethod() to dynamically add the getter & setter methods for rootViewController. Also, I use objc_getAssociatedObject and objc_setAssociatedObject to get & set the rootViewController as an instance variable of UIWindow.
// UIWindow+Additions.h
#interface UIWindow (Additions)
#end
// UIWindow+Additions.m
#import "UIWindow+Additions.h"
#include <objc/runtime.h>
#implementation UIWindow (Additions)
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_4_0
// Add rootViewController getter & setter.
static UIViewController *rootViewControllerKey;
UIViewController *rootViewController3(id self, SEL _cmd);
void setRootViewController3(id self, SEL _cmd, UIViewController *newRootViewController);
UIViewController *rootViewController3(id self, SEL _cmd) {
return (UIViewController *)objc_getAssociatedObject(self, &rootViewControllerKey);
}
void setRootViewController3(id self, SEL _cmd, UIViewController *newRootViewController) {
UIViewController *rootViewController = [self performSelector:#selector(rootViewController)];
if (newRootViewController != rootViewController) {
// Remove old views before adding the new one.
for (UIView *subview in [self subviews]) {
[subview removeFromSuperview];
}
objc_setAssociatedObject(self, &rootViewControllerKey, newRootViewController,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self addSubview:newRootViewController.view];
}
}
+ (void)load {
if (![UIWindow instancesRespondToSelector:#selector(rootViewController)]) {
class_addMethod([self class], #selector(rootViewController),
(IMP)rootViewController3, "##:");
class_addMethod([self class], #selector(setRootViewController:),
(IMP)setRootViewController3, "v#:#");
}
}
#endif
#end
Here's a solution using Associative References to define an instance variable with a category. But, it doesn't work cause, according to #Dave DeLong, I must use a run-time (not compile-time) check for this.
// UIWindow+Additions.h
#interface UIWindow (Addtions)
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_4_0
#property (retain, nonatomic) UIViewController *rootViewController;
#endif
#end
// UIWindow+Additions.m
#import "UIWindow+Additions.h"
#include <objc/runtime.h>
#implementation UIWindow (Additions)
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_4_0
#dynamic rootViewController;
static UIViewController *rootViewControllerKey;
- (UIViewController *)rootViewController {
return (UIViewController *)objc_getAssociatedObject(self, &rootViewControllerKey);
}
- (void)setRootViewController:(UIViewController *)newRootViewController {
UIViewController *rootViewController = self.rootViewController;
if (newRootViewController != rootViewController) {
// Remove old views before adding the new one.
for (UIView *subview in [self subviews]) {
[subview removeFromSuperview];
}
[rootViewController release];
objc_setAssociatedObject(self, &rootViewControllerKey, newRootViewController,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[rootViewController retain];
[self addSubview:rootViewController.view];
}
}
#endif
#end
Based on #David DeLong's feedback, I went with a simple subclass like so:
// UIWindow3.h
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_4_0
#interface UIWindow3 : UIWindow {
}
#property (nonatomic, retain) UIViewController *rootViewController;
#end
#endif
// UIWindow3.m
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_4_0
#import "UIWindow3.h"
#implementation UIWindow3
#synthesize rootViewController;
- (void)setRootViewController:(UIViewController *)newRootViewController {
if (newRootViewController != rootViewController) {
// Remove old views before adding the new one.
for (UIView *subview in [self subviews]) {
[subview removeFromSuperview];
}
[rootViewController release];
rootViewController = newRootViewController;
[rootViewController retain];
[self addSubview:rootViewController.view];
}
}
#end
#endif
However, this also required going through the existing code and using conditional compilation to cast UIWindow to UIWindow3 where ever rootViewController was being accessed. (Note: I think #David DeLong's solution may not require making these additional changes but rather just always using CustomWindow instead of UIWindow.) Thus, this is more annoying than if I could (only for iOS < 4) just add the rootViewController to UIWindow via a category. I may look into doing this with a category using Associative References (only for iOS < 4) because I think that looks like it'd be the most eloquent solution and might be a good technique to learn and have in the toolbox.