How to alloc initWithCoder or initWithFrame - objective-c

#import "DotAnimation.h"
#implementation DotAnimation
DoodlePad* dp;
-(void)setupView {
Ball = CGRectMake(10, 10, 10, 10);
[NSTimer scheduledTimerWithTimeInterval:(1.0f / 30.0f) target:self selector:#selector(traceBall) userInfo:nil repeats:YES];
}
-(id)initWithCoder:(NSCoder *)aDecoder {
if (self == [super initWithCoder:aDecoder]){
[self setupView];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setupView];
}
return self;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.*/
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [[UIColor yellowColor] CGColor]);
CGContextFillEllipseInRect(context, Ball);
}
-(void)traceBall{
vector<CGPoint>::iterator L = dp.doodlePoints->begin();
while(L != dp.doodlePoints->end()){
Ball.origin.x += (*L).x;
Ball.origin.y += (*L).y;
++L;
}
[self setNeedsDisplay];
}
- (void)dealloc
{
[super dealloc];
}
#end
I have this Animation I am trying to use in another file.
So i figure i do something like
trace = [[DotAnimation alloc]initWithCoder];
or
trace = [[DotAnimation alloc]initWithFrame];
I am unsure which one to use or if i am even writing this correctly.
I want to be able to use:
while(k != dp.doodlePoints->end()){
Ball.origin.x += (*k).x;
Ball.origin.y += (*k).y;
++k;
}
In another file but I don't know how to call Ball.origin from DotAnimation
Also it would be great if you could link me to good information on understanding this.

initWithCoder is called by the framework when you embed an object into a XIB. Like a ViewController for example. So you should not call it by yourself.
Calling initWithFrame could be a better solution if you want to set the frame (like that seems to be done here). But you may give a frame, and add a subview created by yourself.
if you don't want to create the subview by yourself, then it's initWithNibName:bundle: that you may call/override. Creating the XIB that contains the view. But you will still need to add that view as subview to the main view that is showing that animated view.

initWithCoder: is something you would call yourself when restoring from an archive; it isn't appropriate for something which appears to be a view object. As it is only used for simple setup code, you can remove it.
Also, animating by changing the "Ball" object from another object isn't really consistent with Cocoa view programming. You should take a look at "Introduction to View Programming Guide for Cocoa" in the documentation, and work from there.

Related

Xcode os x : Why drawRect could not access instance data

