I have two buttons in single-view app main storyboard and would like to disable one of them as well as get value from the UILabel using awakeFromNib method. As far as I concern all relationships and GUI items must be initialized and values must be assigned before calling the awake method. Unfortunately I am not able to do get the value and disable button by applying
- (void)awakeFromNib {
decreaseButton.enabled = NO;
decreaseButton.alpha = 0.2;
[polygon initWithNumberOfSides:numberOfSidesLabel.text.integerValue
minimumNumberOfSides:3
maximumNumberOfSides:12];
}
to the class I have made. I have established the connection between the UILabel and
IBOutlet UILabel *numberOfSidesLabel;
in my created class file.
Can somebody see the mistake or shall I provide more info on the problem?
First, you must call [super awakeFromNib]; when you override this method.
Second, standard practice is to use viewDidLoad. Try that instead.
Related
I have a window that I am creating based off of clicking a row in a Tableview, instantiated like such:
HKLUserProfileController *userProfileController = [[HKLUserProfileController alloc] initWithNibName:#"HKLUserProfileController" bundle:nil];
_wc =[[NSWindowController alloc] initWithWindowNibName:#"HKLProfileWindowController"];
[_wc.window.contentView addSubview:userProfileController.view];
// other extraneous stuff here
[_wc showWindow:self];
if([_wc.window canBecomeKeyWindow]) {
[_wc.window makeMainWindow];
[NSApp activateIgnoringOtherApps:YES];
[self makeKeyAndOrderFront:self];
}
This works, but I cannot seem to get the window to become the main/key/front window. I've tried:
... from this place where I am creating the WindowController, and also from inside viewDidLoad/loadView of the NSViewController whose View was added to the Window. No dice. (this is my ultimate goal here, so if you see something obvious i'm missing, please point it out).
So I realized that I should be trying to do this in the subclass of the WindowController itself... so I thought to put it in windowDidLoad, but no result. I set some breakpoints, and tried the other logical init methods, and to my surprise, NONE of them fire at all.
#implementation HKLProfileWindowController
- (void)windowDidLoad {
[super windowDidLoad];
// breakpoint here
}
-(void)awakeFromNib {
// breakpoint here
}
- (id)init {
// breakpoint here
self = [super init];
if (self) {
// breakpoint here
}
return self;
}
#end
My xib is connected as an Outlet / Delegate to my File's Owner properly, as far as I can tell. This is leading me to believe this is the root of the problem, but for the life of me, I can't figure out what is the issue...
Thanks...
EDIT
I realize what is happening with the keyWindow - because it's coming from shouldSelectRow in the TableView, it IS becoming key on mouseDown events, but the first window - which holds the TableView - is becoming key again on mouseUp events... which is maddening!
Will search for solutions to that, but open to comments here!
(still can't figure out why the init methods are not firing though...
You use NSWindowController in your alloc/init line and you expect it to become an HKLProfileWindowController instance? Why?
You also need the File's Owner custom class set to HKLProfileWindowController but there's no reason to expect that's sufficient to get an HKLProfileWindowController instance when you ask for an NSWindowController. What you get is an NSWindowController and none of the subclass code is used.
I'll delete this answer if your code is from before you implemented the HKLProfileWindowController class, but given what you have pasted here, that's why your breakpoints don't get hit.
Simple structure:
exampleController.h :
#import <Foundation/Foundation.h>
#interface exampleController : NSWindowController {
#public
IBOutlet NSPanel *entryPanel;
#property (nonatomic, strong) IBOutlet NSPanel *entryPanel;
#end
exampleController.m :
#import "exampleController.h"
#implementation exampleController
#synthesize entryPanel;
- (id)init {
self = [super initWithWindowNibName:#"ExamplePanel"];
if (self) {
// Initialization code here.
NSLog(#"entryPanel: %#", entryPanel);
[self.entryPanel setTitle:#"TESTING!"];
}
return self;
}
randomController.m :
...
- (id) init {
self = [super init];
if (self) {
// loading our example controller if it isn't loaded yet.
if (!ourExampleController) {
ourExampleController = [exampleController alloc] init];
}
}
return self;
}
...and then later in the random controller within a method I show the NSPanel via:
[ourExampleController showWindow:self];
[ourExampleController window] makeKeyAndOrderFront:self];
My problem is that no matter what, the first time the NSPanel displays and shows itself the title is always still set to the title that it has in Interface Builder! Even though I explicitly set the title in the exampleController init method.
I've also tried throwing an NSLog(#"entryPanel: %#", entryPanel) in the init method for exampleController and at launch it is always NULL. I do not have to ALLOC all my IBOutlets in the init because I am already synthesizing them?
I've double checked everything in interface builder. The File Owner for the ExamplePanel.xib is set to the exampleController class. The window AND entryPanel outlets are both referencing the NSPanel in our xib file. What am I missing ??
Thanks in advance!
EDIT: Just to add. If I open the window (..and see the default IB title) and then close it and reopen it with a method that changes the title - it seems to work! This problem seems to only reside with the window first opening. It seems like my properties are not being alloc'd until the window first opens?
EUREKA!
As per discussion here:
IBOutlet instances are (null) after loading from NIB
I learnt that the window itself is not loaded when my controller is initialized. Found that surprising since I figured using initWithWindowNibName:#"myNibFile" would also alloc and initialize all outlet properties but since I'm new to OSX Obj-C that appears to not be the case. All the outlet properties are only alloc'd once the window itself is loaded too.
It's easy to just show the window (which also loads the window if it's not loaded yet) and then quickly set all the outlets to my desired values BUT this was an issue for me since I wanted to avoid that ever so slight "screen flicker" (for lack of a better description) that occurs as the values adjust to their new settings.
The solution was to find a way to load the controller and load the window without actually showing it first! Then I discovered this:
Can you force a NSWindow to load, i.e. before it is presented onscreen?
Steps to make that happen:
Add the following to my NSWindowController subclass init method:
// this loads the window as per link/description above
[self window]
The key seems to be though to ensure that in your NIB/XIB file that the Visible At Launch is unchecked. If it is checked (default behavior) then the [self window] call above will still show your window when your app launches. Unchecking the above option ensures the above call does not show your window until you explicitly show it yourself!
E.g. You can define an action button which loads your window:
[exampleController showWindow:self];
[[exampleController window] makeKeyAndOrderFront:self];
Hope this helps someone else out. Was a head scratcher for a couple hours!
You should set the title, etc. in -awakeFromNib or -windowDidLoad instead of an -init… method. That way the values will be set before the window is shown and you won't get the flicker.
With Cocoa, I hope to display one image in one image view. Whenever I click on the showed image. A piece of predefined-size of the image around the click point will be shown in another image view in the same window with the first one. So details will be seen.
To do this, I write my own MyImageView inherited from NSImageView. In this class, I implement the mouseUp method. In this method, I do all the image coordinate things. I can do this right so far. And the new piece will be stored as a property.
#import "MyImageView.h"
#implementation MyImageView
#synthesize point;
#synthesize small_img;
#synthesize big_img;
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.point = NSMakePoint(0.0, 0.0);
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
// Drawing code here.
}
- (void)mouseUp:(NSEvent *)theEvent
{
//calculate the small image and stored in small_img
}
#end
Then I initialize an instance in the AppDelegate and do something like
[small_view setImage: myImageView.small_img]
But no small image will show.
It seems a delegate mechanism will work out. However, I am very confused with the View-Controller-Delegate pattern. And I can't find any material explaining the communication between two NSView subclass objects with sample code I can understand. I am totally a novice in this domain.
Hope somebody help me get this done since it's very important to me.Thanks!
You're right about the delegate mechanism could work. But there are other options.
Basically you have two ImageViews (or subclasses) and they can't communicate. The only piece they have in common is the container or owner (usually a ViewController, but it could be the AppDelegate), so let's call this object just the owner.
To communicate, the clicked ImageView would need to notify the owner that something happened, and the owner in turn would forward this notification to the second ImageView. The first notification can be done through a delegate, and the second method is straightforward (calling [mySecondImageView someMethod];)
It would be too long to explain here how to use a delegate, and there are many examples out there. In short, the first ImageView has a delegate property and the owner sets itself as the delegate (something like myFirstImageView.delegate = self];
If this is too complicated, another solution which might fit well here is using Notifications. Again, there are many examples, but in short, a notification would allow communicating your two ImageViews without intervention of the owner. This type of communication is good for loosely coupled objects.
EDIT:
You can of course set the second view as the delegate and it would work perfectly. I personally like to centralize control over all my objects in my ViewController, but this is my personal preference.
What kind of delegate you need? You would need to create a custom delegate (a #protocol).
#protocol imageViewProtocol
- (void)imageClicked:(... arguments...);
#end
Your FirstImageView would have a property declared in the #interface.
#interface FirstImageView ...
#property (...) id<imageViewProtocol> delegate;
...
#end
#implementation FirstImageView ...
- (void)ImageClick:(id)sender
{
// do stuff
[self.delegate imageClicked:(...arguments...)];
}
#end
I am creating some sample applications to understand the concepts of view navigation, binding etc in cocoa.
Here is the scenario:
I have a window that has a tab view(2 tabs) in MainMenu.Xib.
I have a text field in the first tab and label in the second tab. I want both of them to reflect the same value and I want to do this using binding. Also, I don't want to use the views provided to me along with the tab view.
These are the steps I have done.
The view of each tab view item is set separately in the applicationDidFinishLaunching: method using the following code:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
//initialize view controllers
view1=[[ViewTab1 alloc] initWithNibName:#"ViewTab1" bundle:nil];
view2=[[ViewTab2 alloc] initWithNibName:#"ViewTab2" bundle:nil];
//set views
[[[myTabView tabViewItems] objectAtIndex:0]setView:view1.view];
[[[myTabView tabViewItems] objectAtIndex:1]setView:view2.view];
}
myTabView is the outlet reference of the tab view from MainMenu.xib in AppDelegate.
ViewTab1 is the name of the first view controller (and the xib).
ViewTab2 is the name of the second view controller (and the xib).
ViewTab1 has one single text field (and an associated label). I have bound this to a variable(name) declared in AppDelegate.
ViewTab2 has a label. I have bound this also to the same variable in AppDelegate.
The variable, 'name' is initialized in the init method of AppDelegate.
AppDelegate.h
....
NSString *name;
....
#property(strong) ViewTab1 *view1;
#property(strong) ViewTab2 *view2;
#property (assign) IBOutlet NSTabView *myTabView;
#property (strong) NSString *name;
....
AppDelegate.m
....
#synthesize myTabView;
#synthesize view1,view2;
#synthesize name;
....
- (id)init {
self = [super init];
if (self) {
name=#"dummy";
}
return self;
....
Apart from this I haven't done any coding in my program.
In the ViewTab1.xib I got an object and made it an instance of AppDelegate and then connected the delegate reference of the Application object(NSApplication) to the same object. (I hope this is the right way of getting the AppDelegate object.)
I did the same in ViewTab2.xib
Then I bound the text field in ViewTab1 and label in ViewTab2 to this variable in AppDelegate.
When I run the program both the text field and label shows "dummy". But when I change the value in the text field, its not reflected in the label in the second tab( i.e. ViewTab2).
Please tell me what I'm doing wrong.
How to establish binding to the same App delegate object from any loaded Nib?
Yes, I know this frustrated situation as described in question... after many weeks and hundreds pages of documentation for KVO - Notifications - Bindings I think there is one very simple solution for that.
As we can find in some information sources the nib-loading process produce new instances of members... and we need to use binding connection to the old one.
Note that bindings made in InterfaceBuilder are redirect to these new instances automatically after loading nib
Why not redirect the pointer of App delegate to the old instance?
In method where you loads your nib you can test which object is app delegate before and just after nib load.
If the new one isn’t the same as the previous one you can redirect it as you want.
This simple example works for me in Xcode3 under 10.5.8 with target to OSX10.5 / i386:
// ***** SOMEWHERE IN DEFAULT APP-DELEGATE.m IMPLEMENTATION
- (IBAction) createOtherWindowFromNib: (id)sender
{
// ensure that app delegate is set as you want...
[NSApp setDelegate:self];
NSLog(#"APP-DELEGAT **** CREATE-TEST-WINDOW ***** WHO IS APP-DELEGATE BEFORE NIB LOAD: %# ", [[NSApp delegate] description]);
// we can bind members of the nib to this controller over proxy object named "File’s Owner"
NSWindowController *otherWinCapo = [[NSWindowController alloc] initWithWindowNibName: #"OtherTestWindow"];
NSLog(#"APP-DELEGAT **** CREATE-TEST-WINDOW ***** WHO IS APP-DELEGATE AFTER NIB LOAD: %# ", [[NSApp delegate] description]);
// make some test for delegates before/after here if you need ...
// usually your bindings made inside "OtherTestWindow.xib" by IB doesn’t works in this moment
// ... and some redirection if needed
[NSApp setDelegate:self];
// afer that the bind made in IB inside "OtherTestWindow.xib"
// referred to (proxy object) "Application.delegate.myBOOL" (Bind to:Application, Model Key Path:delegate.myBOOL)
// react to changes of myBOOL placed in default app delegate object as expected
// simultaneously in every open instance of "OtherTestWindow.xib"
[otherWinCapo showWindow: otherWinCapo.window]; // we need populate the window instance on screen to see it
}
I think the problem is that the objects in your xibs that you set to the app delegate class create 2 different instances of the app delegate, so changing the value of the text field changes the value of name in one instance but not in the other. That's what you're doing wrong, unfortunately, I can't think of a solution at this time.
Have you turned on 'Continuously Updates Value' in the NSTextField controls?
See this example.
Working on an experiment on the iPad. Tried some variations on how to do this, but I can't seem to get it to work correctly...
I tap a UIButton on my MainViewController and a TextEntryModule is added to the view. TextEntryModule is its own class (for multiple instantiation) and it contains a UITextView called TextEntry (this all works at the moment).
I tap on the TextEntry UITextView and it brings up the keyboard and another view (located in MainViewController) with a UITextView called TextPreview. (this also works at the moment).
The part I'm having trouble with is synching the two UITextViews. The idea being that when I type into TextEntry, the text in TextPreview will also be updated.
Outlets are linked properly for the text fields, but I think I'm missing something "obvious":
TextEntryModule *tm = (AnnotationModule *)currentModule;
TextPreview.text = tm.TextEntry.text
Thanks in advance!
UITextView: delegate.
- (void)textViewDidChange:(UITextView *)textView
Then assign it the value of the other textview in this method.
Edit
#interface MainViewController <UITextViewDelegate> {
...
}
...
#end
Then you implement this method in the implementation file of MainViewController
#implementation MainViewController
//More code
- (void)textViewDidChange:(UITextView *)textView {
TextEntryModule *tm = (AnnotationModule *)currentModule;
TextPreview.text = tm.TextEntry.text
}
#end
Then you will have to set the TextEntryModule object's delegate to self since the controller now conform to the protocol and can "act" upon this notification.
You need to become a UITextFieldDelegate and monitor when text changes in the one field and then update the other field. Take a look at the documentation on it.