delegate don't work between 2 viewcontrollers - objective-c

I get stuck on using the delegate method.
I have 2 views. In view A, I use a delegate to say I pressed a button to view B.
View B receives the message well.
I would like to sent a message to say that the view B has received the message.
So the view from A to B for my delegate works but the B to A is not working.
.h:
#import <UIKit/UIKit.h>
#protocol ButtonProtocolName <NSObject>
- (void)toucheEnter:(id)sender;
#end
#interface ClavierKsViewController : UIViewController
#property (nonatomic, assign) id <ButtonProtocolName> delegate;
#end
.m
#import "ClavierKsViewController.h"
#interface ClavierKsViewController ()
#end
#implementation ClavierKsViewController
-(IBAction)toucheEntreeClavier:(id)sender{
[self.delegate toucheEnter:sender];
}
#end
Now on view B.h:
#import "ClavierKsViewController.h"
#interface CaisseViewController : UIViewController<ButtonProtocolName>
#property (nonatomic,retain) ClavierKsViewController *clavierKsView;
b.m
- (void)viewDidLoad
{
[super viewDidLoad];
clavierKsView = [self.storyboard instantiateViewControllerWithIdentifier:#"clavierKsView"];
clavierKsView.delegate=self;
}
-(void)toucheEnter:(id)sender{
NSLog(#"valider");
}
in that sense it works
When I created a delegate from view B to view A in based on the same way, I still get the same message: can not find protocol declaration for delegate

The protocol cannot work is likely caused by circular dependency. The solution is to extract your protocol to another .h like "ButtonProtocolName.h" and import the protocol in both a.h and b.h

Related

Unit Testing in XCode (Obj C) With MVVM

I'm testing out some MVVM pattern stuff and seem to have gotten myself confused. Hoping someone here can clarify things for me.
So, what I did was set up a project and added a class that is a subclass of NSObject and called it RootViewModel.
Gave it one method:
- (void) rootTest {
NSLog(#"Booyeah!");
}
In ViewController I imported RootViewModel and made an IBOutlet for it.
#import "ViewController.h"
#import "RootViewModel.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UIButton *btnRunModel;
#property IBOutlet RootViewModel* myModel;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.myModel rootTest];
}
#end
Then in Storyboard I dragged an Object into the ViewController scene, named it RootModel and connected it to the myModel property in ViewController.
Run the app and it works as expected, Booyeah gets logged.
So now here's where I got messed up. I wanted to set up a unit test. So working in the default unit test file I imported ViewController and made it a property and instantiated it in the set up.
#import <XCTest/XCTest.h>
#import "ViewController.h"
#interface ObjectiveVMMVTests : XCTestCase
#property (nonatomic, strong) ViewController* myViewController;
#end
#implementation ObjectiveVMMVTests
- (void)setUp {
[super setUp];
self.myViewController = [[ViewController alloc] init];
}
Then I tried to create a test where I call the rootTest method.
- (void) testRootModel {
[self.myViewController.myModel rootTest];
}
But I get a compiler error saying myViewController has no property myModel. I assumed it would be there, not sure where I messed this up.
In your unit test, you are saying:
#import "ViewController.h"
That's great. So now the unit test knows that this is a class. But that is not where the myModel property is declared. It is declared in ViewController.m, making this a private property.
Move the property declaration into ViewController.h to make it public so the unit test can see it.
Like #matt said, the IBOutlet is not part of the public interface of ViewController. It's private, hidden in the implementation (.m) file.
You have at least two viable options:
Add #property IBOutlet RootViewModel* myModel; to the ViewController.h file to make it part of the public interface;
Add an interface definition to the ObjectiveVMMVTests unit test file that'll satisfy the compiler:
#interface ViewController ()
#property IBOutlet RootViewModel* myModel;
#end
The implementation of the -(RootViewModel*)myModel getter is there anyway, the compiler just needs to know that ViewController does respond to the message. (You could use performSelector if you weren't interested in the returned object.)

Objective-c - control outlet from other class

I just like to play with coding for a hobby, so probably a noob question;
I have a simple storyboard for MacOS with 2 views. Both have there own classes (main class and subclass). How can I control a outlet in the subclass from the main class?
for example
I have a button (IBAction) in the mainclass and a textfield (IBOutlet) in the subclass. I want to set the stringvalue for the textfield with a click on the button in main.
I have searched a lot last days but just don't get it. (or just need a push in the right direction)
EDIT after JingJingTao's answer:
I used the control-drag function to open the second window.
I tried the code JingJingTao gives, but the textfield doesn't respond to the action.
My classes look like this now:
ViewController.h
#import <Cocoa/Cocoa.h>
#interface ViewController : NSViewController
- (IBAction)newText:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#import "ViewController2.h"
#interface ViewController ()
#property (nonatomic) ViewController2 *subclass;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)setRepresentedObject:(id)representedObject {
[super setRepresentedObject:representedObject];
}
- (void)newText:(id)sender {
self.subclass.textField.stringValue = #"button pressed";
}
#end
ViewController2.h
#import "ViewController.h"
#interface ViewController2 : ViewController
#property (nonatomic) IBOutlet NSTextField *textField;
#end
ViewController2.m
#import "ViewController2.h"
#interface ViewController2 ()
#end
#implementation ViewController2
- (void)viewDidLoad {
[super viewDidLoad];
}
#end
Update:
I've attached two screenshots of what it looks like in the storyboard for the first suggestion,
1) Add a view to your ViewController, set the class at the top right to 'YourView', 'YourView' is a just an NSView, add a textfield to it and hook it up.
2) Add YourView as a property to your ViewController, i.e. #property (nonatomic) IBOutlet NSView *yourView; and hook it up.
Let me know if there are any issues.
You just need to put the textfield in the public interface of your subclass, so you can access it in your main class, although it does sound like you're using inheritance and I don't think you need to but that's another topic :D.
Example:
In MainClassViewController.m
#interface MainClassViewController ()
#propert (nonatomic) Subclass *subclass;
#end
#implementation MainClassViewController
// I guess you already add your subclass to the main viewcontroller because they display on the same screen.
- (void)yourButtonTapMethod {
self.subclass.textfield.text = #"Your value";
}
In Subclass.h
#interface Subclass : NSObject
#property (nonatomic) IBOutlet UITextfield *textfield;
I use Cocoa Touch instead of Cocoa, so maybe it's NSTextfield for you. Please let me know if this does not answer your question, good luck.

