Adding button control at running time in objective-c Cocoa - objective-c

If I put these lines in the xxxAppDelegate.m file, it works fine. But I need to use it under another module such as in main.m etc. The compiler generated an error stated that window is undefined. "window" is defined in the xxxAppDelegate modues, how do you reference it in another modules beside xxxAppDelegate.m
NSView *superview = [window contentView];
NSButton *button = [ [ NSButton alloc ] initWithFrame: NSMakeRect( 10.0, 10.0, 200.0, 100.0 ) ];
[ button setBezelStyle:NSRoundedBezelStyle];
[ button setTitle: #"Click" ];
[superview addSubview:button];
[button setTarget:self];
[ button setAction:#selector(doSomething:)];

Cocoa likes to keep things modular. window doesn't exist in the context of the delegate, because it is another class.
make a property for window in your app delegate if it doesn't exist:
.h
#property(readonly)NSWindow * window;
.m
#synthesize window;
then:
((YourDelegateClass *)[NSApp delegate]).window should work.

You could just paste that code wherever you need it (IE, put it in main). Unless, is there some reason you need it declared in the delegate?

Related

ARC and ViewControllers

I have a little misunderstanding regarding ARC. I'm creating a new UIViewController using the following code:
CGRect screenRect = [[UIScreen mainScreen] bounds];
LocationProfileView *locationProfile = [[LocationProfileView alloc] initWithLocation:l];
locationProfile.view.frame = CGRectMake(0, screenRect.size.height, screenRect.size.width, 400);
[appDelegate.window addSubview:locationProfile.view];
[UIView animateWithDuration:.25 animations:^{
locationProfile.view.frame = CGRectMake(0, 0, screenRect.size.width, screenRect.size.height);
}];
In its UIVIew I put a button which removes the view from screen. The problem with this is that locationProfile gets deallocated immediately after its being added to screen, so everytime I'm trying to tap on "Close" button (which calls a method in LocationProfileView class) my application will crash.
So I added a property:
#property(nonatomic, strong) LocationProfileView *locationProfile;
and changed the second line of code in:
locationProfile = [[LocationProfileView alloc] initWithLocation:l];
but now my class won't be deallocated until I initiate it again (because it loses the reference to the first instance of LocationProfileView?). What should I do to make my class being deallocated every time I tap on "Close" button? I guess that setting locationProfile to nil would work, but this means that I'll have to call a method in the main class (the one which contains the code block).
What is the proper way for doing this? Sorry if my questions is too noobish.
Note:
l is an instance of a custom class which contains some infos to be displayed in LocationProfileView's UIVIew.
- (void)closeButtonCallBack {
[self.locationProfile removeFromSuperview];
self.locationProfile = nil;
}
i am assuming the target of your close button is the viewcontroller itself
a strong pointer will the retain the object until the viewController itself is deallocated, unless you assign to it nil
a local variable will be deallocated when it goes out of scope
ALTERNATIVELY
without using the strong pointer, you can do this
LocationProfileView *locationProfile = [[LocationProfileView alloc] initWithLocation:l];
UIButton *close = [UIButton buttonWithType:UIButtonTypeRoundedRect];
close.frame = CGRectMake(0, 100, 100, 30);
[close addTarget:locationProfile action:#selector(removeFromSuperview) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:close];
In your original example,
LocationProfile *locationProfile=...
is a local variable. So it's released as soon as you return from the constructor. That's what you observed.
When you make it a strong property, the view controller retains the locationProfile:
#property(nonatomic, strong) LocationProfileView *locationProfile;

Passing data to a UIView - failure

I have a strange problem that I've never encountered before,
I have data in my viewController that I want to display in a UIView.
It's an iPad App which involve a SplitView Controller, when I click on an element within the table view (masterView) it execute a function in my detailViewController (via a protocol).
A function is executed which launch a UIView and send data to it:
myController:
- (void)SelectionChanged:(DocumentInfo*)document_info withDocu:(Document *)document{
DocumentView *viewDoc=[[DocumentView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
viewDoc.doc=document;
viewDoc.doc_info=document_info;
[viewDoc setBackgroundColor:[UIColor whiteColor]];
[self.view addSubview:viewDoc];
}
DocumentView.h
#import <UIKit/UIKit.h>
#import "Document.h"
#import "DocumentInfo.h"
#class Document;
#class DocumentInfo;
#interface DocumentView : UIView
#property(strong,nonatomic) Document *doc;
#property(strong,nonatomic) DocumentInfo *doc_info;
#end
DocumentView.m
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
UILabel *titreDoc=[[UILabel alloc] initWithFrame:CGRectMake(20, 32, 339, 21)];
titreDoc.textColor = [self makeColorRGB_RED:66 GREEN:101 BLUE:149];
titreDoc.font = [UIFont fontWithName:#"System" size:(24.0)];
[self addSubview:titreDoc];
NSLog(#"%# - %#",doc,doc_info);
titreDoc.text=#"Nouveau Document";
}
return self;
}
My view is well displayed (I mean the label appear) but impossible to get the data which would have been passed to it... (the NSLog print (null) (null) )
Anybody know the reason why?
The problem seems pretty straight forward. You initialize your view (which means that you run - (id)initWithFrame:(CGRect)frame) and after that you're setting the data, so it's normal that you see null values into your init method because the ivars have not being set yet. What you could do is to modify your init method in order to construct your view taking into account these ivars. Something like this perhaps:
- (id)initWithFrame:(CGRect)frame doc:(Document *)doc docInfo:(DocumentInfo *)docInfo;
ps. If you choose to make your custom init method do not forget to call the designated initializer (-initWithFrame:) before any customization.
The reason the NSLog prints null is because the doc and doc_info are nil when the initWithFrame method is called. The doc and doc_info properties are set after the initWithFrame method is called in selectionChanged: method. Add the NSLog function after line 3 in selectionChanged method like this:
- (void)SelectionChanged:(DocumentInfo*)document_info withDocu:(Document *)document{
DocumentView *viewDoc=[[DocumentView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
viewDoc.doc=document;
viewDoc.doc_info=document_info;
NSLog(#"%# - %#",doc,doc_info);
[viewDoc setBackgroundColor:[UIColor whiteColor]];
[self.view addSubview:viewDoc];
}

removeFromSuperview doesn't work properly

In class
#interface StartScene : UIView
I call an instance of
#interface HelpView : UIView {
GameOverMenu* gorm;
PlayScene* tView;
}
and use addSubview. I also got huge code here
-(void) removemyself {
[tView removeFromSuperview];
[gorm removeFromSuperview];
[self removeFromSuperview];
}
-(void)restartPlay {
[tView removeFromSuperview];
[self playSceneDidLoad];
}
-(void)gameOverDidLoad {
[tView removeFromSuperview];
gorm = [[GameOverMenu alloc]initWithFrame:CGRectMake(0, 0, 320, 520)];
gorm.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"backGround.jpg"]];
[gorm checkScore:Scores];
[self addSubview:gorm];
}
-(void)playSceneDidLoad {
[gorm removeFromSuperview];
tView = [[PlayScene alloc]initWithFrame:CGRectMake(0, 0, 320, 520)];
tView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"backGround.jpg"]];
[self addSubview:tView];
[tView ooneFingerTwoTaps];
}
And two sub classes of HelpView:
#interface PlayScene : HelpView
#interface GameOverMenu : HelpView <UITextFieldDelegate>
In StartScene when I push on a button, an instance of HelpView is created and in init method playSceneDidLoad is called.
Inside the PlayScene there is restart button which calls restartPlay method. When game is lost gameOverDidLoad method is called.
And In both PlayScene and GameOverMenu there are quit button, which calls removemyself method, that are supposed to return player to the main menu.
At first glance it should work fine, but if I press restart button for several times and than try to press Quit, it occurs that the views were not removed from superview, one press on a quit button only now removes them one by one.
And we stop on the HelpView, it didn't remove itself (even if I try to call [super removeFromSuperview]; somewhere.
I need to remove views correctly in time and to get to the main menu (StartScene) when quit is pressed. I don't think that a lot of views covering each other is a good variant. What is the problem?
Well I occurs that the point is that if super class' method is called from the subclass and there is such a command [self removeFromSuperview]; or [(someOtherSubview) removeFromSuperview];, it is subclass that uses self or (someOtherSubview). If our subclass doesn't have the pointed subView, than the command would do nothing. And if there is [self removeFromSubview];, subclass would remove itself.
Actually I solved this problem by using buttons as subView of superclass.

UIButton created in code not working properly

So I've been holding off asking this question for a while because I know the solution will most likely be something very very simple. But I have come to the end of my tether so here goes:
I have created a UIButton programatically and linked it to a method, but it is not working!!
.h definition
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface CreaterPage : UIViewController
{
IBOutlet UIView *postcardView;
IBOutlet UIButton *returnButton;
}
-(void)goBack:(UIButton *)button;
#end
.m definition
#import "CreaterPage.h"
#implementation CreaterPage
-(void)viewDidLoad
{
NSLog(#"Creater Page View Loaded Successfully");
UIButton *goHomeButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[goHomeButton addTarget:self action:#selector(goBack:) forControlEvents:UIControlEventTouchDown];
[goHomeButton setTitle:#"Go Back" forState:UIControlStateNormal];
goHomeButton.frame = CGRectMake(100, 100, 100, 100);
[self.view addSubview:goHomeButton];
}
-(void)goBack:(UIButton *)button
{
NSLog(#"Home");
}
#end
And basically, when I run the code, the button appears as defined but my program crashes whenever I press it.
In the main.m file, it gives me the error
Thread 1: Program received signal: "EXC_BAD_ACCESS".
On the line
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
I've tried all sorts and only turned to creating it programatically because I couldn't get it working through the interface builder.
So I'm hoping somebody on here can change my juvenile ways and show me where I'm going wrong :D
Thanks,
Matt
EDIT 1: Changed #selector(goBack) to #selector(goBack:)
My first guess would be that your action is defined as such:
[goHomeButton addTarget:self action:#selector(goBack) forControlEvents:UIControlEventTouchDown];
Note the #selector(goBack) without a colon following the method name. Your method in your .m file is defined as:
-(void)goBack:(UIButton *)button
So I imagine changing your action to #selector(goBack:) would clear things up.
Sidenote: It's very uncommon to define the type of the sender for an IBAction, as you have done. While you might not encounter any issues as long as your UIButton is the only UI object that causes the method to be called, it's very poor practice. You should change your method signature to:
-(IBAction)goBack:(id)sender
Note also the use of IBAction in place of void. While they're syntatically the same thing, the IBAction makes it clear to readers, and to Interface Builder, which methods are available for linking.
Change
[goHomeButton addTarget:self action:#selector(goBack) forControlEvents:UIControlEventTouchDown];
to
[goHomeButton addTarget:self action:#selector(goBack:) forControlEvents:UIControlEventTouchDown];

Adding a subview without a XIB

This may seem painfully simple, but I'm just starting out so bear with me...
I have a main view with a toolbar. When the user presses a button in the toolbar, I would like a small prompt to appear at the top of the screen. I added a UIView within the existing UIView, and set it to be hidden. I connected that to an ivar called 'searchView' in my view controller, and tried the following:
searchView.hidden = NO;
When that didn't work I tried adding...
[self.view addSubview:self.searchView];
...which didn't work either.
Do I need to set the index somehow to bring it above the main view?
Is this approach just altogether wrong?
If you created the UIView from the interface builder, forget to use addSubview because interface builder it's doing this automatically.
To do that in graphical mode (IB), follow this steps:
Define an IBOutlet in your file "h" should be something like:
IBOutlet UIView * myHiddenView;
Then in IB, connect the IBOutlet that you have defined in the h file to the UIView that you have created graphically in the IB and give the hidden property to this UIView (checkbox).
Then in your button press method do this:
myHiddenView.hidden = NO;
If you prefer do it programmatically to copy this example code:
define in the interface of the .h file
UIView *myHiddenView;
after the interface in the .h file:
-(void)displayView:(id)sender;
then in the .m file:
-(void)viewDidLoad{
myHiddenView = [[UIView alloc] initWithFrame: CGRectMake (x, y, width, height)];
myHiddenView.hidden = YES;
[self.view addSubview: myHiddenView]
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(x, y, width, height)];
[button setTitle:#"button" forState:(UIControlState)normal];
[button addTarget: self action: #selector(displayView:) forControlEvents: UIControlEventTouchUpInside];
[self.view addSubview:button];
}
-(void)displayView:(id)sender{
myHiddenView.hidden = NO;
}