Class method used in [Rectangle class] - objective-c

What is the method "class" for, as used in [Rectangle class]? Does it create an instance of Rectangle? I thought that was
Rectangle *aRectangle = [[Rectangle alloc] init].
Why/When would I ever use [Rectangle class]?

Here are probably the two most common uses for [Rectangle class]:
You can use [Rectangle class] to check whether an object is an instance of Rectangle (or an instance of a subclass of Rectangle):
if ([object isKindOfClass:[Rectangle class]]) {
Rectangle *rect = (Rectangle *)object;
// ... use rect
}
But if you just have one message to send, it might be better to just check whether the object understands the message you want to send it:
if ([object respondsToSelector:#selector(area)]) {
double area = [object area];
// etc.
}
You can use the class object to create an instance of the class:
Class rectangleClass = [Rectangle class];
Rectangle *rectangle = [[rectangleClass alloc] init];
Why would you want to do that? Well, if you know the class explicitly at the location (in your code) where you want to create it, you wouldn't. You'd just say [[Rectangle alloc] init], for example.
But consider UIView. Every UIView creates and manages a CALayer. By default, it creates an instance of CALayer, but you might want your view to use a CAShapeLayer (example) or a CAGradientLayer (example) instead. You need a way to tell UIView to create an instance of a different class.
You can tell your UIView subclass what kind of layer to create by overriding the layerClass class method:
#implementation MyShapeView
+ (Class)layerClass {
return [CAShapeLayer class];
}
When it's time for a MyShapeView to create its layer, it'll send itself layerClass and create an instance of whatever class it gets back. The code probably looks something like this:
#implementation UIView {
CALayer *_layer;
}
- (CALayer *)layer {
if (_layer == nil) {
_layer = [[[self layerClass] alloc] init];
_layer.delegate = self;
}
return _layer;
}

Related

using method from another class

