How to effectively call properties in another class - Objective-C - objective-c

I am a beginner with Objective-C and iOS development, and I can't figure out how to send data from one class to another without it returning null or in this case "0.0000"
I'll give the code I have-
In ClassA.h I have two properties
#interface ClassA : UIView
// This is public property!
#property (nonatomic, assign) CGFloat touchPointX;
#property (nonatomic, assign) CGFloat touchPointY;
// My test getters
- (CGFloat) touchLocationX;
- (CGFloat) touchLocationY;
#end;
Then in ClassA.m I implement these.
#implementation ClassA
- (void)baseInit {
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:self.frame];
if (self) {
[self baseInit];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
[self baseInit];
}
return self;
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInView:self];
// Save the touch locations in our local variables.
self.touchPointX = touchLocation.x;
self.touchPointY = touchLocation.y;
[self setNeedsDisplay];
}
}
- (CGFloat) touchLocationX {
return self.touchPointX;
}
- (CGFloat) touchLocationY {
return self.touchPointY;
}
#end
Then I try to log the values in AppDelegate using some code I found on here
AppDelegate.h
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (nonatomic, copy) ClassA *touchLocationX;
#property (nonatomic, copy) ClassA *touchLocationY;
#end
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
NSLog(#"(%#, %#)", _touchLocationX, _touchLocationY");
});
return YES;
}
I haven't been able to figure out how to do something like:
ClassA * myView = [[ClassA alloc] init];
myView = [myViewController theUIViewForClassA];
In the long run all I want is the properties to return the correct values

There are several ways in which data can be shared across an application.
In your example, you want to "send" touchpoints from ClassA to some other class, in this case the AppDelegate, which is fine as it demonstrates how this can work with other classes.
To accomplish this, several design patterns come to mind: Delegate Protocol, Notifications or Singleton.
I'll use the example of a singelton for convenience, because it allows us to use your AppDelegate, which is already a singleton, so this example can closely resemble your existing code.
Note that a singleton design pattern is one in which a singleton class is instantiated only once, whose methods and properties can be accessed by other classes throughout the application.
Example:
AppDelegate.h
Add a method for receiving touch events
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
- (void) notifyTouchEvents:(CGFloat)touchX :(CGFloat)touchY;
#end
AppDelegate.m
Implement the method
#implementation AppDelegate
// Method for receiving touch event data from another class
- (void) notifyTouchEvents:(CGFloat)touchX :(CGFloat)touchY
{
NSLog(#"touchX: %f, touchY: %f", touchX, touchY);
}
ClassA.h
Import the App Delegate
Note: This example doesn't require touchPointX and touchPointY properties.
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#interface ViewController : UIViewController
#property (nonatomic, assign) CGFloat touchPointX;
#property (nonatomic, assign) CGFloat touchPointY;
#end
ClassA.m
Access the AppDelegate touch notification method during touch events
#import "ViewController.h"
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInView:self.view];
// Access the AppDelegate singleton and notification method
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate notifyTouchEvents:touchLocation.x :touchLocation.y];
}
}
#end
In practice, you may find that a combination of using NSNotificationCenter and a Singleton can works best. Search around for more information on using delegates and singletons with Objective C.

Related

How to correctly implement a NSWindowController subclass with xib-file