I am trying to draw something in my custom view, but not sure why drawRect could not access its instance data. Here is the steps I tried.
Create a Mac OS X app, with using storyboard checked.
In the storyboard, delete the view, then add a new custom view under the view at the same place. (I tried if the view is not deleted, same).
Assign EEGView class to the newly added custom view.
then run. From the log information, you will notice that the drawRect could not access the instance data although the instance variables get initialized and updated.
In viewCtroller.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
myView = [[EEGView alloc] init];
//[self.view addSubview:myView];
//Start Timer in 3 seconds to show the result.
NSTimer* _timerAppStart = [NSTimer scheduledTimerWithTimeInterval:2
target:self
selector:#selector(UpdateEEGData)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timerAppStart forMode:NSDefaultRunLoopMode];
}
- (void)UpdateEEGData
{
// NSLog(#"UpdateEEGData.....1");
// myView.aaa = 200;
// myView.nnn = [NSNumber numberWithInteger:myView.aaa];
// make sure this runs on the main thread
if (![NSThread isMainThread]) {
// NSLog(#"NOT in Main thread!");
[self performSelectorOnMainThread:#selector(updateDisplay) withObject:nil waitUntilDone:TRUE];
}else
{
[self.view setNeedsDisplay:YES];
}
NSLog(#"UpdateEEGData.....2");
[myView setAaa:400];
myView.nnn = [NSNumber numberWithInteger:myView.aaa];
// make sure this runs on the main thread
if (![NSThread isMainThread]) {
// NSLog(#"NOT in Main thread!");
[self performSelectorOnMainThread:#selector(updateDisplay) withObject:nil waitUntilDone:TRUE];
}else
{
[self.view setNeedsDisplay:YES];
}
}
-(void)updateDisplay
{
[self.view setNeedsDisplay:YES];
}
In my custom view class EEGView.m
#implementation EEGView
#synthesize aaa;
#synthesize nnn;
-(id)init{
self = [super init];
if (self) {
aaa = 10;
nnn = [NSNumber numberWithInteger:aaa];
NSLog(#"init aaa: %i", aaa);
NSLog(#"init nnn: %i", [nnn intValue]);
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
// Drawing code here.
NSLog(#"drawRect is here");
NSLog(#"drawRect aaa: %i", aaa);
NSLog(#"drawRect nnn: %i", [nnn intValue]);
}
#end
Did I miss anything? Tested in Xcode 7.2 & 7.2. But if I leave the 'using storyboard' unchecked, it works.
Or is it a Xcode bug?
Any advice appreciated.
Thanks in advance.
If you've added EEGView view on storyboard, you shouldn't be also instantiating one in viewDidLoad. You've alloc/init'ed a new one, but it bears no relationship to the one that the storyboard created for you. So, the one created by the storyboard has drawRect called, but you're setting the properties in the separate instance that you created in viewDidLoad which was never added to the view hierarchy (and thus never will have its drawRect called).
When the storyboard instantiates the view controller's view, it will instantiate your EEGView for you. All you need to do is to hook up an IBOutlet for this view in order to get a reference to it from your view controller. (For example, you can control drag from the EEGView in the storyboard scene to the #interface for the view controller that you've pulled up in the assistant editor.)

Notification when content changes in NSOutlineView subclass

I'd like to subclass NSOutlineView to show a label in the middle of itself when there is no content yet. Much like the inspectors in XCode:
Obviously I can't use the delegate methods, because I'm implementing this as a subclass and I have to be able to set the delegate to something else when using this class.
I didn't find any notifications that I could observe, except changes in the bounds property, but that's not very reliable.
I ended up overriding several methods of NSOutlineView to inject my code. It's not a very elegant solution, but it works. If anyone has a better solution, let me know and I might accept your answer instead.
- (void)updateEmptyLabelVisibility {
int r = [[self dataSource] outlineView:self numberOfChildrenOfItem:nil];
BOOL hasRows = r > 0;
_emptyLabel.hidden = hasRows;
}
- (void)reloadItem:(id)item {
[super reloadItem:item];
[self updateEmptyLabelVisibility];
}
- (void)reloadData {
[super reloadData];
[self updateEmptyLabelVisibility];
}
- (void)awakeFromNib {
[super awakeFromNib];
[self updateEmptyLabelVisibility];
}
- (void)endUpdates {
[super endUpdates];
[self updateEmptyLabelVisibility];
}

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.

Where to call [self addSubView]?

I have a custom view to which I need to add a couple of subviews programmatically. Which is the best place to create and add these subviews? Apple docs say:
If your view class manages one or more integral subviews, do the following: Create those subviews during your view’s initialization sequence.
This is a bit unclear to me. Should I handle that in initWithFrame, initWithCoder or somewhere else?
Note that I'm not talking about controllers here, this is a View that needs to initialize itself.
initWithFrame is the method you call to create a UIView programmatically, while initWithCoder is called when a UIView is instanciated from a XIB.
So it all depends on how you're going to create your containing view.
A way to cover all cases :
- (id) initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])){
[self setUpSubViews];
}
return self;
}
- (id) initWithCoder:(NSCoder*)aDecoder
{
if ((self = [super initWithCoder:aDecoder])){
[self setUpSubViews];//same setupv for both Methods
}
return self;
}
- (void) setUpSubViews
{
//here create your subviews hierarchy
}
Do the following: take the method whichever is the designated initializer for your view. That is, it's the most configureable (i. e. most arguments) init... method that every other initializer calls. For example, it can be -initWithFrame:, as most commonly. Then implement this method as follows:
- (id) initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// add new views here, for example:
UIImageView *imageView = [[UIImageView alloc] initWithImage:anImage];
[self addSubview:imageView];
[imageView release];
}
return self;
}
Generally , you should create the view and [self addSubView] in viewdidload/viewwillappear of the parent view controller if you want to show the view all the time. You can also set it hidden if you wih. In some other scenario like you want to show a view on a button click or some other actions, you should add subviews accordingly.
By integral, they mean either important or massive. viewDidLoad works just fine, but for really big, or important stuff, initialization methods are the way to go.

Subviews not showing up in UIView class

I'm trying to lay out some images in code using addSubview, and they are not showing up.
I created a class (myUIView) that subclasses UIView, and then changed the class of the nib file in IB to be myUIView.
Then I put in the following code, but am still getting a blank grey screen.
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
[self setupSubviews];
}
return self;
}
- (void)setupSubviews
{
self.backgroundColor = [UIColor blackColor];
UIImageView *black = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"black.png"]];
black.center = self.center;
black.opaque = YES;
[self addSubview:black];
[black release];
}
yes, just implement initWithCoder.
initWithFrame is called when a UIView is created dynamically, from code.
a view that is loaded from a .nib file is always instantiated using initWithCoder, the coder takes care of reading the settings from the .nib file
i took the habit to do the initialization in a separate method, implementing both initWithCode and initWithFrame (and my own initialization methods when required)
try implementing initWithCoder: sometimes I've had trouble with IB and initWithFrame:
or at least add a logging call to see if your init method is executed