I was reading the Apple Documentation and came across this line:
SKView *spriteView = (SKView *) self.view;
I'm not confused about SKView, but rather the notation that is used. I have never seen it before and do not know what its called or how it works. Normally, if I wanted to create a view, I would do something like:
UIView *view = [UIView alloc]init];
Can someone explain the differences between the two notations or breakdown the first notation? I've seen this used in UIColor a lot as well. I don't know what this is called so I'm not exactly sure what I would have to search.
Normally, if I wanted to create a view...
The notation you're asking about, self.view, doesn't create a view directly. Dot notation is a simple way to access an object's properties. It's exactly the same as [self view]. The object that is self in your example is probably a view controller, and therefore has a view property which is accessed via a -(NSView*)view method and set via -(void)setView:.
Now, view controllers are lazy about loading their views -- they don't load (or create) the view they manage until it's needed. So, the -(UIView)view accessor looks something like:
-(UIView)view
{
if (_view == nil) {
[self loadView];
}
return _view;
}
So it's not the dot notation that creates the view, it's the fact that you're calling the -view method. Of course, since the view property is declared as a UIView* but your view controller manages an instance of SKView, you need to cast the value to SKView*.
Related
I ask this question because it appears that viewDidLoad gets called before the main block of the initializer does and the class variables I'm initializing in the initializer are turning up nil in viewDidLoad. For your reference, I'm doing this entire viewcontroller programmatically and I've created a custom initializer so I can take in various parameters to be used in my viewcontroller. My custom initializer calls UIViewControllers designated initializer of course.
Basically, I'm curious about what is the proper design pattern for sending in parameters to a UIViewController? I've read other threads about this and haven't really gotten a definitive answer. Should I just skip the initializer and set the properties manually (from outside the class)? That seems kind of bleh, I'd really like to send in some parameters and am curious how others go about doing this?
The reason viewDidLoad is called before initialization completes, is probably because you call the view method in the initializer. For example:
- (id)init
{
if ((self = [super init])) {
_thing = 123;
_other = self.view.frame.size.width / 2;
}
return self;
}
viewDidLoad is called when the view loads. And the view loads as soon as you call the view method (or access the view property, if you prefer). So you should avoid referencing the view in init.
To answer your question, I prefer to create an init method for my view controllers.
- (id)initWithThing:(MyThing *)thing thang:(MyThang *)thang
{
if ((self = [super init])) {
_thing = [thing retain];
_thang = [thang retain];
// ...
}
return self;
}
You can also use properties to set extra variables after initing. Personally I prefer to do this with optional properties only, though, and put required properties in the init method. That way I can't init the view controller with an invalid state.
I encapsulate the state of my controllers in an additional state object. So the controller usually has ivars for the GUI elements and a reference to this state object.
State objects are handled by a StateManager object on my delegate. So instead having a controller referencing another controller and set variables directly, all changes go through this manager. A little more work but way less messy.
Any class is able to retrieve the state for any other controller and change it. This is the purpose of some controllers (eg: choosing a video from youtube happens on a dedicated controller). But usually is just one class getting a state to push the next controller with initWithState:. The pushed controller then applies the state to its GUI in viewDidLoad or changes this state object passed from the previous class.
The StateManager keeps a dictionary of all the state objects. I keep the state object graph light, eg: I store a reference to a image, but not the image itself. The real beef of the application is in Core Data, I only use all this to handle the GUI state. The StateManager listens for the application resign event and saves the state dictionary to disk using NSCoding. The state object ivars are always nil or some meaningful value, no dangling pointers.
I'm working on my 2nd iPhone app and this seems to be working, but I wonder too how other people does it. Any input is welcome.
Basically you should have properties defined for your input data but add a custom init-function.
If you create a custom init-Method you should be fine - remember that initWithNibName:bundle: is the main initializer of UIViewController so this is what you want to call from your custom init-Method. viewDidLoad will always be called after your init-Method at the first usage of customVC.view (either from your code or via Framework):
- (id)initWithDataObject:(MyDataObject*)obj
{
self = [super initWithNibName:nil bundle:nil];
if (self) {
self.dataObj = obj;
}
return self;
}
- (void)viewDidLoad {
BlueViewController *blueController = [[BlueViewController alloc] initWithNibName#"BlueView" bundle:nil];
self.blueViewController = blueController; //blueViewController set to var above
[self.view insertSubview:blueController.view atIndex:0];
[blueController release];
[super viewDidLoad];
}
not understanding this code very well. How come i am inserting the subview blueController and not self.blueViewController
also what difference does it make if I don't use self. Not even sure why self is used. I interpret it as I am setting the blueViewController property of the current View Controller to the blueController instance but why would I do that. The book I am reading from does not explain such things in detail. It is pretty much monkey do this.
not understanding this code very well. How come i am inserting the subview blueController and not self.blueViewController
since you have executed the assignment:
self.blueViewController = blueController;
those two variables are the same, so
[self.view insertSubview:self.blueController.view atIndex:0];
would be just the same as the code you posted.
also what difference does it make if I don't use self. Not even sure why self is used. I interpret it as I am setting the blueViewController property of the current View Controller to the blueController instance but why would I do that. The book I am reading from does not explain such things in detail. It is pretty much monkey do this.
if you don't assign to self.blueController, then your variable is just a simple variable local to that function. By having a property self.blueController and storing there a value, you can use that value in all of the selectors (functions) of your class.
check the code and you will see that self.blueController is used also in other functions. e.g., at some point you might decide you like making that subview hidden, or you want to remove it, etc. All of this you can do only if you have a pointer to the controller accessible to your class functions.
self is used if you are referring to an object of the class.
While initializing a variable we must use self. This will increment blueViewController retainCount to 1.
self.blueViewController = blueController;
While inserting also you can use both. Results will be same.
[self.view insertSubview:blueController.view atIndex:0];
[self.view insertSubview:self.blueController.view atIndex:0];
blueController is an alloced and initialized object while blueViewController is just a pointer to the BlueViewController class.By writing
self.blueViewController = blueController
you retain the blueController object.If you do not use self you won't be ratining the object and after you release it at line
[blueController release];
your program will crash as soon as you refer to it again.
I have a subclass of UIView that's instantiated in a XIB file. I need it to do some initialization (settings some variables and creating a subview).
However, I do not always instantiate this view via Interface Builder. I do it programmatically too. In both cases, the initialization needs to be the same.
My designated initializer is initWithValues:.
The question is; where do I perform the initialization?
Since I have to perform it in 2 different locations, I figured I need to refactor it in a separate initialize method (or something like that), and call it from initWithValues:.
But when loading from IB, both initWithCoder: and awakeFromNib are called. From which method do I have to call initialize? Or do I have to call initWithValues: from initWithCoder: and do nothing in awakeFromNib?
You should use initWithFrame: when initializing views (since it's the designated initializer). Hence, if you have initWithValues: make sure you call initWithFrame: from it.
Something like this should work for initializing: ;)
- (void)initialize{
//init your ivars here
}
- (id)initWithCoder:(NSCoder *)aCoder{
if(self = [super initWithCoder:aCoder]){
[self initialize];
}
return self;
}
- (id)initWithFrame:(CGRect)rect{
if(self = [super initWithFrame:rect]){
[self initialize];
}
return self;
}
I was going to add a further explanation, but mplappert's answer is clear enough. Use awakeFromNib if necessary.
That depends on what you need to initialize. As soon as awakeFromNib gets called, all outlets and action connections of your view are established which is not the case in initWithCoder:. So if you need to rely on those connections, use awakeFromNib. Otherwise you can safely do all your initializing in initWithCoder:.
Unfortunately, the above answers don't take into account these things:
- (void) awakeAfterUsingCoder - and the fact it's called after anything is created by the coder (once for every Xib view).
awakeFromNib suffers from the same fate, I've noticed. (The reason I found this)
Another initializing issue is that initWithCoder and initWithFrame can be avoided for custom views. And if they are called, lazy loading (though not as important on views themselves), means you "might" be able to modify values. I believe I've done so in initWithCoder, but if you then initialize values in awakeFromNib, it's undone at least once.
I've gone so far as to:
- (void) awakeFromNib (or didMoveToSuperView);
{
BOOL called = NO;
if(!called)
{
called = YES;
}
}
Another method I use is to simply call the initializer needed, then call my own class or superclass-specific initializer.
I, too, am looking for a dependable one-time place I can rely on. Until then, I hope my headaches save the next person an hour or so.
Steve
At various points during my application's workflow, I need so show a view. That view is quite memory intensive, so I want it to be deallocated when it gets discarded by the user. So, I wrote the following code:
- (MyView *)myView {
if (myView != nil)
return myView;
myView = [[UIView alloc] initWithFrame:CGRectZero]; // allocate memory if necessary.
// further init here
return myView;
}
- (void)discardView {
[myView discard]; // the discard methods puts the view offscreen.
[myView release]; // free memory!
}
- (void)showView {
view = [self myView];
// more code that puts the view onscreen.
}
Unfortunately, this methods only works the first time. Subsequent requests to put the view onscreen result in "message sent to deallocated instance" errors. Apparently, a deallocated instance isn't the same thing as nil. I thought about putting an additional line after [myView release] that reads myView = nil. However, that could result in errors (any calls to myView after that line would probably yield errors).
So, how can I solve this problem?
Setting myView to nil is the correct thing to do here. Not doing so is what's yielding errors, because it undetectably refers to a deallocated object. Your code tests for nil to see if it needs to create a new view, so you should set the variable appropriately.
You're going to have problems because your not using accessors. You need to define a property for the view. Then whenever you refer to view use the self-dot notation. If you do this then simply setting the view property to nil like this:
self.myView=nil;
... will trigger its release automatically.
However, this is bad way to manage the view especially if you load it from nib. The view most likely is a required property of a controller object. Setting it to nil invites crashes.
The better way is to have the view controller handle memory issues. On the iPhone you can put memory management code in viewDidDisappear: or didReceiveMemoryWarning. In any case you don't kill the view as long as the controller is alive but instead release the memory intensive parts of the view e.g. images. This leaves the view as a lightweight shell object. Then in 'viewWillAppear` you load the memory intensive parts back in.
However, the best way to handle this problem is to actually pop the view controller from a navigation stack. At that point the view controller cleans up after itself automatically.
I have several instances of a UIControl class Foo being instantiated, one instance corresponding to each cell in a UITableView. The Class has:
BOOL selected;
UIImageView *imageView;
UIImage *imageOne;
UIImage *imageTwo;
I've assigned each instance a tag:
foo.tag = indexPath.row;
I would now like to reference the UIImageView.image for a (or several) specific instance(s) by its tag to switch it to the other image.
In my search I've seen things like classes being assigned tags using initWithTag (I assume they're assigning tags)...
SomeClass *someClass = [[SomeClass alloc]initWithTag:1 ...
[someArray addObject: [[SomeClass alloc]initWithTag:2 ...
[someArray addObject: [[SomeClass alloc]initWithTag:3 ...
...but I haven't seen how they are later referenced by that tag.
I have seen a reference to getChildByTag which had promise, but I can't find it in the documentation or examples (maybe not iphone).
Does anyone know how reference the imageView.image within an instance using the instance's tag? (the imageView doesn't have a tag)
Thanks
Let me be a bit more specific. As each instance of Foo is set up in the UITableViewCells I use addTarget
[self addTarget: self action: #selector(switchImage:) forControlEvents: UIControlEventTouchDown];
Then I have this method to switch the images:
- (void) switchImage:(id)sender
{
selected = !selected;
imageView.image = (selected ? imageOne : imageTwo);
// self.tag in here is the indexPath.row from the foo.tag initially assigned
// NSLog(#"switchImage:%#",sender);
}
This works perfectly. I click on the image and the image switches. But in the entire tableView, I only want 1 imageOne all the rest to be imageTwo so I want a way to first turn off all images and then turn on the one. I therefore felt that I could to loop thru all of the instances of Foo using the tag to either somehow directly change the imageView.image or run switchImage in each of the instances to turn off each cell's image.
Lastly, when I look at sender via NSLog, I see that each Foo has a different address so I was wondering if something like allTargets (Foo is a UIControl) would allow me to get to all of the switchImage methods.
I'm pretty deep into this rabbit hole but I'll certainly start over if necessary.
I agree that there are other ways to deal with this, but you could add all your Foo objects to an array or set and iterate through them until you find the one with the tag you want, then access the imageview in the usual way.
If I'm getting you right, you try to access Foo objects (globally) by the tag you assigned to them before. There's no way to do that and it's not what the tag property of UIViews was designed for.
You have to use another way to access your Foo objects. There are uncountable ways of setting up a way to access objects. Since you would usually access them from their data source or view controller, I’d add a dictionary with there. You could also use UITableView's visibleCells property to only change images on cells that are actually displayed.
I feel a bit lost, but how about [UIView viewWithTag:]?
UIView instances have a tag property. You can access them from their superview using:
UIView *view = [superview viewWithTag:n];