Xcode os x : Why drawRect could not access instance data - objective-c

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.)

Related

IBOutlet always nil

I've already looked at all the other entries for this question. I have been stuck for a while on a problem I have been unable to solve, any suggestion for an avenue of approach would be sincerely appreciated.
I have a moderately large app that allocs a window controller then loads a window using an XIB containing a single window which contains an AVPlayerView that was placed in the window. The AVPlayerView is wired to an IBOutlet called playerView. The window loads when it is supposed to and then an AVPlayer is created with a URL and is to be assigned to the player of the AVPlayerView, however I always find that the IBOutlet playerView is always nil at the point of trying to make the assignment. I cannot figure out why the IBOutlet is nil. Another anomaly is that the AVPlayerWindow will not become key when so ordered even though there are no other windows on the screen. I know for a fact that I have the outlet wired up in the XIB. Any thoughts?
There are 11,000 lines of code in the app. Here is the tiny portion that attempts to attach the player to the AVPlayerView.
fileURL = [self prepareStringAndBecomeAURL:filePath isDirectory:NO needFileURL:NO];
// thePlayer = [self prepareToPlay:fileURL];
thePlayer = [AVPlayer playerWithURL:fileURL];
if (thePlayer == nil) {
[self popUpErrorMessageWithoutCode:#"Unable to create a video player."];
} else {
playerView.player = thePlayer;
if (thePlayer != nil) {
[theMainWindow orderOut:self];
[self showMoviesWindow:self];
[self performSelector:#selector(continueHandleMovieSelected) withObject:nil afterDelay:1]; // wait for window to appear
}
}
}
- (void)continueHandleMovieSelected
{
[playerView.player play];
}
No, no exceptions.
Window controller and nib are created in another module, an AppController:
- (IBAction)showMoviePlayerWindow:(id)sender
{
if (![self myMoviePlayerWindowController]) {
myMoviePlayerWindowController = [[MoviePlayerWindowController alloc] initWithWindowNibName:#"MoviePlayer"];
[self set_myMoviePlayerWindowController:myMoviePlayerWindowController];
}
[[[self myMoviePlayerWindowController] window] center];
[[[self myMoviePlayerWindowController] window] makeKeyAndOrderFront:self];
[[self myMoviePlayerWindowController] showWindow:self];
}

Using dealloc method to release

I'm trying to understand memory management to do some better apps, but was stopped at one point :
I use some UIButtons. So I alloc them, work with them etc. But i need to release them at one moment.
So I implement deallocmethod for all the object which are usefull all the time the UIViewController is on screen, and which need to be released when it desappeard. So in my UIViewController I implement :
-(void)dealloc
{
NSLog(#"Dealloc call");
[myButton release];
.... //Some objects release
[super dealloc];
}
But i never see the Dealloc call printed, so I think that it doesn't passed by the dealloc method when the UIViewController desappeard.
So, how does it work ? / What is false ?
Thanks !
EDIT : method to change of viewController :
-(void)myMethod
{
if (!nextviewcontroller)
{
nextviewcontroller = [[NextViewController alloc]init];
}
UIView *nextView = nextviewcontroller.view;
UIView *actualView = actualviewcontroller.view;
[actualviewcontroller viewWillAppear:NO];
[nextviewcontroller viewWillDisappear:NO];
[actualView removeFromSuperview];
[self.view addSubview:nextView];
[actualviewcontroller viewDidAppear:NO];
[nextviewcontroller viewDidDisappear:NO];
}

Reading touch events in a QLPreviewController

I've got a QuickLook view that I view some of my app's documents in. It works fine, but I'm having my share of trouble closing the view again. How do I create a touch event / gesture recognizer for which I can detect when the user wants to close the view?
I tried the following, but no events seem to trigger when I test it.
/------------------------ [ TouchPreviewController.h ]---------------------------
#import <Quicklook/Quicklook.h>
#interface TouchPreviewController : QLPreviewController
#end
//------------------------ [ TouchPreviewController.m ]---------------------------
#import "TouchPreviewController.h"
#implementation TouchPreviewController
- (id)init:(CGRect)aRect {
if (self = [super init]) {
// We set it here directly for convenience
// As by default for a UIImageView it is set to NO
UITapGestureRecognizer *singleFingerDTap = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleSingleDoubleTap:)];
singleFingerDTap.numberOfTapsRequired = 2;
[self.view addGestureRecognizer:singleFingerDTap];
[self.view setUserInteractionEnabled:YES];
[self.view setMultipleTouchEnabled:YES];
//[singleFingerDTap release];
}
return self;
}
- (IBAction)handleSingleDoubleTap:(UIGestureRecognizer *) sender {
CGPoint tapPoint = [sender locationInView:sender.view.superview];
[UIView beginAnimations:nil context:NULL];
sender.view.center = tapPoint;
[UIView commitAnimations];
NSLog(#"TouchPreviewController tap!" ) ;
}
// I also tried adding this
- (BOOL)gestureRecognizer:(UIGestureRecognizer *) gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*) otherGestureRecognizer {
return YES;
}
#end
Edit: For clarification, this is how I instantiate the controller:
documents = [[NSArray alloc] initWithObjects: filename , nil ] ;
preview = [[TouchPreviewController alloc] init];
preview.dataSource = self;
preview.delegate = self;
//set the frame from the parent view
CGFloat w= backgroundViewHolder.frame.size.width;
CGFloat h= backgroundViewHolder.frame.size.height;
preview.view.frame = CGRectMake(0, 0,w, h);
//refresh the preview controller
[preview reloadData];
[[preview view] setNeedsLayout];
[[preview view] setNeedsDisplay];
[preview refreshCurrentPreviewItem];
//add it
[quickLookView addSubview:preview.view];
Also, I've defined the callback methods as this:
- (NSInteger) numberOfPreviewItemsInPreviewController: (QLPreviewController *) controller
{
return [documents count];
}
- (id <QLPreviewItem>) previewController: (QLPreviewController *) controller previewItemAtIndex: (NSInteger) index
{
return [NSURL fileURLWithPath:[documents objectAtIndex:index]];
}
Edit2: One thing i noticed. If I try making swiping gestures, I get the following message. This could shed some light on what is wrong/missing?
Ignoring call to [UIPanGestureRecognizer setTranslation:inView:] since
gesture recognizer is not active.
I think your example code is incomplete. It isn't clear how you are instantiating the TouchPreviewController (storyboard, nib file or loadView.)
I have never used the class so I could be way out in left field.
If you've already instantiated a UITapGestureRecognizer in the parent viewController, it is absorbing the tap events and they aren't passed on to your TouchPreviewController.
I would implement the view hierarchy differently by attaching the UITapGestureRecognizer to the parent viewController and handle presentation and unloading of the QLPreviewController there.
I think you might not have to subclass QLPreviewController by instantiating the viewController from a nib file.
When your parent viewController's UITapGestureRecognizer got an event you would either push the QLPreviewController on the navigation stack or pop it off the navigation stack when done.
Hope this is of some help.

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.

How to alloc initWithCoder or initWithFrame

#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.