::Edit::
It works now? haha, maybe I just didn't save my latest IB...who the hell knows.
Thanks!
Hey,
This is my first time playing around with desktop applications (I have experience with iphone applications) and I'm stuck - My text view will not update to show any text:
.m:
#synthesize textView;
NSString *txt = [[[NSString alloc] initWithData:responseData encoding: NSASCIIStringEncoding] autorelease];
NSLog(#"txt = %#", txt);
[textView setString:txt];
.h:
IBOutlet NSTextView *textView;
}
#property(nonatomic, retain) IBOutlet NSTextView *textView;
And IB says textView -> Text View, so everything looks good:
NSLog above outputs the contents of the url resource Im fetching
So, what am I missing?
Double check if the outlets are done in IB. That's probably the reason (textView is probably nil here).
Where is this setString code being called? It's possible the IB outlet isn't instantiated yet. You can check this with a simple
if (textView) {
NSLog(#"textView is not nil);
} else {
NSLog(#"textView is nil");
}
To be sure that everything is set up when you call this, make sure it's after 'awakeFromNib' is called in any objects created through IB.
See NSNibAwaking Protocal
Can you double check NSTextView properties like isEditable, textColor etc to make sure none of them are set incorrectly.
Other than that the code looks good.
I reconnected everything in IB, saved, rebuilt and it worked...= [
Related
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.
I have trouble changing the text in a label programmatically.
When I run the following code, NSLog does display "Setting myLabel to = Hello World!", but the label on the screen is not changed.
UIViewOverlay *overlayWindow;
overlayWindow = [[[NSBundle mainBundle] loadNibNamed:#"UIViewOverlay" owner:self options:nil] objectAtIndex:0];
[self addSubview:overlayWindow];
[overlayWindow setMyLabel:#"Hello World!"];
My NIB file has a 300x300 window with some labels and buttons.
There is a label, which is connected to myLabel in the outlet. The UIView does display, just that the text cannot be changed programmatically.
UIViewOverlay.h
#import <UIKit/UIKit.h>
#interface UIViewOverlay : UIView {
IBOutlet UILabel *myLabel;
}
- (void)setMyLabel:(NSString *) label;
#end
UIViewOverlay.m
#import "UIViewOverlay.h"
#implementation UIViewOverlay
- (void)setMyLabel:(NSString *) label {
myLabel.text = label; // THIS LINE IS NOT WORKING! :-(
NSLog(#"Setting myLabel to = %#", label); // This line is working.
}
#end
Thanks in advance..
You are using an incorrect accessor name for your method to set the label string.
In cocoa, setFoo is the method by which an instance variable called foo is assigned. This isn't just a convention, many areas of functionality depend on it, for example the use of properties, key value coding etc.
In your code, your label is called myLabel. Your method to set the text of that label is called setMyLabel. Either this is causing your outlet to not be connected when the nib is loaded, as the runtime may be trying to use that method to assign the label to your instance variable in the first place, or all of the above has no effect and you have just not connected your outlet.
Make sure you have an object in your code for the label, like this example:
IBOutlet UILabel* aLabel;
And in interface builder (you may have already done this): Connect the aLabel (or whatever name you use) outlet to the actual label. This can be done by control clicking and dragging from the File’s Owner object, in the document window, to the label, in the view. A small, gray window will appear with at least two options, one will be the aLabel defined earlier, and the other will be the view (this is a default outlet required for viewcontrollers, it will have a dash to indicate it is already connected to something). Click on the aLabel option to select it. (I'll be honest, without my mac in front of me I copied most of this paragraph's instructions from this link.)
You have an object called aLabel that you can now treat like any other variable. If you want to change the text value, try this:
aLable.text = #"some text";
Maybe you did not make the connection. Can you try the following?
#import "UIViewOverlay.h"
#implementation UIViewOverlay
- (void)showMyLabel:(NSString *) label {
NSLog(#"My current label contents (myLabel) = %#", myLabel.text); // ** changed to myLabel.text
}
#end
If you aren't able to print the original value, then you aren't connected.
I hope that helps.
I'm quite a rookie when it comes to Cocoa programming, so I hope some experts can hint me on the right direction with a problem I'm facing.
I have a NSCollectionView that is bound to a NSArrayController (A) of elements having two properties: an URL to a picture and a NSArray (B) of elements of another class.
For every element inside the NSArrayController (A) , I load a subview with a NSImageView that should display the image in the URL and a NSTableView that is bound to the elements of the NSArray (B).
Everything works fine, except for one very important thing: the URL to the picture is not immediately available when I create the subview, it becomes available later, but when it does, I don't see the picture loading in the NSImageView.
I would think of a problem in the KVC/KVO implementation, but the strange thing is that the image loads correctly if, when the URL becomes available, the subview is not visible (e.g in a portion of the scrollview that is not displayed).
Example: The NSScrollview size is such that it can display only two subviews at a time. I add three elements to the NSArrayController (A): the first two images don't load, if I scroll down the scrollview to see the third element, I find the image loaded correctly.
Any ideas about what could cause such a strange behaviour?
Thank you in advance
Luca
series.h
#interface Series : NSObject {
#private
NSMutableString * banner;
}
-(Series*)initWithEpisode:(Episode*)ep;
-(void)setBanner:(NSString*)_banner;
#property (retain, readwrite) NSMutableString *banner;
#end
series.m
#implementation Series
#synthesize banner;
-(Series*)initWithEpisode:(Episode*)ep
{
self = [super init];
if(self){
banner = [[NSMutableString alloc]initWithString:#"some invalid URL"];
}
-(void) setBanner:(NSString*)_banner
{
[banner setString:[NSString stringWithFormat:#"some root path/%#", _banner];
}
-(void)dealloc
{
[super dealloc];
[banner release];
}
SeriesListViewController.m
-(void)VideoChangedStatus:(Episode*)ep{
//This is the delegate called by the object Episode when it retrieves the URL via asynchronous call to a webservice
Series *tmp = [[Series alloc]initWithEpisode:ep];
[[seriesList objectAtIndex:[seriesList indexOfObject:tmp]]setBanner:[ep banner]];
}
The binding is done in the subview nib file, to the NSImageView: I set File's Owner of type NSCollectionViewItem and then bind Valueurl to representedObject.banner
I didn't subclass NSCollectionView nor NSCollectionViewItem
After days of trying I found a solution that works: apparently it's not enough to use the setString method, I need to re-intialize the property inside the setBanner method
-(void) setBanner:(NSString*)_banner
{
banner = [NSMutableString[NSString stringWithFormat:#"some root path/%#", _banner]];
}
Still, I'd be very glad to know if someone has an explanation of why setString was causing that strange (to me) problem and why this solution works.
Thank you
Luca
I’m not sure why you’ve declared banner to be a mutable string — it looks like an immutable string would suffice.
At any rate, when you write a custom setter method you need to send -willChangeValueForKey: and -didChangeValueForKey: to ensure KVO (and hence bindings) compliance:
-(void) setBanner:(NSString*)_banner
{
[self willChangeValueForKey:#"banner"];
[banner setString:[NSString stringWithFormat:#"some root path/%#", _banner];
[self didChangeValueForKey:#"banner"];
}
I'm completely new to Xcode and Objective-C but I have succeeded in creating a btton and when the user clicks this button a number is displayed in a textfield, but I can't seem to find a way to put text in there.
-(IBAction) button1pressed: (id) sender {
[field1 setIntValue:36];
}
-(IBAction) button1pressed: (id) sender {
[field1 setStringValue:#"some text"];
}
Method inherited from NSControl, Documentation here: http://developer.apple.com/library/mac/documentation/cocoa/reference/ApplicationKit/Classes/NSControl_Class/Reference/Reference.html#//apple_ref/occ/instm/NSControl/setStringValue:
Make sure your properly making connections in xCode between the IB Text field (I assume your using IB since you're using IBAction) and the IBOutlet NSTextField *field1; instance variable.
I've created a sub class of UIButton:
//
// DetailButton.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface MyDetailButton : UIButton {
NSObject *annotation;
}
#property (nonatomic, retain) NSObject *annotation;
#end
//
// DetailButton.m
//
#import "MyDetailButton.h"
#implementation MyDetailButton
#synthesize annotation;
#end
I figured that I can then create this object and set the annotation object by doing the following:
MyDetailButton* rightButton = [MyDetailButton buttonWithType:UIButtonTypeDetailDisclosure];
rightButton.annotation = localAnnotation;
localAnnotation is an NSObject but it is really an MKAnnotation. I can't see why this doesn't work but at runtime I get this error:
2010-05-27 10:37:29.214 DonorMapProto1[5241:207] *** -[UIButton annotation]: unrecognized selector sent to instance 0x445a190
2010-05-27 10:37:29.215 DonorMapProto1[5241:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[UIButton annotation]: unrecognized selector sent to instance 0x445a190'
'
I can't see why it's even looking at UIButton because I've subclassed that so it should be looking at the MyDetailButton class to set that annotation property. Have I missed something really obvious. It feels like it :)
Thanks in advance for any help you can provide
Ross
UIButton is a class cluster, which implies that Apple's implementation of buttonWithType: probably looks something like this:
+(id)buttonWithType:(UIButtonType)t {
switch (t) {
case UIButtonTypeDetailDisclosure:
return [[[PrivateDetailDisclosureButtonClass alloc] init] autorelease];
case ...
}
}
So when you call [MyDetailButton buttonWithType:UIButtonTypeDetailDisclosure]; you don't get an instance of MyDetailButton, you get an instance of PrivateDetailDisclosureButtonClass (or whatever Apple actually calls it).
Note, however, that you can get buttonWithType to instantiate a subclass if you call it with UIButtonTypeCustom (At least in the simulator running v3.0):
// LGButton is a straightforward subclass of UIButton
LGButton *testBtn = [LGButton buttonWithType:UIButtonTypeCustom];
LGButton *testBtn2 = [LGButton buttonWithType:UIButtonTypeDetailDisclosure];
NSLog(#"testBtn: %#, testBtn2: %#", [testBtn class], [testBtn2 class]);
// Output: testBtn: LGButton, testBtn2: UIButton
I made the same attempt as original poster but it seems that subclassing UIButton to do something like this is hard.
What I did, which is a hack - but works for me now, is add a UITextField as subview of the UIButton. The UITextField has no frame and is hidden, but I can freely store text strings in the textfield's text property. Which is what I wanted to do...
UITextField* tf = [[UITextField alloc] init];
tf.text = #"the text that I wanna store";
tf.hidden = YES;
tf.tag = TAGOFBUTTONSUBTEXTFIELD;
[previouslyCreatedButton addSubview:tf];
[tf release];
I defined TAGOFBUTTONSUBTEXTFIELD as 99 somewhere. Global. It's ugly but...
Then later, to get that text string use something like this:
+(NSString*)getStoredStringFromButton:(UIButton*)button {
UITextField* tf = (UITextField*)[button viewWithTag:TAGOFBUTTONSUBTEXTFIELD];
return tf.text;
}
So this assumes that no one else tries to add a subview with tag 99 to the button.
Lol :-)
That exception is because the actual button that you are trying to get the annotation from is not of class MyDetailButton, it is a UIButton. Verify that you set the class in IB for that particular button. Select the button in IB and press ⌘4 to see its identity, change the Class Identity to MyDetailButton.
Just making a subclass isn't enough; the subclass does not take the place of its superclass. The same way not all UIControls, not all UIViews, not all UIResponders, and not all NSObjects have the behavior of UIButton, not all UIButtons have the behavior of your custom subclass.
What you need is an instance of your subclass. What you have is an instance of UIButton.
The solution is to make that instance an instance of your subclass instead. If you created the button in Interface Builder, select the button and press ⌘6, then change the instance's Custom Class there. If you're creating the button in code, send your alloc message to your custom subclass, not directly to UIButton.