I know this has been posted many times but I cannot get it to work, there are no errors in my code (found by Xcode), but what I'm trying to do isn't working.
1- Setup: I have 2 classes, ViewController (UIViewController) and GraphView (UIView)
2- What I am trying to do: I have a BOOL type variable plotPressedVC in ViewController that takes an initial value NO.
Once a button is pressed (this IBAction is located in the ViewController.m file) plotPressedVC = YES. From this, in my GraphView.m file I have an if statement whose condition is met if pressed = YES.
3- My code:
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController {
#public BOOL plotPressedVC;
}
- (IBAction)plot:(id)sender;
#end
ViewController.h
#import "ViewController.h"
#import "GraphView.h"
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
plotPressedVC = YES;
}
- (IBAction)plot:(id)sender {
plotPressedVC = YES;
GraphView *plotPress = [[GraphView alloc] init];
plotPress.plotPressed = plotPressedVC;
}
#end
GraphView.h
#import <UIKit/UIKit.h>
#interface GraphView : UIView {
BOOL plotPressed;
}
#property (nonatomic) BOOL plotPressed;
#end
GraphView.m
#import "GraphView.h"
#import "ViewController.h"
#implementation GraphView
- (void)drawLineGraphWithContext:(CGContextRef)ctx {
if (plotPressed == YES) {
NSLog(#"yep");
}
// Other code that I want to be in if statement
}
#end
4- Question: What have I done wrong for the if statement's condition to not have been met when pushing the button plot?
EDIT: added NSlog at the end of the plot action, I am getting values 80, -64, -128...
- (IBAction)plot:(id)sender {
plotPressedVC = YES;
NSLog(#"%hhd",plotPressedVC);
GraphView *plotPress = [[GraphView alloc] init];
plotPress.plotPressed = plotPressedVC;
NSLog(#"%hhd",plotPress);
}
New Question: How come plotPress is receiving other numbers than 0 or 1?
It's been asked many times and it usually has the same answer.
The problem is here:
GraphView *plotPress = [[GraphView alloc] init];
plotPress.plotPressed = plotPressedVC;
Here you create a new graph view, set the value on it, and then do nothing else with it. You need to have an outlet or property to the existing graph view and set the plotPressed value on that.
Currently that graph view is never added to another view, and under ARC will be immediately deallocated at the end of the method.
Related
I am trying to update the contents of an NSTextView that is connected to myViewController as a referencing outlet to the Files Owner which is the subclass myViewController.
When I use an IBAction from a button, or use the viewDidLoad method of the controller, I can update the text fine. However, when I try run the method from another class (referred to in this example as anotherViewController), it runs the method, but the textview does not change.
myViewController.h:
#import <Cocoa/Cocoa.h>
#import "anotherViewController.h"
#interface myViewController : NSViewController { }
#property (unsafe_unretained) IBOutlet NSTextView *outText;
#property (weak) IBOutlet NSButton *updateMeButton;
- (void)updateTextView:(NSString *)argText;
- (void)updateTextViewWithoutArg;
#end
myViewController.m:
#import "myViewController.h"
#interface myViewController ()
#end
#implementation myViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.outText.string = #"I work successfully";
}
- (IBAction)updateMeButton:(id)sender {
self.outText.string = #"I am updated text! I also work!";
}
- (void)updateTextView:(NSString *)argText {
self.outText.string = #"I don't make it to the NSTextView :(";
NSLog(#"Should have updated text view");
}
- (void)updateTextViewWithoutArg {
self.outText.string = #"I don't make it to the NSTextView :(";
NSLog(#"Should have updated text view");
}
#end
In anotherViewController.m , which has all the relevant imports, I call this:
myViewController *viewtask = [[myViewController alloc] init];
[viewtask updateTextViewWithoutArg];
Nothing happens. The method runs and logs that it should have updated, but no text updates. I have tried many different approaches, including textstorage and scrollrange methods, they all work the already working sections, but make no difference in the sections not working.
I've also tried just for fun:
myViewController *viewtask;
[viewtask updateTextViewWithoutArg];
Also using the instance variable _outText
Also using [self.outText setString:#"string"];
Also using [_outText setString:#"string"];
Again, they work but only in the already working sections.
This should be simple but isn't logical to me. In swift all I need to do is
self.outText.string = "I update whenever I'm called!"
Views you create in Interface Builder are lazily created, so if you access them before viewDidLoad is called they are nil.
If your case, calling
myViewController *viewtask = [[myViewController alloc] init];
does not cause the views to be created so when you call
[viewtask updateTextViewWithoutArg];
self.outText is nil.
You can see that this is what is happening by updating your code as below:
- (void)updateTextView:(NSString *)argText {
NSAssert(self.outText != nil, #"self.outText must not be nil");
self.outText.string = #"I don't make it to the NSTextView :(";
NSLog(#"Should have updated text view");
}
you should see the assert fire.
I appear to have found a solution by making myViewController a singleton class and using sharedInstance. For this particlar app, myViewController is a debug output window and will never need to be placed in another view.
I won't accept this answer yet, as it's not the best one I'm sure. There may still be a proper solution presented that allows finding the applicable myViewController instance, and modifying the outText property attached to it. Using this singleton makes subclassing tedious as I would have to make a new class for every instance if I wanted to be able to address say 10 View Controllers.
Anyway - the way I've been able to satisfy my simple requirement:
myViewController.h:
#import <Cocoa/Cocoa.h>
#import "anotherViewController.h"
#interface myViewController : NSViewController { }
#property (unsafe_unretained) IBOutlet NSTextView *outText;
#property (weak) IBOutlet NSButton *updateMeButton;
- (void)updateTextView:(NSString *)argText;
- (void)updateTextViewWithoutArg;
+ (id)sharedInstance;
#end
myViewController.m:
#import "myViewController.h"
#interface myViewController ()
#end
#implementation myViewController
static myViewController *sharedInstance = nil;
+ (myViewController *)sharedInstance {
static myViewController *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[myViewController alloc] init];
});
return sharedInstance;
}
- (void)viewDidLoad {
[super viewDidLoad];
sharedInstance = self;
}
- (void)viewDidUnload {
sharedInstance = nil;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.outText.string = #"I work successfully";
}
- (IBAction)updateMeButton:(id)sender {
sharedInstance.outText.string = #"Button Pressed";
}
- (void)updateTextView:(NSString *)argText {
sharedInstance.outText.string = argText;
}
- (void)updateTextViewWithoutArg {
sharedInstance.outText.string = #"I make it to the TextView now";
}
#end
Now when I use this code from within anotherViewController.m it updates the right instance:
[myViewController.sharedInstance updateTextView:#"Updating with this string"];
I'm trying to call a method from another class with a simple button in my storyboard.
Here are my files:
ViewController.m
// ViewController.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import "PrintHello.h"
#interface ViewController : UIViewController <NSObject>{
PrintHello *printMessage;
}
#property (nonatomic, retain) PrintHello *printMessage;
#end
ViewController.m
// ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize printMessage;
- (void)viewDidLoad{
[super viewDidLoad];
NSLog(#"ViewDidLoad loaded");
}
- (IBAction)Button01:(id)sender{
self.printMessage = [[PrintHello alloc] init]; // EDIT: THIS LINE WAS MISSING NOW IT WORKS
[self.printMessage Print];
NSLog(#"Button01 Pressed");
}
#end
PrintHello.h
// PrintHello.h
#import <Foundation/Foundation.h>
#interface PrintHello : NSObject
-(void) Print;
#end
PrintHello.m
// PrintHello.m
#import "PrintHello.h"
#implementation PrintHello
-(void)Print{ NSLog(#"Printed");}
#end
And also in the storyBoard there is a Button01 linked to the Viecontroller.
From the Log i know that:
viewDidLoad is loaded
and the button is pressed when is pressed :)
BUT the method Print is not called?
Where am i doing wrong?
Before you call [self.printMessage Print];, I think you need to put self.printMessage = [[PrintHello alloc] init];.
As woz has said, you haven't initialized printMessage yet, so the object doesn't exist yet! You probably want to initialize it within viewDidLoad of your ViewController.m file, rather than reinitialize the object over and over again within the button click.
-(void)viewDidLoad
{
[super viewDidLoad];
self.printMessage = [[PrintHello alloc] init];
NSLog(#"ViewDidLoad loaded");
}
I have put together a simple quote generator, storing the quotes in an array. Following are the quote view controller interface and implementation files:
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property(nonatomic, retain)NSArray *myQuotes;
#property(nonatomic, retain)NSMutableArray *movieQuotes;
#property (nonatomic, retain) IBOutlet UITextView *quote_text;
-(IBAction)quote_btn_touch:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize myQuotes;
#synthesize movieQuotes;
#synthesize quote_text;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.myQuotes = [NSArray arrayWithObjects:
#"Live and let live",
#"Don't cry over spilt milk",
#"Always look on the bright side of life",
#"Nobody's perfect",
#"Can't see the woods for the trees",
#"Better to have loved and lost than not loved at all",
#"The early bird catches the worm",
#"As slow as a wet week",
nil];
quote_text = nil;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
-(IBAction)quote_btn_touch:(id)sender {
// 1 - Get number of rows in array
int array_tot = [self.myQuotes count];
// 2 - Get random index
int index = (arc4random() % array_tot);
// 3 - Get the quote string for the index
NSString *my_quote = [self.myQuotes objectAtIndex:index];
// 4 - Display the quote in the text view
self.quote_text.text = [NSString stringWithFormat:#"Quote:\n\n%#", my_quote];
}
#end
In the xib file, I connected the text view and button to the File's Owner, using quote_text and quote_btn_touch respectively.
Trouble is that when I click on the button, nothing happens. Any idea what I missed?
Thanks in advance!
your setting quote text to nil on viewDidLoad. If you get rid of that your code should work as long as you correctly bound the button to the viewcontroller function
quote_text = nil;
New to iOS/Objective-C here. I've spent a lot of time trying to figure this out, but just can't manage it. Here's what's happening:
Click a 'Graph' button on my RootViewController's view
GraphViewController takes over, has a graphView property
I set the graphView's dataSource property to self
Checking self.graphView.dataSource with NSLog confirms that it does indeed point to self
When GraphView's drawRect: is called, self.dataSource is set to (null), where I expected it to point to the GraphViewController object
To summarise: I'm instantiating the graphView property in the view controller, then setting its dataSource, but by the time the view's drawRect: is called the dataSource is no longer set.
GraphViewController.m:
#import "GraphViewController.h"
#import "GraphView.h"
#implementation GraphViewController
#synthesize graphView = _graphView;
#synthesize program = _program;
- (GraphView *)graphView {
if(!_graphView) {
_graphView = [[GraphView alloc] init];
[_graphView setDataSource:(id)self];
}
return _graphView;
}
#end
GraphViewController.h:
#import <UIKit/UIKit.h>
#import "GraphView.h"
#interface GraphViewController : UIViewController
#property (strong, nonatomic) IBOutlet GraphView *graphView;
#end
GraphView.m:
#import "GraphView.h"
#implementation GraphView
#synthesize dataSource = _dataSource;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
NSLog(#"initWithFrame called, self.dataSource=%#", self.dataSource);
}
return self;
}
- (void)drawRect:(CGRect)rect
{
NSLog(#"drawRect:");
NSLog(#"\tself=%#", self);
NSLog(#"\tself.dataSource=%#", self.dataSource); // is (null), shouldn't be
[self.dataSource programToGraph];
}
#end
GraphView.h:
#import <UIKit/UIKit.h>
#class GraphView;
#protocol GraphViewDataSource
- (id)programToGraph;
#end
#interface GraphView : UIView
#property (nonatomic, weak) IBOutlet id <GraphViewDataSource> dataSource;
#end
So it looks like the GraphViewController instance is being deallocated, which will nil out the dataSource property.
So you should go back and look at how you are creating and managing that GraphViewController. A common mistake is to create a view controller like that, then borrow its view and throw it into some other view controller's view hierarchy, and then let the original view controller just go away.
I would look at that, and if you don't see it there, post the code where you create and present the GraphViewController.
Oh, and where are you calling initWithFrame: from?? It almost looks like you could also have two different instances of GraphView floating around. Try logging 'self' along with self.datasource to check that also.
I have a little problem with the application I currenty work on. I create a simpliest project to illustrate my problem.
So, I create a "Navigate-Base Application". I add an other UITableViewController named TableViewController (the one which is created with the project is named RootViewController). I create an instance of TableViewController when I touch a line in the RootViewController.
I create a custom class named "MyCustomClass".
MyCustomClass.h (full code) :
#import <Foundation/Foundation.h>
#interface MyCustomClass : NSObject {
NSString *name;
}
#property (nonatomic, retain) NSString * name;
#end
MyCustomClass.m (full code) :
#import "MyCustomClass.h"
#implementation MyCustomClass
#dynamic name;
#end
I had a MyCustomClass attibute in TableViewController class.
TableViewController.h (full code) :
#import <UIKit/UIKit.h>
#import "MyCustomClass.h"
#interface TableViewController : UITableViewController {
MyCustomClass *aCustomObject;
}
#property (nonatomic, retain) MyCustomClass *aCustomObject;
#end
At the load of TableViewController, I try to display aCustomObject's content.
TableViewController.m (top of the file and what I modify in the template's file) :
#import "TableViewController.h"
#implementation TableViewController
#synthesize aCustomObject;
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
NSLog(#"Name : %#",self.aCustomObject.name);
}
Before, I create and give a value to aCustomObject.name in RootViewController :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
TableViewController *detailViewController = [[TableViewController alloc] initWithNibName:#"TableViewController" bundle:nil];
detailViewController.aCustomObject.name = #"The Name";
[self.navigationController pushViewController:detailViewController animated:YES];
}
Console said :
2011-06-22 07:21:11.087
MyTestApp[12822:207] Name : (null)
I think it's a stupid thing but I don't find myself after hours of try.
Thanks a lot and excuse me for my english mistakes,
You forget to initialize your custom object in the tableViewController's viewDidLoad Method.
Try this.
- (void)viewDidLoad {
[super viewDidLoad];
if(aCustomObject == nil){
self.aCustomObject = [[[MyCustomClass alloc] init] autoRelease];
}
self.aCustomObject.name = #"";
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
//this will show empty here.
NSLog(#"Name : %#",self.aCustomObject.name);
}
You use the #dynamic keyword to tell
the compiler that you will fulfill the
API contract implied by a property
either by providing method
implementations directly or at runtime
using other mechanisms such as dynamic
loading of code or dynamic method
resolution. It suppresses the warnings
that the compiler would otherwise
generate if it can’t find suitable
implementations. You should use it
only if you know that the methods will
be available at runtime.
from Apple Documentation
You are claiming in the question that you included full source for MyCustomClass.m. Where did you implement the getter and setter for the property? If you want the compiler to generate the methods for you, you should use
#synthesize name;