Change NSTextView content outside an IBAction Method - objective-c

I'm trying to modify the string contents of an NSTextView object. So far I've been able to make it work with the following code.
.h file
#import "AppDelegate.h"
#interface InterfaceManager : AppDelegate {
IBOutlet NSTextView *content;
}
-(IBAction)displayOutput:(id)sender;
#property (assign) IBOutlet NSWindow *window;
#end
.m file
#import "InterfaceManager.h"
#implementation InterfaceManager
-(IBAction)displayOutput:(id)sender {
[content setString:#"This is a string"];
}
#end
which all works perfectly fine and does exactly what I want.
However I need to be able to do the same thing in a void method. Here's what I've tried.
.h file
#import "AppDelegate.h"
#interface InterfaceManager : AppDelegate {
IBOutlet NSTextView *content;
}
-(void)displayOutput:(NSString *)string;
#property (assign) IBOutlet NSWindow *window;
#end
.m file
#import "InterfaceManager.h"
#implementation InterfaceManager
-(void)displayOutput:(NSString *)string {
[content setString:string];
}
#end
and it doesn't seem to work.
What am I doing wrong and how to I fix it to make it work?
Thanks in advance! :)

If I understand you correctly, you set the text when a button is pushed, but you also want to set the text without the user pushing a button. If that it the case then you don’t need a "void method", you can use any method to do it. For example, I set the client name in my options screen when I first enter the screen using this code.
- (void)viewDidLoad {
// All games allow a client and rewards
if (self.currentClient) {
self.clientInput.text = self.currentClient;
}
}
If you want to have a separate method to set the string, I think you can use something like this:
-(void)myContentSettingMethod {
[self.content setString:#"This is a string"];
}
and then call it whenever you want to set the content.

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.

How to load specific url in Webview programmatically?

I am trying to make a basic search tool in cocoa. I am trying to take the string from a textfield, edit it to make a link that searches the string in google, and then feed that into the WebView. However, Xcode is not letting me use the variable I made for the Web View. I'm getting errors along the lines of "Unknown type name 'WebView'" and "Bad Receiver type '*int'". Any help would be appreciated. Here's my code for
NewWindowController.h:
#import <Cocoa/Cocoa.h>
#interface NewWindowController : NSWindowController
#property (weak) IBOutlet NSComboBox *searchselector;
- (IBAction)onclickGO:(id)sender;
#property (weak) IBOutlet WebView *webv;
#end
NewWindowController.m:
#import "NewWindowController.h"
#interface NewWindowController ()
#end
#implementation NewWindowController
#synthesize searchselector, webv;
- (void)windowDidLoad {
[super windowDidLoad];
NSLog(#"Window did load");
}
- (IBAction)onclickGO:(id)sender {
NSInteger *indexofsearch = [searchselector indexOfSelectedItem];
NSLog(#"%d",indexofsearch);
//test for web view to load page
[[webv mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString: #"https://google.com"]]];
}
#end
WebView is part of WebKit.framework, which is not included in the default projects.
Add #import WebKit; to the top of whatever file you're referencing the WebView object in.
Add WebKit.framework to "Link Binary with Libraries" (in Build Phases)

Set label text from different from class file

What I would like the code to do is so when the button is pressed it runs the function in the Label.m file and it then sets the labels text to "test". Whenever I run it the code calls the function but doesn't change the labels text. Can someone please help me fix my code or show me the correct and easiest way to change a labels text from a class file.
In my FirstViewController.h
#interface FirstViewController : UIViewController{
IBOutlet UILabel *test;
}
#property (nonatomic, retain) IBOutlet UILabel *test;
In my FirstViewController.m
#import "Label.h"
-(IBAction)refresh:(id)sender {
[Label getSchedule];
}
In my Label.h
#import "FirstViewController.h"
#interface Label : NSObject
+ (void)getSchedule;
#end
In my Label.m
#import "FirstViewController.h"
#implementation Label
+ (void)getSchedule{
NSLog(#"log");
FirstViewController *VC = [[FirstViewController alloc] initWithNibName:#"FirstViewController" bundle:nil];
VC.test.text = #"test";
}
#end
Edit: As Maddy mentioned in the comments the original posters code would have worked if it was just called after the viewController had gotten all of its view objects. The easy way to achieve what the original poster wanted would be to simply add:
self.test.text = #"test";
to the viewControllers viewDidLoad method.
I'll leave my original answer here anyway, as I believe it improves on the original posters code and removes some of its dependencies. It still is way too complicated for what it wants to achieve but the pattern as such could be transferred to more befitting scenarios:
To elaborate on my comment:
Your method
+ (void)getSchedule{
NSLog(#"log");
FirstViewController *VC = [[FirstViewController alloc] initWithNibName:#"FirstViewController" bundle:nil];
VC.test.text = #"test";
}
Is a class method. So, naturally it is fired, but your UILabel instance test this instance have no idea about this. Furthermore you seem to have created your own class Label which subclasses NSObject, but the actual label instance is a regular UILabel.
I would guess what you are trying to do is something like this:
#interface Label : UILabel
- (void)getSchedule;
#end
...
#interface FirstViewController : UIViewController
#property (nonatomic, retain) IBOutlet Label *test;
Edit: forgot the method(!)
- (void)getSchedule{
self.text = #"test";
}
And finally in your viewController...
#import "Label.h"
-(IBAction)refresh:(id)sender {
[self.test getSchedule];
}

Update of UITextField using 'self.' Only Working on viewDidLoad

I have an UITextField on the MainStoryboard.
It is set and linked in ViewController.h :
#property (retain, nonatomic) IBOutlet UITextField *finalTextField;
In ViewController.m I have #synthesize finalTextField; after the #implementation ViewController
In the -viewDidLoad area I can successfully update that UITextField by using the code:
self.finalTextField.text = #"99";
That works just fine.
But if I use that same code to update that same TextField anywhere else in ViewController.m I continue to get the error:
"Use of undeclared identifier 'self' "
Any ideas?
void decode(NSString* textToDecode) { ... }
is not a method, it is a "c" function and thus not a part of the class and this has no access to instance variables. Make it a method such as:
- (void)decodeText:(NSString *)text { ... }
#David Brunow's comment suggests the same thing.