Objective-C message being sent to superclass

I have derived a class SignalView from UIView however when I send a message to my object of type SignalView it gets sent to a UIView and I get error :
-[UIView Initialise]: unrecognized selector sent to instance 0x1b3900
my object also appears as a UIView object in the debug watch window:
m_signalview UIView * 0x001b3900
the relevant code is:
// signalview.h
#import <UIKit/UIKit.h>
#interface SignalView : UIView
{
}
-(void)Initialise;
#end
//signalview.m
#import "SignalView.h"
#interface SignalView ()
#end
#implementation SignalView
-(void)Initialise
{
}
// viewcontroller.h
#import <UIKit/UIKit.h>
#import "SignalView.h"
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet SignalView *m_signalview;
#end
// viewcontroller.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize m_signalview;
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#", m_signalview);
[m_signalview Initialise];
}
H2C03 is right. its likely not a SignalView. I would check your outlet in your view's nib file in the interface builder inspector make sure it doesn't still say UIView. If it does change it to SignalView:
Should look like this:
Then it's simply an instance of UIView and not a SignalView. You're allocating and initializing UIView when you would need a SignalView instance. When you have something like this in your code:
m_signalView = [[UIView alloc] init];
change it to
m_signalView = [[SignalView alloc] init];
I see that m_signalview is an IBOutlet.
Are you connecting that to a UIView you added in Interface Builder?
If so you might need to set the right class in its properties so that the loader can create an object of the right class.

Setting up the App Delegate to Manage Data