I tried to add picture to my NSView with calling method from another class.
How I must init firstClass instance in secondClass?
Code example:
firstClass.m:
//method for adding picture
-(void) createPoint:(NSPoint)location{
point = [NSImage imageNamed:#"point3"];
point.size = NSMakeSize(21, 21);
pView = [[NSImageView alloc] init];
[pView setFrameSize:crimePoint.size];
[pView setImage:crimePoint];
[pView setFrameOrigin:CGPointMake(location.x-10, location.y-10)];
[self addSubview:pView];
}
secondClass.m:
- (IBAction)updateButton:(id)sender {
[firstC createPoint:NSMakePoint(300, 300)]; // FirstClass* firstC; (alloced and inited)
}
This one looking same, but i don't understand how to use that answer:
Calling Method From Another UIViewController Has No Visible Effect

drawRect: in custom NSView ignores its own instance variables

I have a custom NSView class named "testClass." I create an instance of this class in my main application controller class. testClass contains a 4 element float array as an instance variable that holds colors for the NSView's background. I want to set those values from the main controller class, and then drawRect: should use them to draw the background. However, when I set the values, drawRect refuses to see them. They're always 0.0.
In Interface Builder, I put the custom view in the main window of the app's MainMenu.xib. I then assign it to testClass.
Here's my code:
#interface testClass : NSView
{
#public
float colors[4];
}
#end
#implementation testClass
-(void)drawRect:(NSRect)dirtyRect
{
//The colors are ALWAYS 0.0, 0.0, 0.0:
fprintf(stderr,"colors: %.2f %.2f %.2f\n",colors[0],colors[1],colors[2]);
NSColor *c = [NSColor colorWithCalibratedRed:colors[0] green:colors[1] blue:colors[2] alpha:1.0];
[c setFill];
NSRectFill(dirtyRect);
}
#end
//Now, in the main app controller:
testClass *test = [[testClass alloc] init];
test->colors[0] = 1.00; //r
test->colors[1] = 0.75; //g
test->colors[2] = 0.25; //b
test->colors[3] = 1.00; //a
[test setNeedsDisplay:YES];
//This prints the colors as they should be:
fprintf(stderr,"colors: %.2f %.2f %.2f\n",
test->colors[0],
test->colors[1],
test->colors[2]);
Why does the custom view not recognize its own variables, even if I call setNeedsDisplay:YES from the class that created the custom class? If what I'm trying to do won't work, then what's the correct approach?
Update:
As user1118321 pointed out, the instance of testClass I created with the [[testClass alloc] init] line wasn't what I should have done. Since I already had an instance of testClass in the form of a Custom View in Interface Builder (to which I assigned "testClass"), I just needed to create a pointer to it as an IBOutlet in the main controller class. Once I did that, it worked!

Setting Bool in different classes

I have the following code where after a bool is true I want to add a drawing to my rect. here is the code I have but for some reason it is either not setting the bool or calling the setNeedsDisplay. Am I referencing to the other class properly? thanks
//in AppController.m
-(IBAction)colorToggle:(id)sender
{
if ([colorFilter state] == NSOnState)
{
CutoutView *theView = [[CutoutView alloc] init];
[theView setFilterEnabled:YES];
}
}
//in cutoutView.m
- (void)drawRect:(NSRect)dirtyRect
{
[[[NSColor blackColor]colorWithAlphaComponent:0.9]set];
NSRectFill(dirtyRect);
//this is what i want to be drawn when my bool is true and update the drawRect
if (filterEnabled == YES) {
NSRectFillUsingOperation(NSMakeRect(100, 100, 300, 300), NSCompositeClear);
[self update];
}
}
-(void)update
{
[self setNeedsDisplay:YES];
}
OK, you know how not every UILabel is the same? Like, you can remove one UILabel from a view without all the others disappearing too? Well, your CutoutView is the same way. When you write CutoutView *theView = [[CutoutView alloc] init]; there, that creates a new CutoutView that isn't displayed anywhere. You need to talk to your existing CutoutView (probably by hooking up an outlet, but there are any number of perfectly valid designs that will accomplish this goal).
You are forgetting to call the drawRect: method, it should looks like this:
CutoutView *theView = [[CutoutView alloc] init];
[theView setFilterEnabled:YES];
[theView setNeedsDisplay];
From the docs:
When the actual content of your view changes, it is your
responsibility to notify the system that your view needs to be
redrawn. You do this by calling your view’s setNeedsDisplay or
setNeedsDisplayInRect: method of the view.

Unique ID on NSViews

Is there any kind of ID that can be used and set in the .nib/.xib via Xcode that can be queried at runtime to identify a particular view instance from code?
In particular when having multiple copies of the same NSView subclass in our interface how can we tell which one we're currently looking at?
In Interface Builder, there is a way to set the "identifier" of an NSView. In this case, I'll use the identifier "54321" as the identifier string.
NSView Conforms to the NSUserInterfaceItemIdentification Protocol, which is a unique identifier as an NSString. You could walk through the view hierarchy and find the NSView with that identifier.
So, to build on this post about getting the list of NSViews, Get ALL views and subview of NSWindow, you could then find the NSView with the identifier you want:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSView *viewToFind = [self viewWithIdentifier:#"54321"];
}
- (NSView *)viewWithIdentifier:(NSString *)identifier
{
NSArray *subviews = [self allSubviewsInView:self.window.contentView];
for (NSView *view in subviews) {
if ([view.identifier isEqualToString:identifier]) {
return view;
}
}
return nil;
}
- (NSMutableArray *)allSubviewsInView:(NSView *)parentView {
NSMutableArray *allSubviews = [[NSMutableArray alloc] initWithObjects: nil];
NSMutableArray *currentSubviews = [[NSMutableArray alloc] initWithObjects: parentView, nil];
NSMutableArray *newSubviews = [[NSMutableArray alloc] initWithObjects: parentView, nil];
while (newSubviews.count) {
[newSubviews removeAllObjects];
for (NSView *view in currentSubviews) {
for (NSView *subview in view.subviews) [newSubviews addObject:subview];
}
[currentSubviews removeAllObjects];
[currentSubviews addObjectsFromArray:newSubviews];
[allSubviews addObjectsFromArray:newSubviews];
}
for (NSView *view in allSubviews) {
NSLog(#"View: %#, tag: %ld, identifier: %#", view, view.tag, view.identifier);
}
return allSubviews;
}
Or, since you are using an NSView subclass, you could set the "tag" of each view at runtime. (Or, you could set the identifier at run-time.) The nice thing about tag, is that there is a pre-built function for finding a view with a specific tag.
// set the tag
NSInteger tagValue = 12345;
[self.myButton setTag:tagValue];
// find it
NSButton *myButton = [self.window.contentView viewWithTag:12345];
Generic NSView objects cannot have their tag property set in Interface Builder. The tag method on NSView is a read-only method and can only be implemented in subclasses of NSView. NSView does not implement a setTag: method.
I suspect the other answers are referring to instances of NSControl which defines a -setTag: method and has an Interface Builder field to allow you to set the tag.
What you can do with generic views is use user-defined runtime attributes. This allows you to pre-set the values of properties in your view object. So if your view defined a property like so:
#property (strong) NSNumber* viewID;
Then in the user-defined attributes section of the Identity inspector in Interface Builder, you could add a property with the keypath viewID, the type Number and the value 123.
In your view's -awakeFromNib method, you can then access the value of the property. You will find that in the example above, the viewID property of your view will have been pre-set to 123.
Here's how to simulate "tags" in OSX without subclassing.
In iOS:
{
// iOS:
// 1. You add a tag to a view and add it as a subView, as in:
UIView *masterView = ... // the superview
UIView *aView = ... // a subview
aView.tag = 13;
[masterView addSubview:aView];
// 2. Later, to retrieve the tagged view:
UIView *aView = [masterView viewWithTag:13];
// returns nil if there's no subview with that tag
}
The equivalent in OSX:
#import <objc/runtime.h> // for associated objects
{
// OSX:
// 1. Somewhere early, create an invariant memory address
static void const *tag13 = &tag13; // put at the top of the file
// 2. Attach an object to the view to which you'll be adding the subviews:
NSView *masterView = ... // the superview
NSView *aView = ... // a subview
[masterView addSubview:aView];
objc_setAssociatedObject(masterView, tag13, aView, OBJC_ASSOCIATION_ASSIGN);
// 3. Later, to retrieve the "tagged" view:
NSView *aView = objc_getAssociatedObject(masterView, tag13);
// returns nil if there's no subview with that associated object "tag"
}
Edit: The associated object "key" (declared as const void *key) needs to be invariant. I'm using an idea by Will Pragnell (https://stackoverflow.com/a/18548365/236415).
Search Stack Overflow for other schemes for making the key.
Here's a simple way get NSView tags in OSX without subclassing.
Although NSView's tag property is read-only, some objects
that inherit from NSView have a read/write tag property.
For example, NSControl and NSImageView have a r/w tag properties.
So, simply use NSControl instead of NSView, and disable (or ignore)
NSControl things.
- (void)tagDemo
{
NSView *myView1 = [NSView new];
myView1.tag = 1; // Error: "Assignment to readonly property"
// ---------
NSControl *myView2 = [NSControl new]; // inherits from NSView
myView2.tag = 2; // no error
myView2.enabled = NO; // consider
myView2.action = nil; // consider
// ---------
NSImageView *myView3 = [NSImageView new]; // inherits from NSControl
myView3.tag = 3; // no error
myView3.enabled = NO; // consider
myView3.action = nil; // consider
}
Later, if you use viewWithTag: to fetch the view, be sure to specify NSControl (or NSImageView) as the returned type.

Working on custom component: subclass UIView or UIViewController?

I'm working on a custom implementation of UISegmentedControl.
I'd like to create a component that able to receive config data and from which obtain a custom View similar to UISegmentedControl.
I started subclassing a UIView and i can create a custom UISegmentedControl with this code:
CustomSegment *segment = [[CustomSegment alloc]
initWithTitles:[NSArray arrayWithObjects:#"one",#"two",nil]];
[self.window addSubview:segment];
But now i'd like to improve my class and add some more customizable parameters to it.
For example i'd like add a custom separators, define the button fonts and so on... here my doubt:
Is it better to work on a UIView subClass or you suggest me to subclass a UIViewController, where i can manage View hierarchy in method like -(void)loadView and -(void)viewDidLoad ?
In a simple UIView subclass, when i launch the custom init method, i setup immediately subviews... while using a UIViewController i can call custom init and define how my subview is builded into -(void)loadView.
Don't use an UIViewController, just extend the UIView class like you did and keep extending its functionality.
Remember to save a pointer to each subview you add (i.e. buttons) in order to be able to access them later.
Define custom setters, for example, a custom setter for changing a button label title would be:
- (void) setButton1Title:(NSString*)str forState:(UIControlState)state{
//You can add some control here
if ([str length] > 20) return;
[_button1 setTitle:str forState:state]; //_button1 is my reference to the button
}
And so on. Don't provide direct access to your subviews, use methods instead.
Also, you can use "layoutSubviews" method to define how your views are going to be displayed in your custom view.
Hope it helps you.
Edit: In your case, I don't see why using lauoutSubviews method but I want to show you what I was trying to say.
Lets say that for example I need to create an UIView class to represent a "Contact" object in my application.
This is what I would do:
#interface ContactView : UIView{
UILabel* _nameLabel;
UILabel* _ageLabel;
Contact* _contact;
}
#property (retain) Contact* contact;
#end
#implementation ContactView
#synthetize contact = _contact;
-(id)initWithContact:(Contact*)c{
self = [super init];
if (self) {
_nameLabel = [[UILabel alloc] init];
_nameLabel.frame = CGRectZero;
[self addSubview:_nameLabel];
[_nameLabel release];
_ageLabel = [[UILabel alloc] init];
_ageLabel.frame = CGRectZero;
[self addSubview:_ageLabel];
[_ageLabel release];
self.contact = c;
}
}
- (void) layoutSubviews{
[super layoutSubviews];
_nameLabel.frame = CGRectMake(0.0f, 0.0f, 200.0f, 25.0f);
_ageLabel.frame = CGRectMake(0.0f, 25.0f, 200.0f, 25.0f);
if (self.contact){
_nameLabel.text = self.contact.name;
_ageLabel.text = self.contact.age;
}else{
_nameLabel.text = #"Unavailable";
_ageLabel.text = #"Unavailable";
}
}
- (void) setContact:(Contact*)c{
self.contact = c;
[self layoutSubviews];
}
#end
Check out how the "layoutSubiews" is used to set the correct frame and data to the labels.
Usually, I use it a lot when creating custom UITableViewCells where you have to reuse the view.
Let me know if I'm being confusing.