I am trying to implement a NSWindowController subclass with new xib-file, I read up in lots of books, and researched on StackOverflow, but none of the steps provided made my window show, nor did the subclass code get executed. The new xib-file has its File's Owner set to "LogNavigatorController" and connections to the window and its contents have been made.
My AppDelegate.h:
#import <Cocoa/Cocoa.h>
#class LogNavigatorWindowController;
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
LogNavigatorWindowController *logsWindowController;
}
#end
My AppDelegate.m:
#import "AppDelegate.h"
#import "LogNavigatorWindowController.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
logsWindowController = [[LogNavigatorWindowController alloc] initWithWindowNibName:#"LogNavigatorWindowController"];
[logsWindowController showWindow:self];
}
#end
My LogNavigatorWindowController.h:
#import <Cocoa/Cocoa.h>
#interface LogNavigatorWindowController : NSWindowController
{
NSArray *directoryList1;
NSArray *directoryList2;
NSMutableArray *directoryList;
NSMutableArray *filePaths1;
NSMutableArray *filePaths2;
}
#property (assign) IBOutlet NSWindow *window;
#property (weak) IBOutlet NSTableView *logsTableView;
#property (unsafe_unretained) IBOutlet NSTextView *logsTextView;
#property (assign) IBOutlet NSArrayController *LogListController;
#property (retain) NSMutableArray *logsArray;
- (void) myDirectoryLogFunction;
#end
My LogNavigatorController.m:
#import "LogNavigatorWindowController.h"
#interface LogNavigatorWindowController ()
#end
#implementation LogNavigatorWindowController
#synthesize logsTableView;
#synthesize logsTextView;
#synthesize window;
- (id)init
{
self = [super initWithWindowNibName:#"LogNavigatorWindowController"];
[self loadWindow];
[self showWindow:#"Log Navigator"];
[self.window makeKeyAndOrderFront:nil];
if (self)
{
// Initialization code here.
[self myDirectoryLogFunction];
}
return self;
}
- (void)windowDidLoad
{
[super windowDidLoad];
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}
- (void) myDirectoryLogFunction
{
NSLog(#"Code execution test successful");
}
#end
You don't need to create the window property since it is already available for NSWindowController subclasses. Maybe that causes the problem.
Also your init method contains a lot of code that doesn't belong there. Remove
[self loadWindow];
[self showWindow:#"Log Navigator"];
[self.window makeKeyAndOrderFront:nil];
as well as replace
self = [super initWithWindowNibName:#"LogNavigatorWindowController"];
with
self = [super init];
You may want to remove the init method at all, since you don't need it in your case.
and move
[self myDirectoryLogFunction];
to the windowDidLoad method.
Also always check that the code for instantiating the window controller (in your case from the app delegates didFinishLaunching: ) is called. Sometimes it helps to create a new project and test there, if you may have changed too much within the original project and by accident removed delegate connections or similar.

NSTextView string is null when calling from an other class

I'm a newbie in Cocoa
I have a function in a class call TextSaver.m :
- (void) save {
TheNotes *myNote = [[TheNotes alloc]init];
myNote.theText = [theTextView string];
NSLog(#"%#",myNote.theText);
[NSKeyedArchiver archiveRootObject:myNote.theText toFile:#"..."];
}
And I'm calling it from the AppDelegate with applicationWillTerminate :
- (void)applicationWillTerminate:(NSNotification *)notification{
[theTextSaver save];
}
But NSLog(#"%#",myNote.theText); results null... Like NSLog(#"%#",theTextView);. Which means when I call the function I can't access theTextView.
I've already try to call this function in the TextSaver.m class with a -(IBAction) and it worked!
Hope you can help me !
EDIT
The TextSaver is created with an #import TextSaver.h and in the appInterface
TextSaver *theTextSaver;
EDIT 2
I rewrite the code to make it simpler :
AppDelegate.h :
#import <Cocoa/Cocoa.h>
#import "TheNotes.h"
#interface AppDelegate : NSObject <NSApplicationDelegate>{
TheNotes *myNote;
}
#property (copy) TheNotes *myNote;
#property (assign) IBOutlet NSWindow *window;
#end
AppDelegate.m :
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize myNote;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification{
myNote = [[TheNotes alloc]init]; //Do I need to put it in -(id) init ?
}
- (void)applicationWillTerminate:(NSNotification *)notification{
[myNote save];
}
#end
TheNotes.h :
#import <Foundation/Foundation.h>
#interface TheNotes : NSObject {
NSString *theText;
IBOutlet NSTextView *theTextView;// Do I need to alloc memory ?
}
-(void) save;
#property (copy) NSString *theText;
#end
TheNotes.m :
#implementation TheNotes
#synthesize theText;
- (void) save {
NSLog(#"%#",theTextView.string);// Save is properly called, but this results (null).
[NSKeyedArchiver archiveRootObject:theTextView.string toFile:#"..."];
}
#end
The two questions you need to answer for yourself are:
Why do I expect my TextSaver to know about the text view?
Where do I tell the TextSaver about the text view?
The other possible answer to the first question is “the TextSaver created the text view”, but I'm assuming that's not the case.
So, you need to find where you think you're telling the TextSaver about the text view and make sure that's the case.
If you haven't done anything specific to tell the TextSaver about the text view, but rather are expecting it to just know about it, then that's probably the problem.
As Phillip Mills alluded to in his comment, merely declaring a variable named theTextView does not mean that the TextSaver knows about the text view. The compiler cannot read English: the names you choose are for your own benefit only; the compiler treats them only as identifiers. It does not see “theTextView” and go “oh, that! that's over there; I'll go get it”.
In order for theTextView to actually point to the text view, you need to put the text view there. You do this via assignment. Either expose theTextView as a property and set it from somewhere else, or set it internally within the TextSaver class (after either creating the text view yourself or getting it from another object).
I would make it a property (named simply textView) and set that property from whatever owns both the TextSaver and the text view.
This is a working example of basically what you're trying to achieve, HTH.
There are two classes, AppDelegate and TestViewController. TestViewController has a UITextView, whenever the user presses the home button of the device while editing the UITextView, the - (void)applicationDidEnterBackground:(UIApplication *)application method of the AppDelegate is called and the note is printed to the console (here you could save the note instead).
I use applicationDidEnterBackground because it's what is called when the app goes into background mode.
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
PVSTestViewController *nextScreen = [[PVSTestViewController alloc] init];
self.delegate = nextScreen; // Assign TestViewController as the AppDelegate's delegate.
self.window.rootViewController = nextScreen;
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[self.delegate saveData]; // Called when the user presses the home button.
}
AppDelegate.h
....
#protocol PVSAppDelegateProtocol <NSObject>
- (void)saveData; // Any class that conforms to our protocol must implement this method.
#end
#interface PVSAppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (nonatomic, weak) id<PVSAppDelegateProtocol> delegate; // Here we store the delegate class.
#end
TestViewController.m
#import "PVSTestViewController.h"
#interface PVSTestViewController ()
#property (weak, nonatomic) IBOutlet UITextView *textView;
#end
#implementation PVSTestViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (void)saveData
{
// This is called by the AppDelegate when the app goes into the background.
NSLog([NSString stringWithFormat:#"Text is: %#", self.textView.text ]);
}
#end
TestViewController.h
....
#import "PVSAppDelegate.h"
#interface PVSTestViewController : UIViewController <PVSAppDelegateProtocol>
#end

NSTimer in AppDelegate does not update UILabel in UIViewController

My goal is to start a timer in the app delegate and use it to call methods in other view controllers. When these methods are called they will update the text of the UILabel. I'm performing the method on the main thread but I can't figure out why the UILabel is not being updated. I know the method in the view controller is being called but the UILabel doesn't get updated. I also verified the IBOutlet connection in Interface Builder. I am using storyboard to lay out my views and attach the view controllers to those views. What am I doing wrong?
In my AppDelegate.m:
#import "AppDelegate.h"
#import "GreyViewController.h"
#interface AppDelegate ()
#property (nonatomic) int counter;
#property (strong, nonatomic) GreyViewController *greyClass;
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(performOnMain)
userInfo:nil
repeats:YES];
return YES;
}
- (GreyViewController *)greyClass {
if (!_greyClass) {
_greyClass = [[GreyViewController alloc] init];
}
return _greyClass;
}
- (void)performOnMain {
[self performSelectorOnMainThread:#selector(updateLabel) withObject:nil waitUntilDone:YES];
}
- (void)updateLabel {
self.counter++;
NSLog(#"appdelegate counter %i", self.counter);
[self.greyClass updateGreyLabel:[NSString stringWithFormat:#"%i", self.counter]];
}
#end
The GreyViewController.h is:
#import <UIKit/UIKit.h>
#interface GreyViewController : UIViewController
#property (strong, nonatomic) NSString *greyString;
#property (weak, nonatomic) IBOutlet UILabel *greyLabel;
- (void)updateGreyLabel:(NSString *)string;
#end
The method in my GreyViewController.m:
- (void)updateGreyLabel:(NSString *)string {
self.greyLabel.text = string;
NSLog(#"greyviewcontroller string %#", string);
}
Every time you call:
GreyViewController *greyClass = [[GreyViewController alloc] init];
in your "updateLabel" method (i.e. every two seconds) you're instantiating a new GreyViewController. It's very likely you do not want to do that.
Instead, instantiate/create it once (or create it in your storyboard or XIB file) and the update the label which is connected to an outlet.

CALayer _layer is not getting initialized on application startup?! (OSX, Cocoa)

I'm very new to AVFoundation and QuartzCore development and I'm having troubles with CALayers. I'm sorry if this is a silly problem.
Here's my code:
.h
#import <Cocoa/Cocoa.h>
#import <AVFoundation/AVFoundation.h>
#import <QuartzCore/QuartzCore.h>
#interface Document : NSPersistentDocument
{
AVPlayer *player;
AVPlayerLayer *playerLayer;
NSView *playerView;
}
#property AVPlayerLayer *playerLayer;
#property AVPlayer *player;
#property IBOutlet NSView *playerView;
#end
.m
#import "Document.h"
#implementation Document
#synthesize playerView;
#synthesize player;
#synthesize playerLayer;
- (id)init
{
self = [super init];
if (self) {
}
return self;
}
- (NSString *)windowNibName
{
return #"Document";
}
- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
[super windowControllerDidLoadNib:aController];
[[aController window] setMovableByWindowBackground:YES];
// HERE the layer is nill, and I don't understand why it's not getting initialized?!
[[[self playerView] layer] setBackgroundColor:CGColorGetConstantColor(kCGColorBlack)];
}
+ (BOOL)autosavesInPlace
{
return YES;
}
#end
Any kind of help is very appreciated!
If the layer is nil, you should start by suspecting that its parent playerView is nil. Is it? If so, you probably haven't hooked up the outlet in the nib. (I see you have declared playerView as an outlet in your code, but that doesn't mean you've configured the nib correctly.)

delegate method does not get called

I'm completely stuck with calling a method from a UIView subclass, the method just doesn't get fired, I have a feeling that I'm doing something wrong but after searching the web I did not find any clue. Thank you in advance
Here's the iPadMainViewController.h file
#import <UIKit/UIKit.h>
#import "TouchView.h"
#interface iPadMainViewController : UIViewController <TouchViewDelegate>
#property (retain) UIWebView *detailsView;
#end
and the iPadMainViewController.h file that holds the method
- (void)MethodNameToCallBack:(NSString *)s
{
NSLog(#"%#",s);
}
Here's the TouchView.h file, which is supposed t
#protocol TouchViewDelegate
- (void)MethodNameToCallBack:(NSString *)s;
#end
#interface TouchView : UIView {
id<TouchViewDelegate> delegate;
}
#property (nonatomic, assign) id delegate;
#end
Here's the TouchView.m file which is supposed to call a method of it's delegate
#implementation TouchView
#synthesize delegate;
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"HELLO FROM INSIDE");
[[self delegate] MethodNameToCallBack:(NSString *)#"HELLO FROM OUTSIDE"];
}
#end
Synthesizing the delegate is not enough, because it just creates the getter and the setter methods. It does not create an instance of iPadMainViewController.
So after you create an instance of TouchView, you should assign an instance of iPadMainViewController as the delegate.
iPadMainViewController *controller = [[iPadMainViewController alloc] init...
// ...
TouchView *touchView = [[TouchView alloc] init...
// ...
touchView.delegate = controller;
Or in the iPadMainViewController's viewDidLoad method:
- (void)viewDidLoad
{
// ...
self.touchView.delegate = self;
}
check after you instantiated a TouchView instance, did you assign its delegate?
Enhance your touchesBegan implementation a little for further debugging:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"HELLO FROM INSIDE");
NSLog(#"our delegate is set towards: %#", delegate);
...
}
Does it log something useful in that second logging statement?
I presume it prints nil and that would be the root cause of your issue; you forgot to assign the delegate.