I'm working on an app with a Split View controller and would like to store the main data class in the App Delegate so I can access it from multiple views (MasterView, DetailView, and several PopUps).
I'm a bit of a noob and can't figure out why Im getting the error:
AppDelegate.m:31:26: Property 'dataController' not found on object of type 'MasterViewController'
Below is the relevant code - any help is much appreciated. Thanks.
AppDelegate.h
#import <UIKit/UIKit.h>
#class EventClassDataController;
#class MasterViewController;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#end
AppDelegate.m
#import <UIKit/UIKit.h>
#class EventClassDataController;
#class MasterViewController;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#end
MasterViewController.h
#import <UIKit/UIKit.h>
#class DetailViewController;
#class EventClassDataController;
#interface MasterViewController : UITableViewController
#property (strong, nonatomic) EventClassDataController *dataController;
#property (strong, nonatomic) DetailViewController *detailViewController;
#end
MasterViewController.m
#import "MasterViewController.h"
#import "DetailViewController.h"
#import "EventClassDataController.h"
#import "EventClass.h"
#interface MasterViewController ()
#end
#implementation MasterViewController
#synthesize detailViewController, dataController;
- (void)awakeFromNib
{
self.clearsSelectionOnViewWillAppear = NO;
self.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0);
[super awakeFromNib];
// Initialize event data
self.dataController = [[EventClassDataController alloc] init];
}
EventClassDataController.h
#import <Foundation/Foundation.h>
#class EventClass;
#interface EventClassDataController : NSObject
#property (nonatomic, copy) NSMutableArray *masterEventList;
-(NSUInteger)countOfList;
-(EventClass *)objectInListAtIndex:(NSUInteger)theIndex;
-(void)addNewEvent:(EventClass *)event;
-(void)removeEvent:(EventClass *)event;
#end
You need to add
#import "MasterViewController.h"
to AppDelegate.m.
Explantation:
From your error message I can tell that you're trying to access a property of MasterViewController on line 26 of your AppDelegate. However, in your AppDelegate class you only have a forward declaration ("#class MasterViewController") and you don't include the actual header file for MasterViewController. What this means is that AppDelegate knows there exists a class named MasterViewController somewhere in your project... but that's all it knows. AppDelegate doesn't know anything about the contents of MasterViewController, i.e. properties or methods MasterViewController declares.
The reason to use #class in a header file is so that you can have properties in an #interface such as
#property (nonatomic, strong) MasterViewController * appDelegatesReferenceToMasterViewController;
without having to import MasterViewController.h in AppDelegate.h. This way, other files in your project can import AppDelegate.h without also inadvertently importing MasterViewController.h.
Translation:
You writing the code:
#class MasterViewController;
is like saying to the compiler:
Yo... compiler. I'm going to be talking about something called MasterViewController in this header file and you don't know what a MasterViewController is... but don't trip out bro. It's all good.
But then, later in the .m file after you write the code:
... masterViewController.dataController ...
the compiler responds:
Whoa, whoa, whoa. I was cool with you just having a variable with the type MasterViewController in the header file because you told me it was all good and not to worry. But now here you are trying to use some aspect of MasterViewController that I know nothing about. How am I supposed to know if this is legal or not? Not cool bro.

Using delegation to talk to view controllers in TabBarController

I'm trying to use protocols and delegation to send messages to different views in a tab bar controller. I've set it up like below but I don't seem to be receiving the message;
#class RaprTabBarViewController;
#protocol RaprTabBarViewControllerDelegate <NSObject>
#optional
- (void)initActivites:(NSArray *)activities;
#end
#interface RaprTabBarViewController : UITabBarController <RKObjectLoaderDelegate>
#property (nonatomic, weak) id <RaprTabBarViewControllerDelegate> delegated;
#end
Then in the implementation
- (void)someMethodThatIsCalled:(NSArray *)objects {
NSLog(#"Delegating..");
[self.delegated initActivites:objects];
}
Now I get the NSLog so the method is definitely called. And now here is a view controller which is embedded in a navigation controller;
#interface ActivitesViewController : UITableViewController <RaprTabBarViewControllerDelegate>
#property (nonatomic, strong) NSArray *activites;
#end
And the implementation
- (void)initActivites:(NSArray *)activities {
NSLog(#"Called initActivities");
self.activites = activities;
}
I do not get the NSLog in the view controller so the initActivites method is never called. What am I doing wrong?
Thanks for your help.