Getting a NSViewController's View if it is a custom class? - objective-c

I use the following code to get my View out of my controller:
CollectionItemView *myView = [self view];
This works pretty well, but I get the warning Incompatible pointer types initializing CollectionItemView __strong with an expression of type NSView. I understand why i get this but is it okay to ignore it or should I overwrite the view property ?
chuck

If you are sure that [self view] is CollectionItemView just do:
CollectionItemView *myView = (CollectionItemView*)[self view];
or (which is better) you can use:
id myView = [self view];

There is no need to overwrite it. troolee already suggested two working solutions.
However, just to be save I'd rather code it differently.
CollectionItemView *myView = nil;
if ([[self view] isKindOfClass:[CollectionItemView class])
self.view = (CollectionItemView*)[self view];
The shorter version without isKindOfClass test is ok when you know for sure from the context that the object must be of type CollectionItemView or any of its subclasses.

Related

Is there a better way to remove a last subview from view

To remove last subview from a view, I use
[[[self.view subviews] lastObject] removeFromSuperview];
I'd use removeLastObject, but can't as subviews is readonly immutable array.
So I need to:
access all subviews
get last object
call method on it to remove it from superview
It works, but is there a better way? This seem to be a bit unnatural. I look for something like [self.view removeLastSubview], but unfortunately it doesn't exist.
To avoid Objective-C's message-sending syntax
Note: dont do that
UIView *lastView = self.view.subviews[self.view.subviews.count-1];
lastView.removeFromSuperview;
I said: dont do that
but seriously:
[[[self.view subviews] lastObject] removeFromSuperview];
is just fine. I'd consider the first snippet code-smell.
Make a category
#interface UIView (Exptened)
- (void)removeLastSubView;
#end
#implementation UIView(Exptened)
- (void)removeLastSubView
{
[[[self subviews] lastObject] removeFromSuperview];
}
#end

Access int variable from other class

I have two windows, my main window "window" and "help window" all inside my App Delegate. In my main window its view is subclassed and I want to draw a rect inside it. My help window has a rect also but it has an NSTracker on it. What I want to do is draw my rect in my window subclass with the x and y coordinates equal to my NSTracker position. The problem I am having is it crashes when I try to bring in the coordinates, any ideas of what I could be doing wrong? thanks
//My subclass of window is called CutoutView. This is all in draw rect
AppDelegate *controller = [[[NSApp delegate] window] contentView];
xValue = controller.mouseLoc.x;
yValue = controller.mouseLoc.y;
NSRectFillUsingOperation(NSMakeRect(xValue,yValue, 600, 400), NSCompositeClear);
[self update];
- (void)update
{
NSLog(#"test");
[self setNeedsDisplay:YES];
}
//My AppDelegate with the tracker helpView is a reference to the view of my second window "Help Window"
-(void)updateTrackingAreas
{
if(trackingArea != nil) {
[self.helpView removeTrackingArea:trackingArea];
[trackingArea release];
}
opts = (NSTrackingActiveAlways | NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved);
trackingArea = [ [NSTrackingArea alloc] initWithRect:[self.helpView bounds]
options:opts
owner:self
userInfo:nil];
[self.helpView addTrackingArea:trackingArea];
}
-(void)mouseMoved:(NSEvent *)theEvent
{
mouseLoc = [NSEvent mouseLocation];
NSLog(#"mouseMoved: %f %f", mouseLoc.x, mouseLoc.y);
}
in my CutoutView am i getting the AppController wrong because it is in a different window "helpWindow"? or does it have to do with my int values?
There are many things wrong with your code and it's obvious that you are fundamentally misunderstanding some basic concepts.
Firstly, you state that this code is in your drawRect: method;
AppDelegate *controller = [[[NSApp delegate] window] contentView];
xValue = controller.mouseLoc.x;
yValue = controller.mouseLoc.y;
NSRectFillUsingOperation(NSMakeRect(xValue,yValue, 600, 400), NSCompositeClear);
[self update];
There are several immediate flaws apparent. Firstly, why are you declaring controller to be of type AppController* when the method you are calling (-contentView) returns an NSView?
Your AppController is not a view (at least it should not be!), so you should be declaring the object as such:
NSView* mainView = [[[NSApp delegate] window] contentView];
If you are indeed using a view as a controller then this is completely wrong. See below for my note about MVC.
You don't specify where the mouseLoc property is coming from. We need to see where this is declared, because that will affect whether or not there are problems with it.
Your drawing code calls [self update], which simply tells the view to redraw itself. This will result in an infinite loop because every time the view draws it is forced to redraw. You should never call setNeedsDisplay: from inside drawRect:.
Even after making these changes, this code is very badly structured and the design is broken.
As it stands, your code violates the Model-View-Controller pattern. A views should not have knowledge of other views. You need to restructure things so that your views display properties of your controller without needing knowledge of other views. That means that you must store the mouse location in your controller (or a model object) and use some method for the view to access that information, preferably a datasource protocol or similar. In my answer to this other question I give an example of how to do that.
You need to read the Cocoa Drawing Guide. You also need to learn more core Cocoa concepts as it is clear you are misunderstanding how Cocoa is supposed to work.

UIViewController and UITableViewController, how to change self.view then back again?

I dont want to add a sub view, but instead change the "self.view" to another view eg (A warning view) then after the user suppresses the warning I would like to switch back. When ever i try to switch back to the original view i just get a blank screen for reasons i cant understand.
Here is what i currently have in one of my UITableViewControllers
//Show warning view controller
self.warningViewControler = [[[WarningViewController alloc] init] autorelease];
self.view = self.warningViewController.view;
//Then later
self.view = self.tableView; //<< Dosnt work
If you want to change your view, and if the original view is defined/linked into XCode, you must retain it before changing self.view to another view. If not, the original view is released and using it back can cause bad things to happen.
Warning :
self.warningViewControler = [[[WarningViewController alloc] init] autorelease];
self.view = self.warningViewController.view
is a bad bad call. Because you autorelease the controller but you use its view. So you get a view retained with a released controller after some time. Retain the controller and release it yourself when its view is not needed anymore.
Here's the better way to do what I think you're trying to do:
WarningViewController *warningViewController = [[WarningViewController alloc] initWithNibName:#"theRightNiborNil" bundle:nil];
[self presentModalViewController:warningViewController animated:YES];
// or if you don't need to support iOS4 any more:
[self presentViewController:warningViewController animated:YES completion:nil]
// and if you aren't using ARC yet, then [warningViewController release];
Then in your WarningViewController you want some action that calls:
[self dismissModalViewControllerAnimated:YES];
// or again if this is iOS5..
[self dismissModalViewControllerAnimated:YES completion:nil];
Hope that helps.

ObjC : difference between self.obj and obj

What is the difference between self.obj and obj in objective C?
I'm asking this because when I write [view method] it's fine
but when trying [self.view method] it's an infinite loop
In fact the code is the following:
//---create a UIView object---
UIView *view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
view.backgroundColor = [UIColor lightGrayColor];
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[view addSubview:button];
self.view = view;
The problem is that [self.view addSubview:button] gives an infinite loop.
Using obj is just what it looks like: direct variable access.
However, self.obj is not what it looks like. Instead, it is context-dependent syntactic sugar for a call to an accessor method. By convention, the setter method would be setObj: and the getter obj, though it is possible to override this. So typically self.obj is equivalent to either [self obj] or [self setObj:x] depending on whether you're reading from the value or assigning to it.
In your example, when you put [self.view method], what you are really doing is [[self view] method]. Without knowing more about the context in which you're using this, it's hard to say why it's giving you an infinite loop, although one obvious case that would do that is if you were making this call inside the accessor method for view.
When you're using '.' you're accessing property. And when you're typing [view method] you're accessing the variable named view. See my answer for this question.
for example:
-(void) doSmth {
UIView* view = [[UIView alloc] init];
[view method]; //sends message 'method' to view variable declared in this function
[self.view method]; //sends message to instance variable of self (accessing it via generated accessor)
}

Objective-c How can add one UIview at the main view directly from a class?

I have this class
- (void) initWithFrame:(CGRect)frame withString:(NSString *)html{
//CGRect rectFrame = [UIScreen mainScreen].applicationFrame;
news = [[UIView alloc] initWithFrame:frame];
web = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 200, 300)];
web.scalesPageToFit = YES;
//web.delegate = self;
web.scalesPageToFit = YES;
[web loadHTMLString:html baseURL:nil];
[news addSubview:web];
[[self view] addSubview:news];
}
I receive Sigabrt at line [[self view] addSubview:news];
How can add the UIview at the main view directly from a class? is it possibile?
1) init<...> methods should return initialized object - declare it returning value of id type. Return self in implementation or nil if it's failed.
2) init<...> methods have to initialize this object. Read about initializing objects here - The Objective-C Programming Language - Allocating and Initializing Objects. Basically you have to call one of the initialization methods of superclass, assign self to returned value (self = [super init]) and check if this value is nil.
Is this class a controller? If so:
3) You have to load it's view - either by loading it from nib on initialization (init or initWithNibName:bundle: methods) or by implementing method loadView. See View Controller Programming Guide for iOS
4) You can be sure your view is available only when viewDidLoad is called on your controller. Implement this method if you want to add something to controller's view.
The reason you are receiving the abort signal is that your view is not initialized. Instead try adding to awakeFromNib call.